diff options
76 files changed, 2695 insertions, 943 deletions
diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/blender_mesh.cpp index 8a6480a9a42..34f559674fa 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/blender_mesh.cpp @@ -348,6 +348,7 @@ static void create_mesh_volume_attribute(BL::Object& b_ob, Attribute::standard_name(std), b_ob.ptr.data, animated, + 0, frame, INTERPOLATION_LINEAR, EXTENSION_CLIP, diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp index 05adb6f5fe0..2c5ecc02e6d 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/blender_session.cpp @@ -142,8 +142,8 @@ void BlenderSession::create_session() /* setup callbacks for builtin image support */ scene->image_manager->builtin_image_info_cb = function_bind(&BlenderSession::builtin_image_info, this, _1, _2, _3); - scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5); - scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5); + scene->image_manager->builtin_image_pixels_cb = function_bind(&BlenderSession::builtin_image_pixels, this, _1, _2, _3, _4, _5, _6); + scene->image_manager->builtin_image_float_pixels_cb = function_bind(&BlenderSession::builtin_image_float_pixels, this, _1, _2, _3, _4, _5, _6); session->scene = scene; @@ -1119,6 +1119,7 @@ void BlenderSession::builtin_image_info(const string &builtin_name, bool BlenderSession::builtin_image_pixels(const string &builtin_name, void *builtin_data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool free_cache) @@ -1137,7 +1138,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name, const int height = b_image.size()[1]; const int channels = b_image.channels(); - unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame); + unsigned char *image_pixels = image_get_pixels_for_frame(b_image, frame, tile); const size_t num_pixels = ((size_t)width) * height; if(image_pixels && num_pixels * channels == pixels_size) { @@ -1182,6 +1183,7 @@ bool BlenderSession::builtin_image_pixels(const string &builtin_name, bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, void *builtin_data, + int tile, float *pixels, const size_t pixels_size, const bool free_cache) @@ -1204,7 +1206,7 @@ bool BlenderSession::builtin_image_float_pixels(const string &builtin_name, const int channels = b_image.channels(); float *image_pixels; - image_pixels = image_get_float_pixels_for_frame(b_image, frame); + image_pixels = image_get_float_pixels_for_frame(b_image, frame, tile); const size_t num_pixels = ((size_t)width) * height; if(image_pixels && num_pixels * channels == pixels_size) { diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h index 2be57f293b4..250b8ca9967 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/blender_session.h @@ -160,11 +160,13 @@ protected: ImageMetaData& metadata); bool builtin_image_pixels(const string &builtin_name, void *builtin_data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool free_cache); bool builtin_image_float_pixels(const string &builtin_name, void *builtin_data, + int tile, float *pixels, const size_t pixels_size, const bool free_cache); diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/blender_shader.cpp index 62c160ca503..830a3682a27 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/blender_shader.cpp @@ -670,6 +670,12 @@ static ShaderNode *add_node(Scene *scene, image->animated = b_image_node.image_user().use_auto_refresh(); image->use_alpha = b_image.use_alpha(); + image->tiles.clear(); + BL::Image::tiles_iterator b_iter; + for(b_image.tiles.begin(b_iter); b_iter != b_image.tiles.end(); ++b_iter) { + image->tiles.push_back(b_iter->tile_number()); + } + /* TODO: restore */ /* TODO(sergey): Does not work properly when we change builtin type. */ #if 0 @@ -884,6 +890,7 @@ static ShaderNode *add_node(Scene *scene, scene->image_manager->tag_reload_image( point_density->filename.string(), point_density->builtin_data, + 0, point_density->interpolation, EXTENSION_CLIP, true); @@ -1245,7 +1252,7 @@ void BlenderSync::sync_materials(BL::Depsgraph& b_depsgraph, bool update_all) /* test if we need to sync */ if(shader_map.sync(&shader, b_mat) || shader->need_sync_object || update_all) { - ShaderGraph *graph = new ShaderGraph(); + ShaderGraph *graph = new ShaderGraph(shader); shader->name = b_mat.name().c_str(); shader->pass_id = b_mat.pass_index(); @@ -1320,7 +1327,7 @@ void BlenderSync::sync_world(BL::Depsgraph& b_depsgraph, bool update_all) if(world_recalc || update_all || b_world.ptr.data != world_map) { Shader *shader = scene->default_background; - ShaderGraph *graph = new ShaderGraph(); + ShaderGraph *graph = new ShaderGraph(shader); /* create nodes */ if(b_world && b_world.use_nodes() && b_world.node_tree()) { @@ -1418,7 +1425,7 @@ void BlenderSync::sync_lights(BL::Depsgraph& b_depsgraph, bool update_all) /* test if we need to sync */ if(shader_map.sync(&shader, b_light) || update_all) { - ShaderGraph *graph = new ShaderGraph(); + ShaderGraph *graph = new ShaderGraph(shader); /* create nodes */ if(b_light.use_nodes() && b_light.node_tree()) { diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/blender_sync.cpp index 93232fbf98f..d19eed0ea6b 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/blender_sync.cpp @@ -620,6 +620,8 @@ SceneParams BlenderSync::get_scene_params(BL::Scene& b_scene, params.bvh_layout = DebugFlags().cpu.bvh_layout; + params.background = background; + return params; } diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/blender_util.h index 4e754d22984..04b2744cd10 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/blender_util.h @@ -34,8 +34,8 @@ extern "C" { size_t BLI_timecode_string_from_time_simple(char *str, size_t maxlen, double time_seconds); void BKE_image_user_frame_calc(void *iuser, int cfra, int fieldnr); void BKE_image_user_file_path(void *iuser, void *ima, char *path); -unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame); -float *BKE_image_get_float_pixels_for_frame(void *image, int frame); +unsigned char *BKE_image_get_pixels_for_frame(void *image, int frame, int tile); +float *BKE_image_get_float_pixels_for_frame(void *image, int frame, int tile); } CCL_NAMESPACE_BEGIN @@ -220,8 +220,15 @@ static inline string image_user_file_path(BL::ImageUser& iuser, int cfra) { char filepath[1024]; + iuser.tile(0); BKE_image_user_frame_calc(iuser.ptr.data, cfra, 0); BKE_image_user_file_path(iuser.ptr.data, ima.ptr.data, filepath); + if(ima.source() == BL::Image::source_TILED) { + char *udim_id = strstr(filepath, "1001"); + if(udim_id) { + memcpy(udim_id, "%04d", 4); + } + } return string(filepath); } @@ -232,15 +239,15 @@ static inline int image_user_frame_number(BL::ImageUser& iuser, int cfra) } static inline unsigned char *image_get_pixels_for_frame(BL::Image& image, - int frame) + int frame, int tile) { - return BKE_image_get_pixels_for_frame(image.ptr.data, frame); + return BKE_image_get_pixels_for_frame(image.ptr.data, frame, tile); } static inline float *image_get_float_pixels_for_frame(BL::Image& image, - int frame) + int frame, int tile) { - return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame); + return BKE_image_get_float_pixels_for_frame(image.ptr.data, frame, tile); } /* Utilities */ diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index ab69afa051e..6f0945b3d15 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -277,7 +277,7 @@ ccl_device_noinline void svm_eval_nodes(KernelGlobals *kg, ShaderData *sd, ccl_a # endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ # ifdef __TEXTURES__ case NODE_TEX_IMAGE: - svm_node_tex_image(kg, sd, stack, node); + svm_node_tex_image(kg, sd, stack, node, &offset); break; case NODE_TEX_IMAGE_BOX: svm_node_tex_image_box(kg, sd, stack, node); diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/svm_image.h index 81ee79c984e..196c56b2cbe 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/svm_image.h @@ -18,6 +18,9 @@ CCL_NAMESPACE_BEGIN ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint srgb, uint use_alpha) { + if(id == -1) { + return make_float4(TEX_IMAGE_MISSING_R, TEX_IMAGE_MISSING_G, TEX_IMAGE_MISSING_B, TEX_IMAGE_MISSING_A); + } float4 r = kernel_tex_image_interp(kg, id, x, y); const float alpha = r.w; @@ -46,9 +49,8 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) { - uint id = node.y; uint co_offset, out_offset, alpha_offset, srgb; decode_node_uchar4(node.z, &co_offset, &out_offset, &alpha_offset, &srgb); @@ -67,6 +69,38 @@ ccl_device void svm_node_tex_image(KernelGlobals *kg, ShaderData *sd, float *sta else { tex_co = make_float2(co.x, co.y); } + + int id = -1; + if(node.y > 0) { + uint num_nodes = node.y; + int next_offset = (*offset) + num_nodes; + if(tex_co.x >= 0.0f && tex_co.y < 10.0f && tex_co.y >= 0.0f) { + int tx = (int) tex_co.x; + int ty = (int) tex_co.y; + int tile = ty*10 + tx; + for(int i = 0; i < num_nodes; i++) { + uint4 node = read_node(kg, offset); + if(node.x == tile) { + id = node.y; + break; + } + if(node.z == tile) { + id = node.w; + break; + } + } + + if(id != -1) { + tex_co.x -= tx; + tex_co.y -= ty; + } + } + *offset = next_offset; + } + else { + id = read_node(kg, offset).y; + } + float4 f = svm_image_texture(kg, id, tex_co.x, tex_co.y, srgb, use_alpha); if(stack_valid(out_offset)) diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/render/graph.cpp index 3a9e2981418..d8781c2cf9a 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/render/graph.cpp @@ -193,7 +193,8 @@ bool ShaderNode::equals(const ShaderNode& other) /* Graph */ -ShaderGraph::ShaderGraph() +ShaderGraph::ShaderGraph(Shader *shader) + : shader(shader) { finalized = false; simplified = false; @@ -557,7 +558,7 @@ void ShaderGraph::constant_fold(Scene *scene) void ShaderGraph::simplify_settings(Scene *scene) { foreach(ShaderNode *node, nodes) { - node->simplify_settings(scene); + node->simplify_settings(scene, shader); } } diff --git a/intern/cycles/render/graph.h b/intern/cycles/render/graph.h index 426522066b3..7943d0ed063 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/render/graph.h @@ -147,7 +147,7 @@ public: /* Simplify settings used by artists to the ones which are simpler to * evaluate in the kernel but keep the final result unchanged. */ - virtual void simplify_settings(Scene * /*scene*/) {}; + virtual void simplify_settings(Scene * /*scene*/, Shader * /*shader*/) {}; virtual bool has_surface_emission() { return false; } virtual bool has_surface_transparent() { return false; } @@ -246,8 +246,9 @@ public: bool finalized; bool simplified; string displacement_hash; + Shader *shader; - ShaderGraph(); + ShaderGraph(Shader *shader); ~ShaderGraph(); ShaderNode *add(ShaderNode *node); diff --git a/intern/cycles/render/image.cpp b/intern/cycles/render/image.cpp index 2865b0e5e97..bead0118a02 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/render/image.cpp @@ -278,12 +278,14 @@ bool ImageManager::get_image_metadata(const string& filename, static bool image_equals(ImageManager::Image *image, const string& filename, void *builtin_data, + int tile, InterpolationType interpolation, ExtensionType extension, bool use_alpha) { return image->filename == filename && image->builtin_data == builtin_data && + image->tile == tile && image->interpolation == interpolation && image->extension == extension && image->use_alpha == use_alpha; @@ -292,6 +294,7 @@ static bool image_equals(ImageManager::Image *image, int ImageManager::add_image(const string& filename, void *builtin_data, bool animated, + int tile, float frame, InterpolationType interpolation, ExtensionType extension, @@ -322,6 +325,7 @@ int ImageManager::add_image(const string& filename, if(img && image_equals(img, filename, builtin_data, + tile, interpolation, extension, use_alpha)) @@ -370,6 +374,7 @@ int ImageManager::add_image(const string& filename, img->metadata = metadata; img->need_load = true; img->animated = animated; + img->tile = tile; img->frame = frame; img->interpolation = interpolation; img->extension = extension; @@ -406,6 +411,7 @@ void ImageManager::remove_image(int flat_slot) void ImageManager::remove_image(const string& filename, void *builtin_data, + int tile, InterpolationType interpolation, ExtensionType extension, bool use_alpha) @@ -417,6 +423,7 @@ void ImageManager::remove_image(const string& filename, if(images[type][slot] && image_equals(images[type][slot], filename, builtin_data, + tile, interpolation, extension, use_alpha)) @@ -434,6 +441,7 @@ void ImageManager::remove_image(const string& filename, */ void ImageManager::tag_reload_image(const string& filename, void *builtin_data, + int tile, InterpolationType interpolation, ExtensionType extension, bool use_alpha) @@ -443,6 +451,7 @@ void ImageManager::tag_reload_image(const string& filename, if(images[type][slot] && image_equals(images[type][slot], filename, builtin_data, + tile, interpolation, extension, use_alpha)) @@ -581,6 +590,7 @@ bool ImageManager::file_load_image(Image *img, if(FileFormat == TypeDesc::FLOAT) { builtin_image_float_pixels_cb(img->filename, img->builtin_data, + img->tile, (float*)&pixels[0], num_pixels * components, img->metadata.builtin_free_cache); @@ -588,6 +598,7 @@ bool ImageManager::file_load_image(Image *img, else if(FileFormat == TypeDesc::UINT8) { builtin_image_pixels_cb(img->filename, img->builtin_data, + img->tile, (uchar*)&pixels[0], num_pixels * components, img->metadata.builtin_free_cache); diff --git a/intern/cycles/render/image.h b/intern/cycles/render/image.h index 0bf06c322d0..c8996b70b38 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/render/image.h @@ -53,6 +53,7 @@ public: int add_image(const string& filename, void *builtin_data, bool animated, + int tile, float frame, InterpolationType interpolation, ExtensionType extension, @@ -61,11 +62,13 @@ public: void remove_image(int flat_slot); void remove_image(const string& filename, void *builtin_data, + int tile, InterpolationType interpolation, ExtensionType extension, bool use_alpha); void tag_reload_image(const string& filename, void *builtin_data, + int tile, InterpolationType interpolation, ExtensionType extension, bool use_alpha); @@ -107,11 +110,13 @@ public: ImageMetaData& metadata)> builtin_image_info_cb; function<bool(const string &filename, void *data, + int tile, unsigned char *pixels, const size_t pixels_size, const bool free_cache)> builtin_image_pixels_cb; function<bool(const string &filename, void *data, + int tile, float *pixels, const size_t pixels_size, const bool free_cache)> builtin_image_float_pixels_cb; @@ -125,6 +130,7 @@ public: bool need_load; bool animated; float frame; + int tile; InterpolationType interpolation; ExtensionType extension; diff --git a/intern/cycles/render/light.cpp b/intern/cycles/render/light.cpp index 49cfae4888b..b8277932a55 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/render/light.cpp @@ -555,9 +555,13 @@ void LightManager::device_update_background(Device *device, if(node->type == EnvironmentTextureNode::node_type) { EnvironmentTextureNode *env = (EnvironmentTextureNode*) node; ImageMetaData metadata; - if(env->image_manager && env->image_manager->get_image_metadata(env->slot, metadata)) { - res.x = max(res.x, metadata.width); - res.y = max(res.y, metadata.height); + if(env->image_manager) { + foreach(int slot, env->slots) { + if(env->image_manager->get_image_metadata(slot, metadata)) { + res.x = max(res.x, metadata.width); + res.y = max(res.y, metadata.height); + } + } } } } diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/render/mesh.cpp index ade575a52d6..3b85186948e 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/render/mesh.cpp @@ -662,6 +662,61 @@ void Mesh::add_subd_face(int* corners, int num_corners, int shader_, bool smooth subd_faces.push_back_reserved(face); } +static void get_uv_tiles_from_attribute(Attribute *attr, int num, vector<bool> &tiles) +{ + if(!attr) { + return; + } + + const float3 *uv = attr->data_float3(); + int size = tiles.size(); + for(int i = 0; i < num; i++, uv++) { + float u = uv->x, v = uv->y; + + if(u < 0.0f || v < 0.0f || u > 10.0f) continue; + + int x = (int) u, y = (int) v; + + /* Be conservative in corners - precisely touching the right-upper corner of a tile + * should not load its right-upper neighbor as well. */ + if(x && (u == x)) x--; + if(y && (v == y)) y--; + + int id = y*10 + x; + if(id >= size) { + tiles.resize(id+1); + size = id; + } + tiles[id] = true; + } +} + +void Mesh::get_uv_tiles(ustring map, vector<bool> &tiles) +{ + if(map.empty()) { + get_uv_tiles_from_attribute(attributes.find(ATTR_STD_UV), + num_triangles()*3, + tiles); + get_uv_tiles_from_attribute(subd_attributes.find(ATTR_STD_UV), + subd_face_corners.size() + num_ngons, + tiles); + get_uv_tiles_from_attribute(curve_attributes.find(ATTR_STD_UV), + num_curves(), + tiles); + } + else { + get_uv_tiles_from_attribute(attributes.find(map), + num_triangles()*3, + tiles); + get_uv_tiles_from_attribute(subd_attributes.find(map), + subd_face_corners.size() + num_ngons, + tiles); + get_uv_tiles_from_attribute(curve_attributes.find(map), + num_curves(), + tiles); + } +} + void Mesh::compute_bounds() { BoundBox bnds = BoundBox::empty; @@ -1996,8 +2051,7 @@ void MeshManager::device_update_displacement_images(Device *device, } ImageSlotTextureNode *image_node = static_cast<ImageSlotTextureNode*>(node); - int slot = image_node->slot; - if(slot != -1) { + foreach(int slot, image_node->slots) { bump_images.insert(slot); } } diff --git a/intern/cycles/render/mesh.h b/intern/cycles/render/mesh.h index 444f03a3664..0cd3919a49c 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/render/mesh.h @@ -280,6 +280,8 @@ public: void add_vertex_normals(); void add_undisplaced(); + void get_uv_tiles(ustring map, vector<bool> &tiles); + void pack_shaders(Scene *scene, uint *shader); void pack_normals(float4 *vnormal); void pack_verts(const vector<uint>& tri_prim_index, diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/render/nodes.cpp index cce851612db..91fb04f1b75 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/render/nodes.cpp @@ -18,6 +18,7 @@ #include "render/image.h" #include "render/integrator.h" #include "render/light.h" +#include "render/mesh.h" #include "render/nodes.h" #include "render/scene.h" #include "render/svm.h" @@ -249,21 +250,19 @@ ImageTextureNode::ImageTextureNode() : ImageSlotTextureNode(node_type) { image_manager = NULL; - slot = -1; - is_float = -1; + is_float = false; is_linear = false; builtin_data = NULL; animated = false; + tiles.push_back(0); } ImageTextureNode::~ImageTextureNode() { if(image_manager) { - image_manager->remove_image(filename.string(), - builtin_data, - interpolation, - extension, - use_alpha); + foreach(int slot, slots) { + image_manager->remove_image(slot); + } } } @@ -271,12 +270,55 @@ ShaderNode *ImageTextureNode::clone() const { ImageTextureNode *node = new ImageTextureNode(*this); node->image_manager = NULL; - node->slot = -1; - node->is_float = -1; + node->slots.clear(); + node->is_float = false; node->is_linear = false; return node; } +void ImageTextureNode::simplify_settings(Scene *scene, Shader *shader) +{ + if(!scene->params.background) { + /* During interactive renders, all tiles are loaded. + * While we could support updating this when UVs change, that could lead + * to annoying interruptions when loading images while editing UVs. */ + return; + } + + ShaderInput *vector_in = input("Vector"); + ustring attribute; + if(vector_in->link) { + ShaderNode *node = vector_in->link->parent; + if(node->type == UVMapNode::node_type) { + UVMapNode *uvmap = (UVMapNode*) node; + attribute = uvmap->attribute; + } + else if(node->type == TextureCoordinateNode::node_type) { + if(vector_in->link != node->output("UV")) { + return; + } + } + else { + return; + } + } + + ccl::vector<bool> used_tiles; + foreach(Mesh *mesh, scene->meshes) { + if(std::find(mesh->used_shaders.begin(), mesh->used_shaders.end(), shader) != mesh->used_shaders.end()) { + mesh->get_uv_tiles(attribute, used_tiles); + } + } + + ccl::vector<int> new_tiles; + foreach(int tile, tiles) { + if (tile < used_tiles.size() && used_tiles[tile]) { + new_tiles.push_back(tile); + } + } + tiles.swap(new_tiles); +} + void ImageTextureNode::attributes(Shader *shader, AttributeRequestSet *attributes) { #ifdef WITH_PTEX @@ -299,27 +341,52 @@ void ImageTextureNode::compile(SVMCompiler& compiler) ShaderOutput *alpha_out = output("Alpha"); image_manager = compiler.image_manager; - if(is_float == -1) { - ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - extension, - use_alpha, - metadata); - is_float = metadata.is_float; - is_linear = metadata.is_linear; + if(slots.size() < tiles.size()) { + slots.clear(); + foreach(int tile, tiles) { + string tile_name; + if(tiles.size() == 1) { + tile_name = filename.string(); + } + else { + tile_name = string_printf(filename.c_str(), 1001 + tile); + } + printf("Loading %s\n", tile_name.c_str()); + ImageMetaData metadata; + slots.push_back(image_manager->add_image(tile_name, + builtin_data, + animated, + tile, + 0, + interpolation, + extension, + use_alpha, + metadata)); + if(tile == 0) { + is_float = metadata.is_float; + is_linear = metadata.is_linear; + } + } } - if(slot != -1) { + bool has_image = false; + foreach(int slot, slots) { + if(slot != -1) { + has_image = true; + break; + } + } + + if(has_image) { + int num_nodes = divide_up(slots.size(), 2); int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); + /* TODO(lukas): Avoid extra node for common case of only one tile. */ + if(projection != NODE_IMAGE_PROJ_BOX) { compiler.add_node(NODE_TEX_IMAGE, - slot, + num_nodes, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), @@ -329,7 +396,7 @@ void ImageTextureNode::compile(SVMCompiler& compiler) } else { compiler.add_node(NODE_TEX_IMAGE_BOX, - slot, + num_nodes, compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), @@ -338,6 +405,21 @@ void ImageTextureNode::compile(SVMCompiler& compiler) __float_as_int(projection_blend)); } + for(int i = 0; i < num_nodes; i++) { + int4 node; + node.x = tiles[2*i]; + node.y = slots[2*i]; + if(2*i+1 < slots.size()) { + node.z = tiles[2*i+1]; + node.w = slots[2*i+1]; + } + else { + node.z = -1; + node.w = -1; + } + compiler.add_node(node.x, node.y, node.z, node.w); + } + tex_mapping.compile_end(compiler, vector_in, vector_offset); } else { @@ -360,26 +442,28 @@ void ImageTextureNode::compile(OSLCompiler& compiler) tex_mapping.compile(compiler); image_manager = compiler.image_manager; - if(is_float == -1) { + if(slots.size() == 0) { ImageMetaData metadata; if(builtin_data == NULL) { image_manager->get_image_metadata(filename.string(), NULL, metadata); + slots.push_back(-1); } else { - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - extension, - use_alpha, - metadata); + slots.push_back(image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + 0, + interpolation, + extension, + use_alpha, + metadata)); } is_float = metadata.is_float; is_linear = metadata.is_linear; } - if(slot == -1) { + if(slots[0] == -1) { compiler.parameter(this, "filename"); } else { @@ -389,7 +473,7 @@ void ImageTextureNode::compile(OSLCompiler& compiler) * "@i<slot_number>" and check whether file name matches this * mask in the OSLRenderServices::texture(). */ - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); + compiler.parameter("filename", string_printf("@i%d", slots[0]).c_str()); } if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) compiler.parameter("color_space", "linear"); @@ -446,8 +530,7 @@ EnvironmentTextureNode::EnvironmentTextureNode() : ImageSlotTextureNode(node_type) { image_manager = NULL; - slot = -1; - is_float = -1; + is_float = false; is_linear = false; builtin_data = NULL; animated = false; @@ -455,12 +538,8 @@ EnvironmentTextureNode::EnvironmentTextureNode() EnvironmentTextureNode::~EnvironmentTextureNode() { - if(image_manager) { - image_manager->remove_image(filename.string(), - builtin_data, - interpolation, - EXTENSION_REPEAT, - use_alpha); + if(image_manager && slots.size()) { + image_manager->remove_image(slots[0]); } } @@ -468,8 +547,8 @@ ShaderNode *EnvironmentTextureNode::clone() const { EnvironmentTextureNode *node = new EnvironmentTextureNode(*this); node->image_manager = NULL; - node->slot = -1; - node->is_float = -1; + node->slots.clear(); + node->is_float = false; node->is_linear = false; return node; } @@ -494,26 +573,27 @@ void EnvironmentTextureNode::compile(SVMCompiler& compiler) ShaderOutput *alpha_out = output("Alpha"); image_manager = compiler.image_manager; - if(slot == -1) { + if(slots.size() == 0) { ImageMetaData metadata; - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - EXTENSION_REPEAT, - use_alpha, - metadata); + slots.push_back(image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + 0, + interpolation, + EXTENSION_REPEAT, + use_alpha, + metadata)); is_float = metadata.is_float; is_linear = metadata.is_linear; } - if(slot != -1) { + if(slots[0] != -1) { int srgb = (is_linear || color_space != NODE_COLOR_SPACE_COLOR)? 0: 1; int vector_offset = tex_mapping.compile_begin(compiler, vector_in); compiler.add_node(NODE_TEX_ENVIRONMENT, - slot, + slots[0], compiler.encode_uchar4( vector_offset, compiler.stack_assign_if_linked(color_out), @@ -546,30 +626,32 @@ void EnvironmentTextureNode::compile(OSLCompiler& compiler) * of builtin images. */ image_manager = compiler.image_manager; - if(is_float == -1) { + if(slots.size() == 0) { + slots.resize(1); ImageMetaData metadata; if(builtin_data == NULL) { image_manager->get_image_metadata(filename.string(), NULL, metadata); } else { - slot = image_manager->add_image(filename.string(), - builtin_data, - animated, - 0, - interpolation, - EXTENSION_REPEAT, - use_alpha, - metadata); + slots[0] = image_manager->add_image(filename.string(), + builtin_data, + animated, + 0, + 0, + interpolation, + EXTENSION_REPEAT, + use_alpha, + metadata); } is_float = metadata.is_float; is_linear = metadata.is_linear; } - if(slot == -1) { + if(slots[0] == -1) { compiler.parameter(this, "filename"); } else { - compiler.parameter("filename", string_printf("@i%d", slot).c_str()); + compiler.parameter("filename", string_printf("@i%d", slots[0]).c_str()); } compiler.parameter(this, "projection"); if(is_linear || color_space != NODE_COLOR_SPACE_COLOR) @@ -1495,11 +1577,7 @@ PointDensityTextureNode::PointDensityTextureNode() PointDensityTextureNode::~PointDensityTextureNode() { if(image_manager) { - image_manager->remove_image(filename.string(), - builtin_data, - interpolation, - EXTENSION_CLIP, - true); + image_manager->remove_image(slot); } } @@ -1525,7 +1603,7 @@ void PointDensityTextureNode::add_image() if(slot == -1) { ImageMetaData metadata; slot = image_manager->add_image(filename.string(), builtin_data, - false, 0, + false, 0, 0, interpolation, EXTENSION_CLIP, true, @@ -2055,7 +2133,7 @@ GlossyBsdfNode::GlossyBsdfNode() distribution_orig = NBUILTIN_CLOSURES; } -void GlossyBsdfNode::simplify_settings(Scene *scene) +void GlossyBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; @@ -2150,7 +2228,7 @@ GlassBsdfNode::GlassBsdfNode() distribution_orig = NBUILTIN_CLOSURES; } -void GlassBsdfNode::simplify_settings(Scene *scene) +void GlassBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; @@ -2245,7 +2323,7 @@ RefractionBsdfNode::RefractionBsdfNode() distribution_orig = NBUILTIN_CLOSURES; } -void RefractionBsdfNode::simplify_settings(Scene *scene) +void RefractionBsdfNode::simplify_settings(Scene *scene, Shader * /*shader*/) { if(distribution_orig == NBUILTIN_CLOSURES) { roughness_orig = roughness; diff --git a/intern/cycles/render/nodes.h b/intern/cycles/render/nodes.h index c2cf13ad020..2e787aa3df2 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/render/nodes.h @@ -74,7 +74,7 @@ public: explicit ImageSlotTextureNode(const NodeType *node_type) : TextureNode(node_type) { special_type = SHADER_SPECIAL_TYPE_IMAGE_SLOT; } - int slot; + vector<int> slots; }; class ImageTextureNode : public ImageSlotTextureNode { @@ -84,9 +84,10 @@ public: ShaderNode *clone() const; void attributes(Shader *shader, AttributeRequestSet *attributes); bool has_attribute_dependency() { return true; } + void simplify_settings(Scene *scene, Shader *shader); ImageManager *image_manager; - int is_float; + bool is_float; bool is_linear; bool use_alpha; ustring filename; @@ -98,6 +99,7 @@ public: float projection_blend; bool animated; float3 vector; + ccl::vector<int> tiles; virtual bool equals(const ShaderNode& other) { @@ -118,7 +120,7 @@ public: virtual int get_group() { return NODE_GROUP_LEVEL_2; } ImageManager *image_manager; - int is_float; + bool is_float; bool is_linear; bool use_alpha; ustring filename; @@ -450,7 +452,7 @@ class GlossyBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(GlossyBsdfNode) - void simplify_settings(Scene *scene); + void simplify_settings(Scene *scene, Shader *shader); bool has_integrator_dependency(); ClosureType get_closure_type() { return distribution; } @@ -462,7 +464,7 @@ class GlassBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(GlassBsdfNode) - void simplify_settings(Scene *scene); + void simplify_settings(Scene *scene, Shader *shader); bool has_integrator_dependency(); ClosureType get_closure_type() { return distribution; } @@ -474,7 +476,7 @@ class RefractionBsdfNode : public BsdfNode { public: SHADER_NODE_CLASS(RefractionBsdfNode) - void simplify_settings(Scene *scene); + void simplify_settings(Scene *scene, Shader *shader); bool has_integrator_dependency(); ClosureType get_closure_type() { return distribution; } diff --git a/intern/cycles/render/scene.h b/intern/cycles/render/scene.h index dd8069537eb..c107a0b073d 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/render/scene.h @@ -170,6 +170,8 @@ public: bool persistent_data; int texture_limit; + bool background; + SceneParams() { shadingsystem = SHADINGSYSTEM_SVM; diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/render/shader.cpp index e428e174712..8148757b693 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/render/shader.cpp @@ -579,7 +579,8 @@ void ShaderManager::add_default(Scene *scene) { /* default surface */ { - ShaderGraph *graph = new ShaderGraph(); + Shader *shader = new Shader(); + ShaderGraph *graph = new ShaderGraph(shader); DiffuseBsdfNode *diffuse = new DiffuseBsdfNode(); diffuse->color = make_float3(0.8f, 0.8f, 0.8f); @@ -587,7 +588,6 @@ void ShaderManager::add_default(Scene *scene) graph->connect(diffuse->output("BSDF"), graph->output()->input("Surface")); - Shader *shader = new Shader(); shader->name = "default_surface"; shader->graph = graph; scene->shaders.push_back(shader); @@ -596,7 +596,8 @@ void ShaderManager::add_default(Scene *scene) /* default light */ { - ShaderGraph *graph = new ShaderGraph(); + Shader *shader = new Shader(); + ShaderGraph *graph = new ShaderGraph(shader); EmissionNode *emission = new EmissionNode(); emission->color = make_float3(0.8f, 0.8f, 0.8f); @@ -605,7 +606,6 @@ void ShaderManager::add_default(Scene *scene) graph->connect(emission->output("Emission"), graph->output()->input("Surface")); - Shader *shader = new Shader(); shader->name = "default_light"; shader->graph = graph; scene->shaders.push_back(shader); @@ -614,9 +614,9 @@ void ShaderManager::add_default(Scene *scene) /* default background */ { - ShaderGraph *graph = new ShaderGraph(); - Shader *shader = new Shader(); + ShaderGraph *graph = new ShaderGraph(shader); + shader->name = "default_background"; shader->graph = graph; scene->shaders.push_back(shader); @@ -625,9 +625,9 @@ void ShaderManager::add_default(Scene *scene) /* default empty */ { - ShaderGraph *graph = new ShaderGraph(); - Shader *shader = new Shader(); + ShaderGraph *graph = new ShaderGraph(shader); + shader->name = "default_empty"; shader->graph = graph; scene->shaders.push_back(shader); diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 1303e46ab6c..a47baec23dd 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -736,6 +736,10 @@ class IMAGE_PT_view_properties(Panel): row.active = uvedit.show_other_objects row.prop(uvedit, "other_uv_filter", text="Filter") + if ima is None: + row = layout.row() + row.prop(uvedit, "tile_grid_shape", text="Grid Shape") + class IMAGE_UL_render_slots(UIList): def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): @@ -773,6 +777,33 @@ class IMAGE_PT_render_slots(Panel): col.operator("image.clear_render_slot", icon='X', text="") +class IMAGE_PT_tile_properties(Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'UI' + bl_label = "Tiles" + + @classmethod + def poll(cls, context): + sima = context.space_data + return (sima and sima.image and sima.image.source == 'TILED') + + def draw(self, context): + layout = self.layout + + sima = context.space_data + ima = sima.image + + row = layout.row(align=True) + row.operator("image.add_tile") + row.operator("image.remove_tile") + + tile = ima.tiles.get(sima.current_tile) + if tile: + col = layout.column(align=True) + col.operator("image.fill_tile") + col.prop(tile, "label") + + class IMAGE_PT_tools_transform_uvs(Panel, UVToolsPanel): bl_label = "Transform" @@ -1407,6 +1438,7 @@ classes = ( IMAGE_UL_render_slots, IMAGE_PT_render_slots, IMAGE_PT_view_properties, + IMAGE_PT_tile_properties, IMAGE_PT_tools_transform_uvs, IMAGE_PT_tools_align_uvs, IMAGE_PT_tools_uvs, diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 90c0016d0ed..f7a88ee4a11 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -74,6 +74,7 @@ void BLF_size(int fontid, int size, int dpi); void BLF_color4ubv(int fontid, const unsigned char rgba[4]); void BLF_color3ubv(int fontid, const unsigned char rgb[3]); void BLF_color3ubv_alpha(int fontid, const unsigned char rgb[3], unsigned char alpha); +void BLF_color4ub(int fontid, unsigned char r, unsigned char g, unsigned char b, unsigned char alpha); void BLF_color3ub(int fontid, unsigned char r, unsigned char g, unsigned char b); void BLF_color4f(int fontid, float r, float g, float b, float a); void BLF_color4fv(int fontid, const float rgba[4]); diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index b0e0cdac407..aeb69ff4b72 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -513,6 +513,18 @@ void BLF_color3ub(int fontid, unsigned char r, unsigned char g, unsigned char b) } } +void BLF_color4ub(int fontid, unsigned char r, unsigned char g, unsigned char b, unsigned char alpha) +{ + FontBLF *font = blf_get(fontid); + + if (font) { + font->color[0] = r; + font->color[1] = g; + font->color[2] = b; + font->color[3] = alpha; + } +} + void BLF_color4fv(int fontid, const float rgba[4]) { FontBLF *font = blf_get(fontid); diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index a41d9582add..b36530c3db0 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,6 +45,7 @@ struct Scene; struct Object; struct ImageFormatData; struct ImagePool; +struct ImageTile; struct Main; struct ReportList; struct RenderResult; @@ -134,6 +135,7 @@ struct RenderResult; #define IMA_SRC_MOVIE 3 #define IMA_SRC_GENERATED 4 #define IMA_SRC_VIEWER 5 +#define IMA_SRC_TILED 6 /* ima->type, how to handle/generate it */ #define IMA_TYPE_IMAGE 0 @@ -194,7 +196,7 @@ struct Image *BKE_image_load_exists(struct Main *bmain, const char *filepath); /* adds image, adds ibuf, generates color or pattern */ struct Image *BKE_image_add_generated( struct Main *bmain, unsigned int width, unsigned int height, const char *name, - int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d); + int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d, bool tiled); /* adds image from imbuf, owns imbuf */ struct Image *BKE_image_add_from_imbuf(struct Main *bmain, struct ImBuf *ibuf, const char *name); @@ -271,6 +273,19 @@ bool BKE_image_has_alpha(struct Image *image); /* check if texture has gpu texture code */ bool BKE_image_has_opengl_texture(struct Image *ima); +/* get tile index for tiled images */ +void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *tile, char *label, int len_label); + +struct ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label); +bool BKE_image_remove_tile(struct Image *ima, struct ImageTile *tile); + +bool BKE_image_fill_tile(struct Image *ima, struct ImageTile *tile, int width, int height, const float color[4], int gen_type); + +struct ImageTile *BKE_image_get_tile(struct Image *ima, int tile_number); +struct ImageTile *BKE_image_get_tile_from_iuser(struct Image *ima, struct ImageUser *iuser); + +int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], float new_uv[2], float ofs[2]); + void BKE_image_get_size(struct Image *image, struct ImageUser *iuser, int *width, int *height); void BKE_image_get_size_fl(struct Image *image, struct ImageUser *iuser, float size[2]); void BKE_image_get_aspect(struct Image *image, float *aspx, float *aspy); @@ -281,14 +296,15 @@ void BKE_image_buf_fill_checker(unsigned char *rect, float *rect_float, int heig void BKE_image_buf_fill_checker_color(unsigned char *rect, float *rect_float, int height, int width); /* Cycles hookup */ -unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame); -float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame); +unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile); +float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); bool BKE_image_has_packedfile(struct Image *image); bool BKE_image_is_animated(struct Image *image); +bool BKE_image_has_multiple_ibufs(struct Image *image); bool BKE_image_is_dirty(struct Image *image); void BKE_image_file_format_set(struct Image *image, int ftype, const struct ImbFormatOptions *options); bool BKE_image_has_loaded_ibuf(struct Image *image); diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 34f54704f75..ab859ae7c8e 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -435,7 +435,7 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int Image *ima; ima = (Image *)id; if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) { if (flag & BKE_BPATH_TRAVERSE_RELOAD_EDITED) { if (!BKE_image_has_packedfile(ima) && diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index aedbd0673c2..806e7123244 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -91,6 +91,7 @@ #include "RE_pipeline.h" #include "GPU_draw.h" +#include "GPU_texture.h" #include "BLI_sys_types.h" // for intptr_t support @@ -111,9 +112,9 @@ static void image_add_view(Image *ima, const char *viewname, const char *filepat /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF -/* quick lookup: supports 1 million frames, thousand passes */ -#define IMA_MAKE_INDEX(frame, index) (((frame) << 10) + (index)) -#define IMA_INDEX_FRAME(index) ((index) >> 10) +/* quick lookup: supports 1 million entries, thousand passes */ +#define IMA_MAKE_INDEX(entry, index) (((entry) << 10) + (index)) +#define IMA_INDEX_ENTRY(index) ((index) >> 10) #if 0 #define IMA_INDEX_PASS(index) (index & ~1023) #endif @@ -142,7 +143,7 @@ static void imagecache_keydata(void *userkey, int *framenr, int *proxy, int *ren { ImageCacheKey *key = userkey; - *framenr = IMA_INDEX_FRAME(key->index); + *framenr = IMA_INDEX_ENTRY(key->index); *proxy = IMB_PROXY_NONE; *render_flags = 0; } @@ -165,6 +166,16 @@ static void imagecache_put(Image *image, int index, ImBuf *ibuf) IMB_moviecache_put(image->cache, &key, ibuf); } +static void imagecache_remove(Image *image, int index) +{ + if (!image->cache) + return; + + ImageCacheKey key; + key.index = index; + IMB_moviecache_remove(image->cache, &key); +} + static struct ImBuf *imagecache_get(Image *image, int index) { if (image->cache) { @@ -257,7 +268,9 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) GPU_free_image(ima); } - ima->ok = IMA_OK; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + tile->ok = IMA_OK; + } if (do_lock) { BLI_spin_unlock(&image_spin); @@ -290,6 +303,8 @@ void BKE_image_free(Image *ima) BKE_icon_id_delete(&ima->id); BKE_previewimg_free(&ima->preview); + + BLI_freelistN(&ima->tiles); } /* only image block itself */ @@ -297,8 +312,6 @@ static void image_init(Image *ima, short source, short type) { BLI_assert(MEMCMP_STRUCT_OFS_IS_ZERO(ima, id)); - ima->ok = IMA_OK; - ima->aspx = ima->aspy = 1.0; ima->gen_x = 1024; ima->gen_y = 1024; ima->gen_type = IMA_GENTYPE_GRID; @@ -309,6 +322,10 @@ static void image_init(Image *ima, short source, short type) if (source == IMA_SRC_VIEWER) ima->flag |= IMA_VIEW_AS_RENDER; + ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles"); + tile->ok = IMA_OK; + BLI_addtail(&ima->tiles, tile); + if (type == IMA_TYPE_R_RESULT) { for (int i = 0; i < 8; i++) { BKE_image_add_renderslot(ima, NULL); @@ -338,33 +355,40 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ return ima; } -/* Get the ibuf from an image cache by it's index and frame. +/* Get the ibuf from an image cache by it's index and entry. * Local use here only. * * Returns referenced image buffer if it exists, callee is to * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf_for_index_frame(Image *ima, int index, int frame) +static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, int index, int entry) { if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(frame, index); + index = IMA_MAKE_INDEX(entry, index); } return imagecache_get(ima, index); } /* no ima->ibuf anymore, but listbase */ -static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame) +static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry) { if (ibuf) { if (index != IMA_NO_INDEX) - index = IMA_MAKE_INDEX(frame, index); + index = IMA_MAKE_INDEX(entry, index); imagecache_put(ima, index, ibuf); } } +static void image_remove_ibuf(Image *ima, int index, int entry) +{ + if (index != IMA_NO_INDEX) + index = IMA_MAKE_INDEX(entry, index); + imagecache_remove(ima, index); +} + static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) { const ImagePackedFile *imapf_src; @@ -408,9 +432,11 @@ void BKE_image_copy_data(Main *UNUSED(bmain), Image *ima_dst, const Image *ima_s } BLI_listbase_clear(&ima_dst->anims); - - for (int i = 0; i < TEXTARGET_COUNT; i++) { - ima_dst->gputexture[i] = NULL; + BLI_duplicatelist(&ima_dst->tiles, &ima_src->tiles); + LISTBASE_FOREACH(ImageTile*, tile, &ima_dst->tiles) { + for (int j = 0; j < TEXTARGET_COUNT; j++) { + tile->gputexture[j] = NULL; + } } if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) { @@ -476,14 +502,69 @@ bool BKE_image_scale(Image *image, int width, int height) bool BKE_image_has_opengl_texture(Image *ima) { - for (int i = 0; i < TEXTARGET_COUNT; i++) { - if (ima->gputexture[i]) { - return true; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + if (tile->gputexture[i]) { + return true; + } } } return false; } +ImageTile *BKE_image_get_tile(Image *ima, int tile_number) +{ + if (!ima) return NULL; + if (tile_number < 0) return NULL; + + /* First tile is guaranteed to have number zero. */ + if (tile_number == 0) + return ima->tiles.first; + + if (ima->source != IMA_SRC_TILED) return NULL; + + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->tile_number == tile_number) { + return tile; + } + } + + return NULL; +} + +ImageTile *BKE_image_get_tile_from_iuser(Image *ima, ImageUser *iuser) +{ + return BKE_image_get_tile(ima, iuser? iuser->tile : 0); +} + +int BKE_image_get_tile_from_pos(struct Image *ima, const float uv[2], float new_uv[2], float ofs[2]) +{ + float local_ofs[2]; + if (!ofs) { + ofs = local_ofs; + } + + copy_v2_v2(new_uv, uv); + zero_v2(ofs); + + if ((ima->source != IMA_SRC_TILED) || uv[0] < 0.0f || uv[1] < 0.0f || uv[0] >= 10.0f) { + return 0; + } + + int ix = (int) uv[0]; + int iy = (int) uv[1]; + int tile_number = 10*iy + ix; + + if (BKE_image_get_tile(ima, tile_number) == NULL) { + return 0; + } + ofs[0] = ix; + ofs[1] = iy; + sub_v2_v2(new_uv, ofs); + + return tile_number; +} + static void image_init_color_management(Image *ima) { ImBuf *ibuf; @@ -568,8 +649,10 @@ Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exist (ima->id.us == 0)) { id_us_plus(&ima->id); /* officially should not, it doesn't link here! */ - if (ima->ok == 0) - ima->ok = IMA_OK; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->ok == 0) + tile->ok = IMA_OK; + } if (r_exists) *r_exists = true; return ima; @@ -649,10 +732,16 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char /* adds new image block, creates ImBuf and initializes color */ Image *BKE_image_add_generated( Main *bmain, unsigned int width, unsigned int height, const char *name, - int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d) + int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d, const bool tiled) { /* on save, type is changed to FILE in editsima.c */ - Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); + Image *ima; + if (tiled) { + ima = image_alloc(bmain, name, IMA_SRC_TILED, IMA_TYPE_IMAGE); + } + else { + ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); + } if (ima) { int view_id; @@ -669,7 +758,7 @@ Image *BKE_image_add_generated( for (view_id = 0; view_id < 2; view_id++) { ImBuf *ibuf; ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0); + image_assign_ibuf(ima, ibuf, stereo3d ? view_id : (tiled ? 0 : IMA_NO_INDEX), 0); /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ IMB_freeImBuf(ibuf); @@ -678,7 +767,8 @@ Image *BKE_image_add_generated( image_add_view(ima, names[view_id], ""); } - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; } return ima; @@ -701,7 +791,8 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) if (ima) { STRNCPY(ima->name, ibuf->name); image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; } return ima; @@ -718,7 +809,7 @@ static void image_memorypack_multiview(Image *ima) image_free_packedfiles(ima); for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0); ibuf->ftype = IMB_FTYPE_PNG; ibuf->planes = R_IMF_PLANES_RGBA; @@ -773,7 +864,7 @@ void BKE_image_memorypack(Image *ima) return; } - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); if (ibuf == NULL) return; @@ -972,7 +1063,7 @@ static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void int except_frame = *(int *)userdata; return (ibuf->userflags & IB_BITMAPDIRTY) == 0 && (ibuf->index != IMA_NO_INDEX) && - (except_frame != IMA_INDEX_FRAME(ibuf->index)); + (except_frame != IMA_INDEX_ENTRY(ibuf->index)); } /* except_frame is weak, only works for seqs without offset... */ @@ -987,6 +1078,8 @@ void BKE_image_free_anim_ibufs(Image *ima, int except_frame) void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) { + /* Free all image buffers from frames other than cfra, for movies + * and image sequences. */ Image *ima; for (ima = bmain->image.first; ima; ima = ima->id.next) @@ -2628,6 +2721,26 @@ void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) image_init_imageuser(ima, iuser); } +static void image_free_tile(Image *ima, ImageTile *tile) +{ + for (int t = 0; t < TEXTARGET_COUNT; t++) { + if (tile->gputexture[t]) { + GPU_texture_free(tile->gputexture[t]); + tile->gputexture[t] = NULL; + } + } + + if (BKE_image_is_multiview(ima)) { + const int totviews = BLI_listbase_count(&ima->views); + for (int i = 0; i < totviews; i++) { + image_remove_ibuf(ima, i, tile->tile_number); + } + } + else { + image_remove_ibuf(ima, 0, tile->tile_number); + } +} + void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) { if (ima == NULL) @@ -2653,7 +2766,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) if (ima->source == IMA_SRC_GENERATED) { if (ima->gen_x == 0 || ima->gen_y == 0) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); if (ibuf) { ima->gen_x = ibuf->x; ima->gen_y = ibuf->y; @@ -2671,6 +2784,17 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) ima->name[0] = '\0'; } + if (ima->source != IMA_SRC_TILED) { + /* Free all but the first tile. */ + ImageTile *base_tile = BKE_image_get_tile(ima, 0); + for (ImageTile *tile = base_tile->next; tile; tile = tile->next) { + image_free_tile(ima, tile); + MEM_freeN(tile); + } + base_tile->next = NULL; + ima->tiles.last = base_tile; + } + #if 0 /* force reload on first use, but not for multilayer, that makes nodes and buttons in ui drawing fail */ if (ima->type != IMA_TYPE_MULTILAYER) @@ -2686,7 +2810,10 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) BKE_image_free_buffers(ima); #endif - ima->ok = 1; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + tile->ok = 1; + } + if (iuser) iuser->ok = 1; @@ -2736,7 +2863,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) case IMA_SIGNAL_USER_NEW_IMAGE: if (iuser) { iuser->ok = 1; - if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (ima->type == IMA_TYPE_MULTILAYER) { image_init_imageuser(ima, iuser); } @@ -2746,7 +2873,9 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) case IMA_SIGNAL_COLORMANAGE: BKE_image_free_buffers(ima); - ima->ok = 1; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + tile->ok = 1; + } if (iuser) iuser->ok = 1; @@ -2864,12 +2993,95 @@ void BKE_image_multiview_index(Image *ima, ImageUser *iuser) } } +void BKE_image_get_tile_label(Image *ima, ImageTile *tile, char *label, int len_label) +{ + label[0] = '\0'; + if (!ima || !tile) { + return; + } + + if (tile->label[0]) + BLI_strncpy(label, tile->label, len_label); + else + BLI_snprintf(label, len_label, "%d", 1001 + tile->tile_number); +} + +ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *label) +{ + if (ima->source != IMA_SRC_TILED) { + return NULL; + } + + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->tile_number == tile_number) + return NULL; /* TODO(lukas): Maybe return existing? */ + } + + ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile"); + tile->ok = 1; + tile->tile_number = tile_number; + BLI_addtail(&ima->tiles, tile); + + if (label) { + BLI_strncpy(tile->label, label, sizeof(tile->label)); + } + + return tile; +} + +bool BKE_image_remove_tile(struct Image *ima, ImageTile *tile) +{ + if (!ima || !tile || ima->source != IMA_SRC_TILED) { + return false; + } + + if (tile == ima->tiles.first) { + /* Can't remove first tile. */ + return false; + } + + image_free_tile(ima, tile); + BLI_remlink(&ima->renderslots, tile); + MEM_freeN(tile); + + return true; +} + +bool BKE_image_fill_tile(struct Image *ima, ImageTile *tile, int width, int height, const float color[4], int gen_type) +{ + if (!ima || !tile || ima->source != IMA_SRC_TILED) { + return false; + } + + image_free_tile(ima, tile); + + ImageUser iuser = {NULL}; + iuser.ok = true; + ImBuf *main_ibuf = image_acquire_ibuf(ima, &iuser, NULL); + if (!main_ibuf) { + return false; + } + int planes = main_ibuf->planes; + bool is_float = (main_ibuf->rect_float != NULL); + BKE_image_release_ibuf(ima, main_ibuf, NULL); + + ImBuf *tile_ibuf = add_ibuf_size(width, height, ima->name, planes, is_float, gen_type, color, &ima->colorspace_settings); + + if (tile_ibuf) { + image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); + BKE_image_release_ibuf(ima, tile_ibuf, NULL); + tile->ok = 1; + return true; + } + return false; +} + /* if layer or pass changes, we need an index for the imbufs list */ /* note it is called for rendered results, but it doesnt use the index! */ /* and because rendered results use fake layer/passes, don't correct for wrong indices here */ bool BKE_image_is_multilayer(Image *ima) { - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (ima->type == IMA_TYPE_MULTILAYER) { return true; } @@ -2957,7 +3169,7 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima) bool BKE_image_is_openexr(struct Image *ima) { #ifdef WITH_OPENEXR - if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { return BLI_path_extension_check(ima->name, ".exr"); } #else @@ -3059,7 +3271,7 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) #endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ -static void image_initialize_after_load(Image *ima, ImBuf *UNUSED(ibuf)) +static void image_initialize_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ibuf)) { /* Preview is NULL when it has never been used as an icon before. * Never handle previews/icons outside of main thread. */ @@ -3070,8 +3282,8 @@ static void image_initialize_after_load(Image *ima, ImBuf *UNUSED(ibuf)) /* timer */ BKE_image_tag_time(ima); - ima->ok = IMA_OK_LOADED; - + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + tile->ok = IMA_OK_LOADED; } static int imbuf_alpha_flags_for_image(Image *ima) @@ -3154,19 +3366,23 @@ static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, cons } } else { - image_initialize_after_load(ima, ibuf); + image_initialize_after_load(ima, iuser, ibuf); *r_assign = true; } #else - image_initialize_after_load(ima, ibuf); + image_initialize_after_load(ima, iuser, ibuf); *r_assign = true; #endif } + else { + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + tile->ok = 0; + } return ibuf; } -static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, int frame) { struct ImBuf *ibuf = NULL; const bool is_multiview = BKE_image_is_multiview(ima); @@ -3176,7 +3392,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) if (!is_multiview) { ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); if (assign) { - image_assign_ibuf(ima, ibuf, 0, frame); + image_assign_ibuf(ima, ibuf, 0, entry); } } else { @@ -3197,7 +3413,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) if (assign) { for (i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, frame); + image_assign_ibuf(ima, ibuf_arr[i], i, entry); } } @@ -3215,9 +3431,10 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) return ibuf; } -static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int frame) +static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame) { struct ImBuf *ibuf = NULL; + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); /* either we load from RenderResult, or we have to load a new one */ @@ -3233,7 +3450,7 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f ima->rr = NULL; } - ibuf = image_load_sequence_file(ima, iuser, frame); + ibuf = image_load_sequence_file(ima, iuser, entry, frame); if (ibuf) { /* actually an error */ ima->type = IMA_TYPE_IMAGE; @@ -3252,17 +3469,17 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f ibuf->mall = IB_rectfloat; ibuf->channels = rpass->channels; - image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, frame); + image_initialize_after_load(ima, iuser, ibuf); + image_assign_ibuf(ima, ibuf, iuser ? iuser->multi_index : 0, entry); } // else printf("pass not found\n"); } else - ima->ok = 0; + tile->ok = 0; if (iuser) - iuser->ok = ima->ok; + iuser->ok = tile->ok; return ibuf; } @@ -3274,6 +3491,8 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i ia = BLI_findlink(&ima->anims, view_id); + ImageTile *tile = BKE_image_get_tile(ima, 0); + if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; @@ -3312,13 +3531,13 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i IMB_PROXY_NONE)); if (ibuf) { - image_initialize_after_load(ima, ibuf); + image_initialize_after_load(ima, iuser, ibuf); } else - ima->ok = 0; + tile->ok = 0; } else - ima->ok = 0; + tile->ok = 0; return ibuf; } @@ -3329,6 +3548,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); int i; + ImageTile *tile = BKE_image_get_tile(ima, 0); if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) { image_free_anims(ima); @@ -3362,7 +3582,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) image_assign_ibuf(ima, ibuf_arr[i], i, frame); } else { - ima->ok = 0; + tile->ok = 0; } } @@ -3381,7 +3601,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) } if (iuser) - iuser->ok = ima->ok; + iuser->ok = tile->ok; return ibuf; } @@ -3447,7 +3667,7 @@ static ImBuf *load_image_single( else #endif { - image_initialize_after_load(ima, ibuf); + image_initialize_after_load(ima, iuser, ibuf); *r_assign = true; /* check if the image is a font image... */ @@ -3464,7 +3684,8 @@ static ImBuf *load_image_single( } } else { - ima->ok = 0; + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + tile->ok = 0; } return ibuf; @@ -3537,8 +3758,10 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) MEM_freeN(ibuf_arr); } - if (iuser) - iuser->ok = ima->ok; + if (iuser) { + ImageTile *tile = BKE_image_get_tile(ima, 0); + iuser->ok = tile->ok; + } return ibuf; } @@ -3560,7 +3783,7 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) if (rpass) { ibuf = IMB_allocImBuf(ima->rr->rectx, ima->rr->recty, 32, 0); - image_initialize_after_load(ima, ibuf); + image_initialize_after_load(ima, iuser, ibuf); ibuf->rect_float = rpass->rect; ibuf->flags |= IB_rectfloat; @@ -3570,10 +3793,11 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) } } + ImageTile *tile = BKE_image_get_tile(ima, 0); if (ibuf == NULL) - ima->ok = 0; + tile->ok = 0; if (iuser) - iuser->ok = ima->ok; + iuser->ok = tile->ok; return ibuf; } @@ -3695,7 +3919,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc } } - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); /* make ibuf if needed, and initialize it */ if (ibuf == NULL) { @@ -3770,7 +3994,8 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc ibuf->dither = dither; - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; return ibuf; } @@ -3779,7 +4004,7 @@ static int image_get_multiview_index(Image *ima, ImageUser *iuser) { const bool is_multilayer = BKE_image_is_multilayer(ima); const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL); - int index = BKE_image_is_animated(ima) ? 0 : IMA_NO_INDEX; + int index = BKE_image_has_multiple_ibufs(ima) ? 0 : IMA_NO_INDEX; if (is_multilayer) { return iuser ? iuser->multi_index : index; @@ -3797,24 +4022,27 @@ static int image_get_multiview_index(Image *ima, ImageUser *iuser) return index; } -static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index) +static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) { - int frame = 0, index = image_get_multiview_index(ima, iuser); + int entry = 0, index = image_get_multiview_index(ima, iuser); /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { - frame = iuser ? iuser->framenr : ima->lastframe; + entry = iuser ? iuser->framenr : ima->lastframe; } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { - frame = iuser ? iuser->framenr : ima->lastframe; + entry = iuser ? iuser->framenr : ima->lastframe; } else if (ima->type == IMA_TYPE_MULTILAYER) { - frame = iuser ? iuser->framenr : ima->lastframe; + entry = iuser ? iuser->framenr : ima->lastframe; } } + else if (ima->source == IMA_SRC_TILED) { + entry = iuser? iuser->tile : 0; + } - *r_frame = frame; + *r_entry = entry; *r_index = index; } @@ -3824,54 +4052,70 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, int *r_index) +static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) { ImBuf *ibuf = NULL; - int frame = 0, index = image_get_multiview_index(ima, iuser); + int entry = 0, index = image_get_multiview_index(ima, iuser); /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { - frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); /* XXX temp stuff? */ - if (ima->lastframe != frame) + if (ima->lastframe != entry) ima->tpageflag |= IMA_TPAGE_REFRESH; - ima->lastframe = frame; + ima->lastframe = entry; } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { - frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); /* XXX temp stuff? */ - if (ima->lastframe != frame) { + if (ima->lastframe != entry) { ima->tpageflag |= IMA_TPAGE_REFRESH; } - ima->lastframe = frame; + ima->lastframe = entry; /* counter the fact that image is set as invalid when loading a frame * that is not in the cache (through image_acquire_ibuf for instance), * yet we have valid frames in the cache loaded */ if (ibuf) { - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; if (iuser) - iuser->ok = ima->ok; + iuser->ok = tile->ok; } } else if (ima->type == IMA_TYPE_MULTILAYER) { - frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); + entry = iuser ? iuser->framenr : ima->lastframe; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + } + } + else if (ima->source == IMA_SRC_TILED) { + if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { + entry = iuser? iuser->tile : 0; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + + if ((ima->type == IMA_TYPE_IMAGE) && ibuf) { + ImageTile *tile = BKE_image_get_tile(ima, entry); + tile->ok = IMA_OK_LOADED; + + /* iuser->ok is useless for tiled images because iuser->tile changes all the time. */ + if (iuser) + iuser->ok = 1; + } } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); else if (ima->type == IMA_TYPE_MULTILAYER) - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -3879,8 +4123,8 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, * a big bottleneck */ } - if (r_frame) - *r_frame = frame; + if (r_entry) + *r_entry = entry; if (r_index) *r_index = index; @@ -3893,11 +4137,17 @@ BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser) if (ima == NULL) return false; + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + if (iuser) { if (iuser->ok == 0) return false; + if (ima->source == IMA_SRC_TILED) { + if (tile->ok == 0) + return false; + } } - else if (ima->ok == 0) + else if (tile->ok == 0) return false; return true; @@ -3910,7 +4160,7 @@ BLI_INLINE bool image_quick_test(Image *ima, ImageUser *iuser) static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) { ImBuf *ibuf = NULL; - int frame = 0, index = 0; + int entry = 0, index = 0; if (r_lock) *r_lock = NULL; @@ -3919,29 +4169,40 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) if (!image_quick_test(ima, iuser)) return NULL; - ibuf = image_get_cached_ibuf(ima, iuser, &frame, &index); + ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index); if (ibuf == NULL) { /* we are sure we have to load the ibuf, using source and type */ if (ima->source == IMA_SRC_MOVIE) { /* source is from single file, use flipbook to store ibuf */ - ibuf = image_load_movie_file(ima, iuser, frame); + ibuf = image_load_movie_file(ima, iuser, entry); } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { /* regular files, ibufs in flipbook, allows saving */ - ibuf = image_load_sequence_file(ima, iuser, frame); + ibuf = image_load_sequence_file(ima, iuser, entry, entry); + } + /* no else; on load the ima type can change */ + if (ima->type == IMA_TYPE_MULTILAYER) { + /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ + ibuf = image_load_sequence_multilayer(ima, iuser, entry, entry); + } + } + else if (ima->source == IMA_SRC_TILED) { + if (ima->type == IMA_TYPE_IMAGE) { + /* regular files, ibufs in flipbook, allows saving */ + ibuf = image_load_sequence_file(ima, iuser, entry, 0); } /* no else; on load the ima type can change */ if (ima->type == IMA_TYPE_MULTILAYER) { /* only 1 layer/pass stored in imbufs, no exrhandle anim storage, no saving */ - ibuf = image_load_sequence_multilayer(ima, iuser, frame); + ibuf = image_load_sequence_multilayer(ima, iuser, entry, 0); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_load_image_file(ima, iuser, frame); /* cfra only for '#', this global is OK */ + ibuf = image_load_image_file(ima, iuser, entry); /* cfra only for '#', this global is OK */ /* no else; on load the ima type can change */ if (ima->type == IMA_TYPE_MULTILAYER) /* keeps render result, stores ibufs in listbase, allows saving */ @@ -3956,7 +4217,8 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type, ima->gen_color, &ima->colorspace_settings); image_assign_ibuf(ima, ibuf, index, 0); - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { if (ima->type == IMA_TYPE_R_RESULT) { @@ -3972,14 +4234,14 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) *r_lock = ima; /* XXX anim play for viewer nodes not yet supported */ - frame = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); + entry = 0; // XXX iuser ? iuser->framenr : 0; + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); if (!ibuf) { /* Composite Viewer, all handled in compositor */ /* fake ibuf, will be filled in compositor */ ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); - image_assign_ibuf(ima, ibuf, index, frame); + image_assign_ibuf(ima, ibuf, index, entry); } } } @@ -4066,7 +4328,7 @@ typedef struct ImagePoolEntry { Image *image; ImBuf *ibuf; int index; - int frame; + int entry; } ImagePoolEntry; typedef struct ImagePool { @@ -4100,16 +4362,16 @@ void BKE_image_pool_free(ImagePool *pool) MEM_freeN(pool); } -BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int frame, int index, bool *found) +BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int entry, int index, bool *found) { - ImagePoolEntry *entry; + ImagePoolEntry *pool_entry; *found = false; - for (entry = pool->image_buffers.first; entry; entry = entry->next) { - if (entry->image == image && entry->frame == frame && entry->index == index) { + for (pool_entry = pool->image_buffers.first; pool_entry; pool_entry = pool_entry->next) { + if (pool_entry->image == image && pool_entry->entry == entry && pool_entry->index == index) { *found = true; - return entry->ibuf; + return pool_entry->ibuf; } } @@ -4119,7 +4381,7 @@ BLI_INLINE ImBuf *image_pool_find_entry(ImagePool *pool, Image *image, int frame ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool) { ImBuf *ibuf; - int index, frame; + int index, entry; bool found; if (!image_quick_test(ima, iuser)) @@ -4130,31 +4392,31 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool return BKE_image_acquire_ibuf(ima, iuser, NULL); } - image_get_frame_and_index(ima, iuser, &frame, &index); + image_get_entry_and_index(ima, iuser, &entry, &index); - ibuf = image_pool_find_entry(pool, ima, frame, index, &found); + ibuf = image_pool_find_entry(pool, ima, entry, index, &found); if (found) return ibuf; BLI_spin_lock(&image_spin); - ibuf = image_pool_find_entry(pool, ima, frame, index, &found); + ibuf = image_pool_find_entry(pool, ima, entry, index, &found); /* will also create entry even in cases image buffer failed to load, * prevents trying to load the same buggy file multiple times */ if (!found) { - ImagePoolEntry *entry; + ImagePoolEntry *pool_entry; ibuf = image_acquire_ibuf(ima, iuser, NULL); - entry = BLI_mempool_alloc(pool->memory_pool); - entry->image = ima; - entry->frame = frame; - entry->index = index; - entry->ibuf = ibuf; + pool_entry = BLI_mempool_alloc(pool->memory_pool); + pool_entry->image = ima; + pool_entry->entry = entry; + pool_entry->index = index; + pool_entry->ibuf = ibuf; - BLI_addtail(&pool->image_buffers, entry); + BLI_addtail(&pool->image_buffers, pool_entry); } BLI_spin_unlock(&image_spin); @@ -4289,13 +4551,20 @@ void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) BLI_strncpy(filepath, ima->name, FILE_MAX); } - if (ima->source == IMA_SRC_SEQUENCE) { + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { char head[FILE_MAX], tail[FILE_MAX]; unsigned short numlen; - int frame = iuser ? iuser->framenr : ima->lastframe; + + int index; + if (ima->source == IMA_SRC_SEQUENCE) { + index = iuser ? iuser->framenr : ima->lastframe; + } + else { + index = 1001 + (iuser ? iuser->tile : 0); + } BLI_stringdec(filepath, head, tail, &numlen); - BLI_stringenc(filepath, head, tail, numlen, frame); + BLI_stringenc(filepath, head, tail, numlen, index); } BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&ima->id)); @@ -4372,7 +4641,7 @@ void BKE_image_get_aspect(Image *image, float *aspx, float *aspy) *aspy = 1.0f; } -unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame) +unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame, int tile) { ImageUser iuser = {NULL}; void *lock; @@ -4380,6 +4649,7 @@ unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame) unsigned char *pixels = NULL; iuser.framenr = frame; + iuser.tile = tile; iuser.ok = true; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); @@ -4399,7 +4669,7 @@ unsigned char *BKE_image_get_pixels_for_frame(struct Image *image, int frame) return pixels; } -float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame) +float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame, int tile) { ImageUser iuser = {NULL}; void *lock; @@ -4407,6 +4677,7 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame) float *pixels = NULL; iuser.framenr = frame; + iuser.tile = tile; iuser.ok = true; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); @@ -4451,6 +4722,12 @@ bool BKE_image_is_animated(Image *image) return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE); } +/* Checks whether the image consists of multiple buffers. */ +bool BKE_image_has_multiple_ibufs(Image *image) +{ + return ELEM(image->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE, IMA_SRC_TILED); +} + bool BKE_image_is_dirty(Image *image) { bool is_dirty = false; diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index da455faaa86..cd596bfd26f 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -237,7 +237,7 @@ void packAll(Main *bmain, ReportList *reports, bool verbose) BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot ++; } - else if (BKE_image_is_animated(ima) && verbose) { + else if (BKE_image_has_multiple_ibufs(ima) && verbose) { BKE_reportf(reports, RPT_WARNING, "Image '%s' skipped, movies and image sequences not supported", ima->id.name + 2); } diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index ec16a6854c4..b391e77c31c 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -262,6 +262,8 @@ MINLINE bool equals_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED MINLINE bool equals_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT; MINLINE bool equals_v4v4(const float a[4], const float b[4]) ATTR_WARN_UNUSED_RESULT; +MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) ATTR_WARN_UNUSED_RESULT; + MINLINE bool compare_v2v2(const float a[2], const float b[2], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_v3v3(const float a[3], const float b[3], const float limit) ATTR_WARN_UNUSED_RESULT; MINLINE bool compare_v4v4(const float a[4], const float b[4], const float limit) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index 189b94a6f13..8dd7c2ef945 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -1042,6 +1042,11 @@ MINLINE bool equals_v4v4(const float v1[4], const float v2[4]) return ((v1[0] == v2[0]) && (v1[1] == v2[1]) && (v1[2] == v2[2]) && (v1[3] == v2[3])); } +MINLINE bool equals_v2v2_int(const int v1[2], const int v2[2]) +{ + return ((v1[0] == v2[0]) && (v1[1] == v2[1])); +} + MINLINE bool compare_v2v2(const float v1[2], const float v2[2], const float limit) { return (compare_ff(v1[0], v2[0], limit) && diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index f226693b989..91abcdf7a89 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1677,21 +1677,24 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain) { Image *ima = oldmain->image.first; Scene *sce = oldmain->scene.first; - int a; fd->imamap = oldnewmap_new(); for (; ima; ima = ima->id.next) { if (ima->cache) oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0); - for (a = 0; a < TEXTARGET_COUNT; a++) - if (ima->gputexture[a]) - oldnewmap_insert(fd->imamap, ima->gputexture[a], ima->gputexture[a], 0); if (ima->rr) oldnewmap_insert(fd->imamap, ima->rr, ima->rr, 0); - LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) - if (slot->render) - oldnewmap_insert(fd->imamap, slot->render, slot->render, 0); + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + for (int a = 0; a < TEXTARGET_COUNT; a++) { + if (tile->gputexture[a]) { + oldnewmap_insert(fd->imamap, tile->gputexture[a], tile->gputexture[a], 0); + } + } + } + LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) + if (slot->render) + oldnewmap_insert(fd->imamap, slot->render, slot->render, 0); } for (; sce; sce = sce->id.next) { if (sce->nodetree && sce->nodetree->previews) { @@ -1711,10 +1714,9 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain) OldNew *entry = fd->imamap->entries; Image *ima = oldmain->image.first; Scene *sce = oldmain->scene.first; - int i; /* used entries were restored, so we put them to zero */ - for (i = 0; i < fd->imamap->nentries; i++, entry++) { + for (int i = 0; i < fd->imamap->nentries; i++, entry++) { if (entry->nr > 0) entry->newp = NULL; } @@ -1723,16 +1725,23 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain) ima->cache = newimaadr(fd, ima->cache); if (ima->cache == NULL) { ima->tpageflag &= ~IMA_GLBIND_IS_DATA; - for (i = 0; i < TEXTARGET_COUNT; i++) { - ima->gputexture[i] = NULL; - } ima->rr = NULL; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + for (int j = 0; j < TEXTARGET_COUNT; j++) { + tile->gputexture[j] = NULL; + } + } } + LISTBASE_FOREACH(RenderSlot *, slot, &ima->renderslots) slot->render = newimaadr(fd, slot->render); - for (i = 0; i < TEXTARGET_COUNT; i++) - ima->gputexture[i] = newimaadr(fd, ima->gputexture[i]); + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + for (int j = 0; j < TEXTARGET_COUNT; j++) { + tile->gputexture[j] = newimaadr(fd, tile->gputexture[j]); + } + } + ima->rr = newimaadr(fd, ima->rr); } for (; sce; sce = sce->id.next) { @@ -3950,11 +3959,15 @@ static void direct_link_image(FileData *fd, Image *ima) else ima->cache = NULL; + link_list(fd, &(ima->tiles)); + /* if not restored, we keep the binded opengl index */ if (!ima->cache) { ima->tpageflag &= ~IMA_GLBIND_IS_DATA; - for (int i = 0; i < TEXTARGET_COUNT; i++) { - ima->gputexture[i] = NULL; + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + tile->gputexture[i] = NULL; + } } ima->rr = NULL; } @@ -3989,7 +4002,10 @@ static void direct_link_image(FileData *fd, Image *ima) BLI_listbase_clear(&ima->anims); ima->preview = direct_link_preview_image(fd, ima->preview); ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format); - ima->ok = 1; + + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + tile->ok = 1; + } } diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 39ceb527209..d10e9ff294a 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1490,6 +1490,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + if (!DNA_struct_elem_find(fd->filesdna, "SpaceAction", "char", "mode_prev")) { for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { @@ -1640,6 +1641,28 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) BKE_screen_view3d_shading_init(&scene->display.shading); } } + + if (!DNA_struct_elem_find(fd->filesdna, "Image", "short", "num_tiles")) { + for (Image *ima = bmain->image.first; ima; ima = ima->id.next) { + ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles"); + tile->ok = 1; + BLI_addtail(&ima->tiles, tile); + } + } + + if (!DNA_struct_elem_find(fd->filesdna, "SpaceImage", "int", "tile_grid_shape[2]")) { + for (bScreen *screen = bmain->screen.first; screen; screen = screen->id.next) { + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + sima->tile_grid_shape[0] = 1; + sima->tile_grid_shape[1] = 1; + } + } + } + } + } } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index 503f8b44ec3..ce2362dacc8 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2210,6 +2210,8 @@ static void write_image(WriteData *wd, Image *ima) } writestruct(wd, DATA, Stereo3dFormat, 1, ima->stereo3d_format); + writelist(wd, DATA, ImageTile, &ima->tiles); + ima->packedfile = NULL; writelist(wd, DATA, RenderSlot, &ima->renderslots); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index 51fd5d4dc76..2734069a449 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -155,7 +155,8 @@ void ViewerOperation::initImage() /* zero size can happen if no image buffers exist to define a sensible resolution */ if (ibuf->x > 0 && ibuf->y > 0) imb_addrectfloatImBuf(ibuf); - ima->ok = IMA_OK_LOADED; + ImageTile *tile = BKE_image_get_tile(ima, 0); + tile->ok = IMA_OK_LOADED; ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index 23fbbe56c16..4692b026d76 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -32,6 +32,7 @@ #include "BLI_utildefines.h" #include "BLI_rand.h" +#include "BKE_image.h" #include "BKE_node.h" #include "BKE_modifier.h" #include "BKE_particle.h" @@ -568,7 +569,7 @@ void workbench_deferred_cache_init(WORKBENCH_Data *vedata) } static WORKBENCH_MaterialData *get_or_create_material_data( - WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type) + WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int image_tile) { WORKBENCH_StorageList *stl = vedata->stl; WORKBENCH_PassList *psl = vedata->psl; @@ -583,6 +584,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data( material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1; material_template.color_type = color_type; material_template.ima = ima; + material_template.image_tile = image_tile; uint hash = workbench_material_get_hash(&material_template); material = BLI_ghash_lookup(wpd->material_hash, SET_UINT_IN_POINTER(hash)); @@ -628,7 +630,7 @@ static void workbench_cache_populate_particles(WORKBENCH_Data *vedata, Object *o Material *mat = give_current_material(ob, part->omat); ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL); int color_type = workbench_material_determine_color_type(wpd, image); - WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type); + WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, 0); struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ? wpd->prepass_solid_hair_sh : @@ -680,23 +682,32 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) bool is_drawn = false; if (!is_sculpt_mode && TEXTURE_DRAWING_ENABLED(wpd) && ELEM(ob->type, OB_MESH)) { const Mesh *me = ob->data; - if (me->mloopuv) { + if (me->mloopuv && me->totcol) { const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol)); - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL; - if (materials_len > 0 && geom_array) { - for (int i = 0; i < materials_len; i++) { - if (geom_array[i] == NULL) { - continue; - } - Material *mat = give_current_material(ob, i + 1); - Image *image; - ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); - int color_type = workbench_material_determine_color_type(wpd, image); - material = get_or_create_material_data(vedata, ob, mat, image, color_type); - DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob); + bool *is_tiled = MEM_mallocN(sizeof(bool)*materials_len, "workbench deferred is tiled"); + for(int i = 0; i < materials_len; i++) { + Image *image; + ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); + is_tiled[i] = image && (image->source == IMA_SRC_TILED); + } + + int num_batches = 0; + struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches); + + for (int i = 0; i < num_batches; i++) { + if (geom_batches[i].batch == NULL) { + continue; } + + int matid = geom_batches[i].material + 1; + Material *mat = give_current_material(ob, matid); + Image *image; + ED_object_get_active_image(ob, matid, &image, NULL, NULL, NULL); + int color_type = workbench_material_determine_color_type(wpd, image); + material = get_or_create_material_data(vedata, ob, mat, image, color_type, geom_batches[i].tile); + DRW_shgroup_call_object_add(material->shgrp, geom_batches[i].batch, ob); + is_drawn = true; } } @@ -708,7 +719,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) /* No material split needed */ struct GPUBatch *geom = DRW_cache_object_surface_get(ob); if (geom) { - material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type); + material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0); if (is_sculpt_mode) { DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat); } @@ -733,7 +744,7 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) } Material *mat = give_current_material(ob, i + 1); - material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR); + material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0); if (is_sculpt_mode) { DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat); } diff --git a/source/blender/draw/engines/workbench/workbench_forward.c b/source/blender/draw/engines/workbench/workbench_forward.c index c81ecc492db..e4a9c8abe26 100644 --- a/source/blender/draw/engines/workbench/workbench_forward.c +++ b/source/blender/draw/engines/workbench/workbench_forward.c @@ -31,6 +31,7 @@ #include "BLI_dynstr.h" #include "BLI_utildefines.h" +#include "BKE_image.h" #include "BKE_node.h" #include "BKE_particle.h" #include "BKE_modifier.h" @@ -137,7 +138,7 @@ static void workbench_init_object_data(DrawData *dd) } static WORKBENCH_MaterialData *get_or_create_material_data( - WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type) + WORKBENCH_Data *vedata, Object *ob, Material *mat, Image *ima, int color_type, int image_tile) { WORKBENCH_StorageList *stl = vedata->stl; WORKBENCH_PassList *psl = vedata->psl; @@ -153,6 +154,7 @@ static WORKBENCH_MaterialData *get_or_create_material_data( material_template.object_id = OBJECT_ID_PASS_ENABLED(wpd) ? engine_object_data->object_id : 1; material_template.color_type = color_type; material_template.ima = ima; + material_template.image_tile = image_tile; uint hash = workbench_material_get_hash(&material_template); material = BLI_ghash_lookup(wpd->material_hash, SET_UINT_IN_POINTER(hash)); @@ -181,9 +183,12 @@ static WORKBENCH_MaterialData *get_or_create_material_data( /* Depth */ if (workbench_material_determine_color_type(wpd, material->ima) == V3D_SHADING_TEXTURE_COLOR) { + ImageUser iuser = {NULL}; + iuser.ok = 1; + iuser.tile = material->image_tile; material->shgrp_object_outline = DRW_shgroup_create( e_data.object_outline_texture_sh, psl->object_outline_pass); - GPUTexture *tex = GPU_texture_from_blender(material->ima, NULL, GL_TEXTURE_2D, false, 0.0f); + GPUTexture *tex = GPU_texture_from_blender(material->ima, &iuser, GL_TEXTURE_2D, false, 0.0f); DRW_shgroup_uniform_texture(material->shgrp_object_outline, "image", tex); } else { @@ -420,7 +425,7 @@ static void workbench_forward_cache_populate_particles(WORKBENCH_Data *vedata, O Material *mat = give_current_material(ob, part->omat); ED_object_get_active_image(ob, part->omat, &image, NULL, NULL, NULL); int color_type = workbench_material_determine_color_type(wpd, image); - WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type); + WORKBENCH_MaterialData *material = get_or_create_material_data(vedata, ob, mat, image, color_type, 0); struct GPUShader *shader = (color_type != V3D_SHADING_TEXTURE_COLOR) ? wpd->transparent_accum_hair_sh @@ -490,31 +495,39 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) const Mesh *me = ob->data; if (me->mloopuv) { const int materials_len = MAX2(1, (is_sculpt_mode ? 1 : ob->totcol)); - struct GPUMaterial **gpumat_array = BLI_array_alloca(gpumat_array, materials_len); - struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL; - if (materials_len > 0 && geom_array) { - for (int i = 0; i < materials_len; i++) { - if (geom_array[i] == NULL) { - continue; - } - Material *mat = give_current_material(ob, i + 1); - Image *image; - ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); - /* use OB_SOLID when no texture could be determined */ + bool *is_tiled = MEM_mallocN(sizeof(bool)*materials_len, "workbench forward is tiled"); + for(int i = 0; i < materials_len; i++) { + Image *image; + ED_object_get_active_image(ob, i + 1, &image, NULL, NULL, NULL); + is_tiled[i] = image && (image->source == IMA_SRC_TILED); + } - int color_type = wpd->shading.color_type; - if (color_type == V3D_SHADING_TEXTURE_COLOR) { - /* use OB_SOLID when no texture could be determined */ - if (image == NULL) { - color_type = V3D_SHADING_MATERIAL_COLOR; - } - } + int num_batches = 0; + struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches); - material = get_or_create_material_data(vedata, ob, mat, image, color_type); - DRW_shgroup_call_object_add(material->shgrp_object_outline, geom_array[i], ob); - DRW_shgroup_call_object_add(material->shgrp, geom_array[i], ob); + for (int i = 0; i < num_batches; i++) { + if (geom_batches[i].batch == NULL) { + continue; } + + int matid = geom_batches[i].material + 1; + Material *mat = give_current_material(ob, matid); + Image *image; + ED_object_get_active_image(ob, matid, &image, NULL, NULL, NULL); + /* use OB_SOLID when no texture could be determined */ + + int color_type = wpd->shading.color_type; + if (color_type == V3D_SHADING_TEXTURE_COLOR) { + /* use OB_SOLID when no texture could be determined */ + if (image == NULL) { + color_type = V3D_SHADING_MATERIAL_COLOR; + } + } + + material = get_or_create_material_data(vedata, ob, mat, image, color_type, geom_batches[i].tile); + DRW_shgroup_call_object_add(material->shgrp_object_outline, geom_batches[i].batch, ob); + DRW_shgroup_call_object_add(material->shgrp, geom_batches[i].batch, ob); is_drawn = true; } } @@ -526,7 +539,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) /* No material split needed */ struct GPUBatch *geom = DRW_cache_object_surface_get(ob); if (geom) { - material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type); + material = get_or_create_material_data(vedata, ob, NULL, NULL, wpd->shading.color_type, 0); if (is_sculpt_mode) { DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat); DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat); @@ -553,7 +566,7 @@ void workbench_forward_cache_populate(WORKBENCH_Data *vedata, Object *ob) } Material *mat = give_current_material(ob, i + 1); - material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR); + material = get_or_create_material_data(vedata, ob, mat, NULL, V3D_SHADING_MATERIAL_COLOR, 0); if (is_sculpt_mode) { DRW_shgroup_call_sculpt_add(material->shgrp_object_outline, ob, ob->obmat); DRW_shgroup_call_sculpt_add(material->shgrp, ob, ob->obmat); diff --git a/source/blender/draw/engines/workbench/workbench_materials.c b/source/blender/draw/engines/workbench/workbench_materials.c index 54e8aa1c3b3..156cfb9f051 100644 --- a/source/blender/draw/engines/workbench/workbench_materials.c +++ b/source/blender/draw/engines/workbench/workbench_materials.c @@ -131,6 +131,7 @@ uint workbench_material_get_hash(WORKBENCH_MaterialData *material_template) /* add texture reference */ if (material_template->ima) { result += BLI_ghashutil_inthash_p_murmur(material_template->ima); + result += BLI_ghashutil_inthash(material_template->image_tile); } return result; @@ -185,7 +186,10 @@ void workbench_material_shgroup_uniform( WORKBENCH_PrivateData *wpd, DRWShadingGroup *grp, WORKBENCH_MaterialData *material) { if (workbench_material_determine_color_type(wpd, material->ima) == V3D_SHADING_TEXTURE_COLOR) { - GPUTexture *tex = GPU_texture_from_blender(material->ima, NULL, GL_TEXTURE_2D, false, 0.0f); + ImageUser iuser = {NULL}; + iuser.ok = 1; + iuser.tile = material->image_tile; + GPUTexture *tex = GPU_texture_from_blender(material->ima, &iuser, GL_TEXTURE_2D, false, 0.0f); DRW_shgroup_uniform_texture(grp, "image", tex); } else { @@ -205,4 +209,5 @@ void workbench_material_copy(WORKBENCH_MaterialData *dest_material, const WORKBE copy_v4_v4(dest_material->specular_color, source_material->specular_color); dest_material->roughness = source_material->roughness; dest_material->ima = source_material->ima; + dest_material->image_tile = source_material->image_tile; } diff --git a/source/blender/draw/engines/workbench/workbench_private.h b/source/blender/draw/engines/workbench/workbench_private.h index 99d1b7060fd..795695c8a08 100644 --- a/source/blender/draw/engines/workbench/workbench_private.h +++ b/source/blender/draw/engines/workbench/workbench_private.h @@ -205,6 +205,7 @@ typedef struct WORKBENCH_MaterialData { int object_id; int color_type; Image *ima; + int image_tile; /* Linked shgroup for drawing */ DRWShadingGroup *shgrp; diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index dff863b11bb..bd8a549dbca 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2870,20 +2870,20 @@ GPUBatch **DRW_cache_mesh_surface_shaded_get( } /* Return list of batches */ -GPUBatch **DRW_cache_mesh_surface_texpaint_get(Object *ob) +TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_get(Object *ob, bool *is_tiled, int *num_batches) { BLI_assert(ob->type == OB_MESH); Mesh *me = ob->data; - return DRW_mesh_batch_cache_get_surface_texpaint(me); + return DRW_mesh_batch_cache_get_surface_texpaint(me, is_tiled, num_batches); } -GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob) +TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_single_get(Object *ob, bool is_tiled, int *num_batches) { BLI_assert(ob->type == OB_MESH); Mesh *me = ob->data; - return DRW_mesh_batch_cache_get_surface_texpaint_single(me); + return DRW_mesh_batch_cache_get_surface_texpaint_single(me, is_tiled, num_batches); } GPUBatch *DRW_cache_mesh_surface_verts_get(Object *ob) diff --git a/source/blender/draw/intern/draw_cache.h b/source/blender/draw/intern/draw_cache.h index 129c0252f30..b43f93b5826 100644 --- a/source/blender/draw/intern/draw_cache.h +++ b/source/blender/draw/intern/draw_cache.h @@ -32,6 +32,12 @@ struct ModifierData; struct Object; struct PTCacheEdit; +typedef struct TexpaintCacheBatch { + struct GPUBatch *batch; + int material; + int tile; +} TexpaintCacheBatch; + void DRW_shape_cache_free(void); void DRW_shape_cache_reset(void); @@ -142,8 +148,8 @@ struct GPUBatch *DRW_cache_mesh_verts_weight_overlay_get(struct Object *ob); struct GPUBatch **DRW_cache_mesh_surface_shaded_get( struct Object *ob, struct GPUMaterial **gpumat_array, uint gpumat_array_len, char **auto_layer_names, int **auto_layer_is_srgb, int *auto_layer_count); -struct GPUBatch **DRW_cache_mesh_surface_texpaint_get(struct Object *ob); -struct GPUBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob); +struct TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_get(struct Object *ob, bool *is_tiled, int *num_batches); +struct TexpaintCacheBatch *DRW_cache_mesh_surface_texpaint_single_get(struct Object *ob, bool is_tiled, int *num_batches); void DRW_cache_mesh_sculpt_coords_ensure(struct Object *ob); diff --git a/source/blender/draw/intern/draw_cache_impl.h b/source/blender/draw/intern/draw_cache_impl.h index eeb7b1c41ee..173674a7a1f 100644 --- a/source/blender/draw/intern/draw_cache_impl.h +++ b/source/blender/draw/intern/draw_cache_impl.h @@ -97,8 +97,8 @@ struct GPUBatch *DRW_lattice_batch_cache_get_overlay_verts(struct Lattice *lt); struct GPUBatch **DRW_mesh_batch_cache_get_surface_shaded( struct Mesh *me, struct GPUMaterial **gpumat_array, uint gpumat_array_len, char **auto_layer_names, int **auto_layer_is_srgb, int *auto_layer_count); -struct GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me); -struct GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me); +struct TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint(struct Mesh *me, bool *is_tiled, int *num_batches); +struct TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(struct Mesh *me, bool is_tiled, int *num_batches); struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_edges(struct Mesh *me, bool use_wire, bool use_sel); struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_faces(struct Mesh *me); struct GPUBatch *DRW_mesh_batch_cache_get_weight_overlay_verts(struct Mesh *me); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 5ca1c65054c..1f0a6ed7ede 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -1558,6 +1558,8 @@ typedef struct MeshBatchCache { GPUVertBuf *pos_with_normals; GPUVertBuf *tri_aligned_uv; /* Active UV layer (mloopuv) */ + int *tri_tiles; + /** * Other uses are all positions or loose elements. * This stores all visible elements, needed for selection. @@ -1599,12 +1601,16 @@ typedef struct MeshBatchCache { GPUVertFormat shaded_triangles_format; GPUVertBuf *shaded_triangles_data; GPUIndexBuf **shaded_triangles_in_order; + GPUIndexBuf **shaded_triangles_in_order_tiled; + GPUIndexBuf **shaded_triangles_in_order_tiled_single; GPUBatch **shaded_triangles; /* Texture Paint.*/ /* per-texture batch */ - GPUBatch **texpaint_triangles; - GPUBatch *texpaint_triangles_single; + int num_texpaint_batches; + int num_texpaint_batches_single; + TexpaintCacheBatch *texpaint_triangles; + TexpaintCacheBatch *texpaint_triangles_single; /* Edit Cage Mesh buffers */ GPUVertBuf *ed_tri_pos; @@ -1801,12 +1807,17 @@ static void mesh_batch_cache_clear_selective(Mesh *me, GPUVertBuf *vert) } MEM_SAFE_FREE(cache->shaded_triangles); if (cache->texpaint_triangles) { - for (int i = 0; i < cache->mat_len; ++i) { - GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]); + for (int i = 0; i < cache->num_texpaint_batches; ++i) { + GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i].batch); } } MEM_SAFE_FREE(cache->texpaint_triangles); - GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single); + if (cache->texpaint_triangles_single) { + for (int i = 0; i < cache->num_texpaint_batches_single; ++i) { + GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single[i].batch); + } + } + MEM_SAFE_FREE(cache->texpaint_triangles_single); } /* TODO: add the other ones if needed. */ else { @@ -1859,6 +1870,7 @@ static void mesh_batch_cache_clear(Mesh *me) GPU_BATCH_DISCARD_SAFE(cache->triangles_with_weights); GPU_BATCH_DISCARD_SAFE(cache->triangles_with_vert_colors); GPU_VERTBUF_DISCARD_SAFE(cache->tri_aligned_uv); + MEM_SAFE_FREE(cache->tri_tiles); GPU_VERTBUF_DISCARD_SAFE(cache->ed_fcenter_pos_with_nor_and_sel); GPU_VERTBUF_DISCARD_SAFE(cache->ed_edge_pos); GPU_VERTBUF_DISCARD_SAFE(cache->ed_vert_pos); @@ -1877,31 +1889,50 @@ static void mesh_batch_cache_clear(Mesh *me) DRW_TEXTURE_FREE_SAFE(cache->edges_face_overlay_tx); GPU_VERTBUF_DISCARD_SAFE(cache->shaded_triangles_data); + if (cache->shaded_triangles_in_order) { for (int i = 0; i < cache->mat_len; ++i) { GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order[i]); } } + MEM_SAFE_FREE(cache->shaded_triangles_in_order); + + if (cache->shaded_triangles_in_order_tiled) { + for (int i = 0; i < cache->num_texpaint_batches; ++i) { + GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order_tiled[i]); + } + } + MEM_SAFE_FREE(cache->shaded_triangles_in_order_tiled); + + if (cache->shaded_triangles_in_order_tiled_single) { + for (int i = 0; i < cache->num_texpaint_batches_single; ++i) { + GPU_INDEXBUF_DISCARD_SAFE(cache->shaded_triangles_in_order_tiled_single[i]); + } + } + MEM_SAFE_FREE(cache->shaded_triangles_in_order_tiled_single); + if (cache->shaded_triangles) { for (int i = 0; i < cache->mat_len; ++i) { GPU_BATCH_DISCARD_SAFE(cache->shaded_triangles[i]); } } - - MEM_SAFE_FREE(cache->shaded_triangles_in_order); MEM_SAFE_FREE(cache->shaded_triangles); MEM_SAFE_FREE(cache->auto_layer_names); MEM_SAFE_FREE(cache->auto_layer_is_srgb); if (cache->texpaint_triangles) { - for (int i = 0; i < cache->mat_len; ++i) { - GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i]); + for (int i = 0; i < cache->num_texpaint_batches; ++i) { + GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles[i].batch); } } - MEM_SAFE_FREE(cache->texpaint_triangles); - - GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single); + MEM_SAFE_FREE(cache->texpaint_triangles_single); + if (cache->texpaint_triangles_single) { + for (int i = 0; i < cache->num_texpaint_batches_single; ++i) { + GPU_BATCH_DISCARD_SAFE(cache->texpaint_triangles_single[i].batch); + } + } + MEM_SAFE_FREE(cache->texpaint_triangles_single); } @@ -2143,8 +2174,17 @@ static GPUVertBuf *mesh_batch_cache_get_tri_shading_data(MeshRenderData *rdata, return cache->shaded_triangles_data; } +static int get_tile_index_from_uv(const float *uv) +{ + int x = uv[0]; + int y = uv[1]; + CLAMP(x, 0, 9); + CLAMP(y, 0, 9); + return y*10 + x; +} + static GPUVertBuf *mesh_batch_cache_get_tri_uv_active( - MeshRenderData *rdata, MeshBatchCache *cache) + MeshRenderData *rdata, MeshBatchCache *cache, bool has_tiles, bool *is_tiled) { BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOP | MR_DATATYPE_LOOPUV)); @@ -2164,6 +2204,20 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active( } const int tri_len = mesh_render_data_looptri_len_get(rdata); + const int mat_len = mesh_render_data_mat_len_get(rdata); + + if (is_tiled) { + for (int i = 0; i < mat_len; i++) { + if (is_tiled[i]) { + has_tiles = true; + break; + } + } + } + + if (has_tiles) { + cache->tri_tiles = MEM_callocN(sizeof(int) * tri_len, "mesh cache tri tiles"); + } GPUVertBuf *vbo = cache->tri_aligned_uv = GPU_vertbuf_create_with_format(&format); @@ -2184,14 +2238,18 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active( if (BM_elem_flag_test(bm_looptri[0]->f, BM_ELEM_HIDDEN)) { continue; } + const float *elem = NULL; for (uint t = 0; t < 3; t++) { const BMLoop *loop = bm_looptri[t]; const int index = BM_elem_index_get(loop); if (index != -1) { - const float *elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv; + elem = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(loop, layer_offset))->uv; GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, elem); } } + if (elem && has_tiles) { + cache->tri_tiles[i] = get_tile_index_from_uv(elem); + } } } else { @@ -2201,6 +2259,9 @@ static GPUVertBuf *mesh_batch_cache_get_tri_uv_active( GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[0]].uv); GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[1]].uv); GPU_vertbuf_attr_set(vbo, attr_id.uv, vidx++, mloopuv[mlt->tri[2]].uv); + if (has_tiles) { + cache->tri_tiles[i] = get_tile_index_from_uv(mloopuv[mlt->tri[2]].uv); + } } } @@ -3564,22 +3625,47 @@ static GPUIndexBuf *mesh_batch_cache_get_loose_edges(MeshRenderData *rdata, Mesh return cache->ledges_in_order; } -static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material( - MeshRenderData *rdata, MeshBatchCache *cache) +static inline int get_batch_index_for_tri(bool single, bool texpaint, short mat, int mat_len, int *tri_tiles, int tri) +{ + if (single || (mat >= mat_len)) { + mat = 0; + } + if (texpaint) { + return 100*mat + tri_tiles[tri]; + } + return mat; +} + +static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split( + MeshRenderData *rdata, MeshBatchCache *cache, bool single, bool texpaint, int **batch_mapping, int *batch_num) { BLI_assert(rdata->types & (MR_DATATYPE_VERT | MR_DATATYPE_POLY)); - if (cache->shaded_triangles_in_order == NULL) { + GPUIndexBuf*** shaded_triangles; + if (texpaint) { + shaded_triangles = single? &cache->shaded_triangles_in_order_tiled_single : &cache->shaded_triangles_in_order_tiled; + } + else { + shaded_triangles = &cache->shaded_triangles_in_order; + } + + if (*shaded_triangles == NULL) { const int poly_len = mesh_render_data_polys_len_get(rdata); const int tri_len = mesh_render_data_looptri_len_get(rdata); const int mat_len = mesh_render_data_mat_len_get(rdata); - int *mat_tri_len = MEM_callocN(sizeof(*mat_tri_len) * mat_len, __func__); - cache->shaded_triangles_in_order = MEM_callocN(sizeof(*cache->shaded_triangles) * mat_len, __func__); - GPUIndexBufBuilder *elb = MEM_callocN(sizeof(*elb) * mat_len, __func__); + if (cache->tri_tiles == NULL) { + texpaint = false; + } + + /* Triangles are split according to material and/or tile index. + * To do this, we first need to find the combinations that are actually used. */ + int index_num = (single? 1 : mat_len) * (texpaint? 100 : 1); + int *batch_len = MEM_callocN(sizeof(int) * index_num, __func__); /* Note that polygons (not triangles) are used here. * This OK because result is _guaranteed_ to be the same. */ + uint tri = 0; if (rdata->edit_bmesh) { BMesh *bm = rdata->edit_bmesh->bm; BMIter fiter; @@ -3587,26 +3673,67 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material( BM_ITER_MESH(efa, &fiter, bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - const short ma_id = efa->mat_nr < mat_len ? efa->mat_nr : 0; - mat_tri_len[ma_id] += (efa->len - 2); + for(int j = 2; j < efa->len; j++, tri++) { + int index = get_batch_index_for_tri(single, texpaint, efa->mat_nr, mat_len, cache->tri_tiles, tri); + batch_len[index]++; + } } } } else { for (uint i = 0; i < poly_len; i++) { - const MPoly *mp = &rdata->mpoly[i]; ; - const short ma_id = mp->mat_nr < mat_len ? mp->mat_nr : 0; - mat_tri_len[ma_id] += (mp->totloop - 2); + const MPoly *mp = &rdata->mpoly[i]; + for(int j = 2; j < mp->totloop; j++, tri++) { + int index = get_batch_index_for_tri(single, texpaint, mp->mat_nr, mat_len, cache->tri_tiles, tri); + batch_len[index]++; + } } } + /* Now, all combinations that appear in the mesh get an assigned batch index. + * Also, we need to know each batches' length, so the batch_len array is replaced + * with each batches' length (instead of each combinations' length). + * This can be done in one pass since batch_num is guaranteed to be less or equal to i. + */ + int *index_to_batch = MEM_mallocN(sizeof(int) * index_num, __func__); + *batch_num = 0; + for (int i = 0; i < index_num; i++) { + if (batch_len[i] > 0) { + index_to_batch[i] = *batch_num; + batch_len[*batch_num] = batch_len[i]; + (*batch_num)++; + } + else { + index_to_batch[i] = -1; + } + } + + /* Return the mapping from batches to material/tile pairs to the caller. */ + int *mapping = *batch_mapping = MEM_mallocN(sizeof(int) * 2 * (*batch_num), __func__); + for (int i = 0; i < index_num; i++) { + if (index_to_batch[i] >= 0) { + int batch = index_to_batch[i]; + if (texpaint) { + mapping[2*batch + 0] = (i / 100); + mapping[2*batch + 1] = (i % 100); + } + else { + mapping[2*batch + 0] = i; + mapping[2*batch + 1] = 0; + } + } + } + + *shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * (*batch_num), __func__); + GPUIndexBufBuilder *elb = MEM_callocN(sizeof(*elb) * (*batch_num), __func__); + /* Init ELBs. */ - for (int i = 0; i < mat_len; ++i) { - GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, mat_tri_len[i], tri_len * 3); + for (int i = 0; i < *batch_num; ++i) { + GPU_indexbuf_init(&elb[i], GPU_PRIM_TRIS, batch_len[i], tri_len * 3); } /* Populate ELBs. */ - uint nidx = 0; + tri = 0; if (rdata->edit_bmesh) { BMesh *bm = rdata->edit_bmesh->bm; BMIter fiter; @@ -3614,10 +3741,9 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material( BM_ITER_MESH(efa, &fiter, bm, BM_FACES_OF_MESH) { if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { - const short ma_id = efa->mat_nr < mat_len ? efa->mat_nr : 0; - for (int j = 2; j < efa->len; j++) { - GPU_indexbuf_add_tri_verts(&elb[ma_id], nidx + 0, nidx + 1, nidx + 2); - nidx += 3; + for(int j = 2; j < efa->len; j++, tri++) { + int index = get_batch_index_for_tri(single, texpaint, efa->mat_nr, mat_len, cache->tri_tiles, tri); + GPU_indexbuf_add_tri_verts(&elb[index_to_batch[index]], 3*tri + 0, 3*tri + 1, 3*tri + 2); } } } @@ -3625,24 +3751,24 @@ static GPUIndexBuf **mesh_batch_cache_get_triangles_in_order_split_by_material( else { for (uint i = 0; i < poly_len; i++) { const MPoly *mp = &rdata->mpoly[i]; ; - const short ma_id = mp->mat_nr < mat_len ? mp->mat_nr : 0; - for (int j = 2; j < mp->totloop; j++) { - GPU_indexbuf_add_tri_verts(&elb[ma_id], nidx + 0, nidx + 1, nidx + 2); - nidx += 3; + for(int j = 2; j < mp->totloop; j++, tri++) { + int index = get_batch_index_for_tri(single, texpaint, mp->mat_nr, mat_len, cache->tri_tiles, tri); + GPU_indexbuf_add_tri_verts(&elb[index_to_batch[index]], 3*tri + 0, 3*tri + 1, 3*tri + 2); } } } /* Build ELBs. */ - for (int i = 0; i < mat_len; ++i) { - cache->shaded_triangles_in_order[i] = GPU_indexbuf_build(&elb[i]); + for (int i = 0; i < *batch_num; ++i) { + (*shaded_triangles)[i] = GPU_indexbuf_build(&elb[i]); } - MEM_freeN(mat_tri_len); + MEM_freeN(batch_len); + MEM_freeN(index_to_batch); MEM_freeN(elb); } - return cache->shaded_triangles_in_order; + return *shaded_triangles; } static GPUVertBuf *mesh_create_edge_pos_with_sel( @@ -4311,19 +4437,23 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded( cache->shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * mat_len, __func__); - GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split_by_material(rdata, cache); + int *batch_mapping; + int batch_num; + GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, false, false, &batch_mapping, &batch_num); GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache); GPUVertBuf *vbo_shading = mesh_batch_cache_get_tri_shading_data(rdata, cache); - for (int i = 0; i < mat_len; ++i) { - cache->shaded_triangles[i] = GPU_batch_create( + for (int i = 0; i < batch_num; ++i) { + int mat = batch_mapping[2*i + 0]; + cache->shaded_triangles[mat] = GPU_batch_create( GPU_PRIM_TRIS, vbo, el[i]); if (vbo_shading) { - GPU_batch_vertbuf_add(cache->shaded_triangles[i], vbo_shading); + GPU_batch_vertbuf_add(cache->shaded_triangles[mat], vbo_shading); } } + MEM_SAFE_FREE(batch_mapping); mesh_render_data_free(rdata); } @@ -4336,7 +4466,7 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded( return cache->shaded_triangles; } -GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me) +TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me, bool *is_tiled, int *num_batches) { MeshBatchCache *cache = mesh_batch_cache_get(me); @@ -4346,28 +4476,33 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_texpaint(Mesh *me) MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOPUV; MeshRenderData *rdata = mesh_render_data_create(me, datatype); - const int mat_len = mesh_render_data_mat_len_get(rdata); + GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache, false, is_tiled); + GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache); - cache->texpaint_triangles = MEM_callocN(sizeof(*cache->texpaint_triangles) * mat_len, __func__); + int *batch_mapping; + GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, false, true, &batch_mapping, &cache->num_texpaint_batches); - GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split_by_material(rdata, cache); + cache->texpaint_triangles = MEM_callocN(sizeof(*cache->texpaint_triangles) * cache->num_texpaint_batches, __func__); - GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache); - for (int i = 0; i < mat_len; ++i) { - cache->texpaint_triangles[i] = GPU_batch_create( + for (int i = 0; i < cache->num_texpaint_batches; ++i) { + cache->texpaint_triangles[i].batch = GPU_batch_create( GPU_PRIM_TRIS, vbo, el[i]); - GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache); if (vbo_uv) { - GPU_batch_vertbuf_add(cache->texpaint_triangles[i], vbo_uv); + GPU_batch_vertbuf_add(cache->texpaint_triangles[i].batch, vbo_uv); } + cache->texpaint_triangles[i].material = batch_mapping[2*i + 0]; + cache->texpaint_triangles[i].tile = batch_mapping[2*i + 1]; } + + MEM_SAFE_FREE(batch_mapping); mesh_render_data_free(rdata); } + *num_batches = cache->num_texpaint_batches; return cache->texpaint_triangles; } -GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me) +TexpaintCacheBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me, bool is_tiled, int *num_batches) { MeshBatchCache *cache = mesh_batch_cache_get(me); @@ -4377,16 +4512,43 @@ GPUBatch *DRW_mesh_batch_cache_get_surface_texpaint_single(Mesh *me) MR_DATATYPE_VERT | MR_DATATYPE_LOOP | MR_DATATYPE_POLY | MR_DATATYPE_LOOPTRI | MR_DATATYPE_LOOPUV; MeshRenderData *rdata = mesh_render_data_create(me, datatype); + GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache, is_tiled, NULL); GPUVertBuf *vbo = mesh_batch_cache_get_tri_pos_and_normals(rdata, cache); - cache->texpaint_triangles_single = GPU_batch_create( - GPU_PRIM_TRIS, vbo, NULL); - GPUVertBuf *vbo_uv = mesh_batch_cache_get_tri_uv_active(rdata, cache); - if (vbo_uv) { - GPU_batch_vertbuf_add(cache->texpaint_triangles_single, vbo_uv); + if (cache->tri_tiles) { + int *batch_mapping; + GPUIndexBuf **el = mesh_batch_cache_get_triangles_in_order_split(rdata, cache, true, true, &batch_mapping, &cache->num_texpaint_batches_single); + + cache->texpaint_triangles_single = MEM_callocN(sizeof(*cache->texpaint_triangles) * cache->num_texpaint_batches_single, __func__); + + for (int i = 0; i < cache->num_texpaint_batches_single; ++i) { + cache->texpaint_triangles[i].batch = GPU_batch_create( + GPU_PRIM_TRIS, vbo, el[i]); + if (vbo_uv) { + GPU_batch_vertbuf_add(cache->texpaint_triangles[i].batch, vbo_uv); + } + cache->texpaint_triangles[i].material = batch_mapping[2*i + 0]; + cache->texpaint_triangles[i].tile = batch_mapping[2*i + 1]; + } + + MEM_SAFE_FREE(batch_mapping); } + else { + cache->texpaint_triangles_single = MEM_callocN(sizeof(*cache->texpaint_triangles), __func__); + cache->texpaint_triangles_single[0].batch = GPU_batch_create( + GPU_PRIM_TRIS, vbo, NULL); + if (vbo_uv) { + GPU_batch_vertbuf_add(cache->texpaint_triangles_single[0].batch, vbo_uv); + } + cache->texpaint_triangles_single[0].material = 0; + cache->texpaint_triangles_single[0].tile = 0; + cache->num_texpaint_batches = 1; + } + mesh_render_data_free(rdata); } + + *num_batches = cache->num_texpaint_batches; return cache->texpaint_triangles_single; } diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index dccb869c133..bdc4504e278 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -766,9 +766,18 @@ static DRWShadingGroup *drw_shgroup_material_inputs(DRWShadingGroup *grp, struct for (GPUInput *input = inputs->first; input; input = input->next) { /* Textures */ if (input->ima) { + ImageUser *tex_iuser = input->iuser; + + /* If there's no specified iuser but we need a different tile, create a temporary one. */ + ImageUser iuser = {NULL}; + iuser.ok = true; + iuser.tile = input->image_tile; + if (!tex_iuser && iuser.tile != 0) + tex_iuser = &iuser; + double time = 0.0; /* TODO make time variable */ GPUTexture *tex = GPU_texture_from_blender( - input->ima, input->iuser, input->textarget, input->image_isdata, time); + input->ima, tex_iuser, input->textarget, input->image_isdata, time); if (input->bindtex) { DRW_shgroup_uniform_texture(grp, input->shadername, tex); diff --git a/source/blender/draw/modes/paint_texture_mode.c b/source/blender/draw/modes/paint_texture_mode.c index 8256bb4d0d7..1f1bf606d14 100644 --- a/source/blender/draw/modes/paint_texture_mode.c +++ b/source/blender/draw/modes/paint_texture_mode.c @@ -26,6 +26,8 @@ #include "DRW_engine.h" #include "DRW_render.h" +#include "BKE_image.h" + #include "BIF_gl.h" /* If builtin shaders are needed */ @@ -118,7 +120,6 @@ typedef struct PAINT_TEXTURE_PrivateData { /* This keeps the references of the shading groups for * easy access in PAINT_TEXTURE_cache_populate() */ DRWShadingGroup *shgroup_fallback; - DRWShadingGroup **shgroup_image_array; /* face-mask */ DRWShadingGroup *lwire_shgrp; @@ -189,7 +190,6 @@ static void PAINT_TEXTURE_cache_init(void *vedata) if (!stl->g_data) { /* Alloc transient pointers */ stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); - stl->g_data->shgroup_image_array = NULL; } { @@ -203,53 +203,6 @@ static void PAINT_TEXTURE_cache_init(void *vedata) * any given time (i.e. use static vars) */ static float color[4] = {1.0f, 0.0f, 1.0f, 1.0}; DRW_shgroup_uniform_vec4(stl->g_data->shgroup_fallback, "color", color, 1); - - MEM_SAFE_FREE(stl->g_data->shgroup_image_array); - - const DRWContextState *draw_ctx = DRW_context_state_get(); - Object *ob = draw_ctx->obact; - if (ob && ob->type == OB_MESH) { - Scene *scene = draw_ctx->scene; - const bool use_material_slots = (scene->toolsettings->imapaint.mode == IMAGEPAINT_MODE_MATERIAL); - const Mesh *me = ob->data; - - stl->g_data->shgroup_image_array = MEM_mallocN( - sizeof(*stl->g_data->shgroup_image_array) * (use_material_slots ? me->totcol : 1), __func__); - - if (use_material_slots) { - for (int i = 0; i < me->totcol; i++) { - Material *ma = give_current_material(ob, i + 1); - Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; - GPUTexture *tex = ima ? - GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false, 0.0f) : NULL; - - if (tex) { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces); - DRW_shgroup_uniform_texture(grp, "image", tex); - DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1); - stl->g_data->shgroup_image_array[i] = grp; - } - else { - stl->g_data->shgroup_image_array[i] = NULL; - } - } - } - else { - Image *ima = scene->toolsettings->imapaint.canvas; - GPUTexture *tex = ima ? - GPU_texture_from_blender(ima, NULL, GL_TEXTURE_2D, false, 0.0f) : NULL; - - if (tex) { - DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces); - DRW_shgroup_uniform_texture(grp, "image", tex); - DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1); - stl->g_data->shgroup_image_array[0] = grp; - } - else { - stl->g_data->shgroup_image_array[0] = NULL; - } - } - } } /* Face Mask */ @@ -293,29 +246,62 @@ static void PAINT_TEXTURE_cache_populate(void *vedata, Object *ob) if (use_surface) { if (me->mloopuv != NULL) { if (use_material_slots) { - struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL; - if ((me->totcol == 0) || (geom_array == NULL)) { - struct GPUBatch *geom = DRW_cache_mesh_surface_get(ob); - DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom, ob->obmat); - ok = true; - } - else { - for (int i = 0; i < me->totcol; i++) { - if (stl->g_data->shgroup_image_array[i]) { - DRW_shgroup_call_add(stl->g_data->shgroup_image_array[i], geom_array[i], ob->obmat); + //struct GPUBatch **geom_array = me->totcol ? DRW_cache_mesh_surface_texpaint_get(ob) : NULL; + if (me->totcol) { + bool *is_tiled = MEM_mallocN(sizeof(bool)*me->totcol, "paint texture is tiled"); + for(int i = 0; i < me->totcol; i++) { + Material *ma = give_current_material(ob, i + 1); + Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + is_tiled[i] = ima && (ima->source == IMA_SRC_TILED); + } + + int num_batches = 0; + struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_get(ob, is_tiled, &num_batches); + + for (int i = 0; i < num_batches; i++) { + ImageUser iuser = {NULL}; + iuser.ok = 1; + iuser.tile = geom_batches[i].tile; + + Material *ma = give_current_material(ob, geom_batches[i].material + 1); + Image *ima = (ma && ma->texpaintslot) ? ma->texpaintslot[ma->paint_active_slot].ima : NULL; + GPUTexture *tex = ima ? + GPU_texture_from_blender(ima, &iuser, GL_TEXTURE_2D, false, 0.0f) : NULL; + + if (tex) { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces); + DRW_shgroup_uniform_texture(grp, "image", tex); + DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1); + DRW_shgroup_call_add(grp, geom_batches[i].batch, ob->obmat); } else { - DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom_array[i], ob->obmat); + DRW_shgroup_call_add(stl->g_data->shgroup_fallback, geom_batches[i].batch, ob->obmat); } ok = true; } } } else { - struct GPUBatch *geom = DRW_cache_mesh_surface_texpaint_single_get(ob); - if (geom && stl->g_data->shgroup_image_array[0]) { - DRW_shgroup_call_add(stl->g_data->shgroup_image_array[0], geom, ob->obmat); - ok = true; + Image *ima = scene->toolsettings->imapaint.canvas; + + if (ima) { + bool is_tiled = (ima->source == IMA_SRC_TILED); + int num_batches = 0; + struct TexpaintCacheBatch *geom_batches = DRW_cache_mesh_surface_texpaint_single_get(ob, is_tiled, &num_batches); + + for (int i = 0; i < num_batches; i++) { + ImageUser iuser = {NULL}; + iuser.ok = 1; + iuser.tile = geom_batches[i].tile; + GPUTexture *tex = GPU_texture_from_blender(ima, &iuser, GL_TEXTURE_2D, false, 0.0f); + if (tex) { + DRWShadingGroup *grp = DRW_shgroup_create(e_data.image_sh, psl->image_faces); + DRW_shgroup_uniform_texture(grp, "image", tex); + DRW_shgroup_uniform_float(grp, "alpha", &draw_ctx->v3d->overlay.texture_paint_mode_opacity, 1); + DRW_shgroup_call_add(grp, geom_batches[i].batch, ob->obmat); + ok = true; + } + } } } } @@ -345,12 +331,9 @@ static void PAINT_TEXTURE_cache_populate(void *vedata, Object *ob) static void PAINT_TEXTURE_cache_finish(void *vedata) { PAINT_TEXTURE_PassList *psl = ((PAINT_TEXTURE_Data *)vedata)->psl; - PAINT_TEXTURE_StorageList *stl = ((PAINT_TEXTURE_Data *)vedata)->stl; /* Do something here! dependant on the objects gathered */ UNUSED_VARS(psl); - - MEM_SAFE_FREE(stl->g_data->shgroup_image_array); } /* Draw time ! Control rendering pipeline from here */ diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index f8af67b55ff..74da8f73729 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -48,7 +48,7 @@ struct Mask *ED_space_image_get_mask(struct SpaceImage *sima); void ED_space_image_set_mask(struct bContext *C, struct SpaceImage *sima, struct Mask *mask); bool ED_space_image_color_sample(struct SpaceImage *sima, struct ARegion *ar, int mval[2], float r_col[3]); -struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock); +struct ImBuf *ED_space_image_acquire_buffer(struct SpaceImage *sima, void **r_lock, int tile); void ED_space_image_release_buffer(struct SpaceImage *sima, struct ImBuf *ibuf, void *lock); bool ED_space_image_has_buffer(struct SpaceImage *sima); diff --git a/source/blender/editors/include/ED_paint.h b/source/blender/editors/include/ED_paint.h index 246419d64aa..b35236cadd0 100644 --- a/source/blender/editors/include/ED_paint.h +++ b/source/blender/editors/include/ED_paint.h @@ -41,7 +41,7 @@ void ED_keymap_paint(struct wmKeyConfig *keyconf); /* paint_image.c */ void ED_imapaint_clear_partial_redraw(void); void ED_imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h, bool find_old); -void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op); +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], struct wmOperator *op, const int mouse[2]); /* paint_image_undo.c */ void ED_image_undo_push_begin(const char *name); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 914641c1add..6b27ef845b9 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -106,7 +106,7 @@ void ED_region_visibility_change_update(struct bContext *C, struct ARegion *a void ED_region_info_draw(struct ARegion *ar, const char *text, float fill_color[4], const bool full_redraw); void ED_region_info_draw_multiline(ARegion *ar, const char *text_array[], float fill_color[4], const bool full_redraw); void ED_region_image_metadata_draw(int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); -void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy); +void ED_region_grid_draw(struct ARegion *ar, float zoomx, float zoomy, float x0, float y0); float ED_region_blend_alpha(struct ARegion *ar); void ED_region_visible_rect(struct ARegion *ar, struct rcti *rect); bool ED_region_is_overlap(int spacetype, int regiontype); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index ec10fc9d494..caae71c9df7 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -1272,7 +1272,7 @@ void UI_drop_color_copy(wmDrag *drag, wmDropBox *drop) RNA_boolean_set(drop->ptr, "gamma", drag_info->gamma_corrected); } -static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); uiBut *but = NULL; @@ -1313,7 +1313,7 @@ static int drop_color_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( srgb_to_linearrgb_v3_v3(color, color); } - ED_imapaint_bucket_fill(C, color, op); + ED_imapaint_bucket_fill(C, color, op, event->mval); } ED_region_tag_redraw(ar); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index e8fabdabf17..1d93d664a5a 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -275,9 +275,12 @@ static void refresh_images(BakeImages *bake_images) int i; for (i = 0; i < bake_images->size; i++) { Image *ima = bake_images->data[i].image; - if (ima->ok == IMA_OK_LOADED) { - GPU_free_image(ima); - DEG_id_tag_update(&ima->id, 0); + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->ok == IMA_OK_LOADED) { + GPU_free_image(ima); + DEG_id_tag_update(&ima->id, 0); + break; + } } } } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 3423eedf7ca..8adb77beac3 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -982,8 +982,12 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat ImBuf *ibuf = NULL; ImageUser iuser = {NULL}; + if (ima == NULL) + return; + + ImageTile *tile = BKE_image_get_tile(ima, 0); /* ima->ok is zero when Image cannot load */ - if (ima == NULL || ima->ok == 0) + if (tile->ok == 0) return; /* setup dummy image user */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 1a63bc1cd53..ff12ed90621 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -2735,15 +2735,15 @@ void ED_region_image_metadata_draw(int x, int y, ImBuf *ibuf, const rctf *frame, GPU_matrix_pop(); } -void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy) +void ED_region_grid_draw(ARegion *ar, float zoomx, float zoomy, float x0, float y0) { float gridsize, gridstep = 1.0f / 32.0f; float fac, blendfac; int x1, y1, x2, y2; - /* the image is located inside (0, 0), (1, 1) as set by view2d */ - UI_view2d_view_to_region(&ar->v2d, 0.0f, 0.0f, &x1, &y1); - UI_view2d_view_to_region(&ar->v2d, 1.0f, 1.0f, &x2, &y2); + /* the image is located inside (x0, y0), (x0+1, y0+1) as set by view2d */ + UI_view2d_view_to_region(&ar->v2d, x0, y0, &x1, &y1); + UI_view2d_view_to_region(&ar->v2d, x0+1.0f, y0+1.0f, &x2, &y2); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 897a74eb497..df8d41d2568 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -163,7 +163,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int IMB_freeImBuf(tmpibuf); } -void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint) +void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, ImageUser *iuser, short texpaint) { if (imapaintpartial.x1 != imapaintpartial.x2 && imapaintpartial.y1 != imapaintpartial.y2) @@ -182,7 +182,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int h = imapaintpartial.y2 - imapaintpartial.y1; if (w && h) { /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); + GPU_paint_update_image(image, iuser, imapaintpartial.x1, imapaintpartial.y1, w, h); } } } @@ -587,7 +587,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) float color[3]; srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); - paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); } else { paint_proj_stroke( @@ -1224,7 +1224,7 @@ void PAINT_OT_brush_colors_flip(wmOperatorType *ot) } -void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op, const int mouse[2]) { wmWindowManager *wm = CTX_wm_manager(C); SpaceImage *sima = CTX_wm_space_image(C); @@ -1234,7 +1234,8 @@ void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) ED_image_undo_push_begin(op->type->name); - paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + float mouse_init[2] = {mouse[0], mouse[1]}; + paint_2d_bucket_fill(C, color, NULL, mouse_init, NULL, NULL); BKE_undosys_step_push(wm->undo_stack, C, op->type->name); diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index da08766b322..62daf39e278 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -43,6 +43,7 @@ #include "BLI_stack.h" #include "BLI_bitmap.h" #include "BLI_task.h" +#include "BLI_listbase.h" #include "BKE_colorband.h" #include "BKE_context.h" @@ -82,7 +83,7 @@ typedef struct BrushPainterCache { bool is_texbrush; bool is_maskbrush; - int lastdiameter; + int lastdiameter[2]; float last_tex_rotation; float last_mask_rotation; float last_pressure; @@ -94,22 +95,21 @@ typedef struct BrushPainterCache { unsigned short *tex_mask_old; unsigned int tex_mask_old_w; unsigned int tex_mask_old_h; + + int image_size[2]; } BrushPainterCache; typedef struct BrushPainter { Scene *scene; Brush *brush; - float lastpaintpos[2]; /* position of last paint op */ - float startpaintpos[2]; /* position of first paint */ - short firsttouch; /* first paint op */ struct ImagePool *pool; /* image pool */ rctf tex_mapping; /* texture coordinate mapping */ rctf mask_mapping; /* mask texture coordinate mapping */ - BrushPainterCache cache; + bool cache_invert; } BrushPainter; typedef struct ImagePaintRegion { @@ -118,6 +118,28 @@ typedef struct ImagePaintRegion { int width, height; } ImagePaintRegion; +typedef enum ImagePaintTileState { + PAINT2D_TILE_EMPTY = 0, + PAINT2D_TILE_FAILED, + PAINT2D_TILE_READY, +} ImagePaintTileState; + +typedef struct ImagePaintTile { + ImageUser iuser; + ImBuf *canvas; + float radius_fac[2]; + int size[2]; + float uv_ofs[2]; + bool need_redraw; + BrushPainterCache cache; + int tile_number; + + ImagePaintTileState state; + + float last_paintpos[2]; /* position of last paint op */ + float start_paintpos[2]; /* position of first paint */ +} ImagePaintTile; + typedef struct ImagePaintState { BrushPainter *painter; SpaceImage *sima; @@ -129,10 +151,7 @@ typedef struct ImagePaintState { Brush *brush; short tool, blend; Image *image; - ImBuf *canvas; ImBuf *clonecanvas; - const char *warnpackedfile; - const char *warnmultifile; bool do_masking; @@ -143,7 +162,8 @@ typedef struct ImagePaintState { int do_facesel; int symmetry; - bool need_redraw; + ImagePaintTile *tiles; + int num_tiles; BlurKernel *blurkernel; } ImagePaintState; @@ -156,42 +176,39 @@ static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool inver painter->brush = brush; painter->scene = scene; painter->firsttouch = 1; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - painter->cache.invert = invert; + painter->cache_invert = invert; return painter; } -static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction) +static void brush_painter_2d_require_imbuf(Brush *brush, ImagePaintTile *tile, bool use_float, bool use_color_correction) { - Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; + if ((cache->use_float != use_float)) { + if (cache->ibuf) IMB_freeImBuf(cache->ibuf); + if (cache->curve_mask) MEM_freeN(cache->curve_mask); + if (cache->tex_mask) MEM_freeN(cache->tex_mask); + if (cache->tex_mask_old) MEM_freeN(cache->tex_mask_old); + cache->ibuf = NULL; + cache->curve_mask = NULL; + cache->tex_mask = NULL; + cache->lastdiameter[0] = -1; /* force ibuf create in refresh */ + } - if ((painter->cache.use_float != use_float)) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - painter->cache.ibuf = NULL; - painter->cache.curve_mask = NULL; - painter->cache.tex_mask = NULL; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - } - - painter->cache.use_float = use_float; - painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; + cache->use_float = use_float; + cache->use_color_correction = use_float && use_color_correction; + cache->is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; + cache->is_maskbrush = (brush->mask_mtex.tex) ? true : false; } -static void brush_painter_2d_free(BrushPainter *painter) +static void brush_painter_cache_2d_free(BrushPainterCache *cache) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - MEM_freeN(painter); + if (cache->ibuf) IMB_freeImBuf(cache->ibuf); + if (cache->texibuf) IMB_freeImBuf(cache->texibuf); + if (cache->curve_mask) MEM_freeN(cache->curve_mask); + if (cache->tex_mask) MEM_freeN(cache->tex_mask); + if (cache->tex_mask_old) MEM_freeN(cache->tex_mask_old); } static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) @@ -202,7 +219,7 @@ static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) } /* create a mask with the mask texture */ -static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) +static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size[2]) { Scene *scene = painter->scene; Brush *brush = painter->brush; @@ -213,11 +230,11 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si unsigned short *mask, *m; int x, y, thread = 0; - mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); + mask = MEM_mallocN(sizeof(unsigned short) * size[0] * size[1], "brush_painter_mask"); m = mask; - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++, m++) { + for (y = 0; y < size[1]; y++) { + for (x = 0; x < size[0]; x++, m++) { float res; brush_imbuf_tex_co(&mask_mapping, x, y, texco); res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); @@ -230,11 +247,12 @@ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int si /* update rectangular section of the brush image */ static void brush_painter_mask_imbuf_update( - BrushPainter *painter, unsigned short *tex_mask_old, - int origx, int origy, int w, int h, int xt, int yt, int diameter) + BrushPainter *painter, ImagePaintTile *tile, unsigned short *tex_mask_old, + int origx, int origy, int w, int h, int xt, int yt, const int diameter[2]) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; rctf tex_mapping = painter->mask_mapping; struct ImagePool *pool = painter->pool; unsigned short res; @@ -243,8 +261,8 @@ static void brush_painter_mask_imbuf_update( int x, y, thread = 0; - unsigned short *tex_mask = painter->cache.tex_mask; - unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + unsigned short *tex_mask = cache->tex_mask; + unsigned short *tex_mask_cur = cache->tex_mask_old; /* fill pixels */ for (y = origy; y < h; y++) { @@ -253,8 +271,8 @@ static void brush_painter_mask_imbuf_update( float texco[3]; /* handle byte pixel */ - unsigned short *b = tex_mask + (y * diameter + x); - unsigned short *t = tex_mask_cur + (y * diameter + x); + unsigned short *b = tex_mask + (y * diameter[0] + x); + unsigned short *t = tex_mask_cur + (y * diameter[0] + x); if (!use_texture_old) { brush_imbuf_tex_co(&tex_mapping, x, y, texco); @@ -263,7 +281,7 @@ static void brush_painter_mask_imbuf_update( /* read from old texture buffer */ if (use_texture_old) { - res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + res = *(tex_mask_old + ((y - origy + yt) * cache->tex_mask_old_w + (x - origx + xt))); } /* write to new texture mask */ @@ -280,32 +298,33 @@ static void brush_painter_mask_imbuf_update( * This can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter[2]) { - BrushPainterCache *cache = &painter->cache; + BrushPainterCache *cache = &tile->cache; unsigned short *tex_mask_old; int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; /* create brush image buffer if it didn't exist yet */ if (!cache->tex_mask) - cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask"); /* create new texture image buffer with coordinates relative to old */ tex_mask_old = cache->tex_mask_old; - cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask"); if (tex_mask_old) { ImBuf maskibuf; ImBuf maskibuf_old; - maskibuf.x = maskibuf.y = diameter; + maskibuf.x = diameter[0]; + maskibuf.y = diameter[1]; maskibuf_old.x = cache->tex_mask_old_w; maskibuf_old.y = cache->tex_mask_old_h; srcx = srcy = 0; w = cache->tex_mask_old_w; h = cache->tex_mask_old_h; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter[0] / 2 - w / 2); + desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter[1] / 2 - h / 2); /* hack, use temporary rects so that clipping works */ IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); @@ -316,53 +335,55 @@ static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const w = h = 0; } - x1 = min_ii(destx, diameter); - y1 = min_ii(desty, diameter); - x2 = min_ii(destx + w, diameter); - y2 = min_ii(desty + h, diameter); + x1 = min_ii(destx, diameter[0]); + y1 = min_ii(desty, diameter[1]); + x2 = min_ii(destx + w, diameter[0]); + y2 = min_ii(desty + h, diameter[1]); /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) - brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + brush_painter_mask_imbuf_update(painter, tile, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); if (tex_mask_old) MEM_freeN(tex_mask_old); /* sample texture in new areas */ - if ((0 < x1) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); - if ((x2 < diameter) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + if ((0 < x1) && (0 < diameter[1])) + brush_painter_mask_imbuf_update(painter, tile, NULL, 0, 0, x1, diameter[1], 0, 0, diameter); + if ((x2 < diameter[0]) && (0 < diameter[1])) + brush_painter_mask_imbuf_update(painter, tile, NULL, x2, 0, diameter[0], diameter[1], 0, 0, diameter); if ((x1 < x2) && (0 < y1)) - brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); - if ((x1 < x2) && (y2 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + brush_painter_mask_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0, diameter); + if ((x1 < x2) && (y2 < diameter[1])) + brush_painter_mask_imbuf_update(painter, tile, NULL, x1, y2, x2, diameter[1], 0, 0, diameter); /* through with sampling, now update sizes */ - cache->tex_mask_old_w = diameter; - cache->tex_mask_old_h = diameter; + cache->tex_mask_old_w = diameter[0]; + cache->tex_mask_old_h = diameter[1]; } /* create a mask with the falloff strength */ -static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius) +static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, const int diameter[2], const float radius[2]) { Brush *brush = painter->brush; - int xoff = -radius; - int yoff = -radius; unsigned short *mask, *m; int x, y; - mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + mask = MEM_mallocN(sizeof(unsigned short) * diameter[0] * diameter[1], "brush_painter_mask"); m = mask; - for (y = 0; y < diameter; y++) { - for (x = 0; x < diameter; x++, m++) { - float xy[2] = {x + xoff, y + yoff}; + float max_radius = max_ff(radius[0], radius[1]); + float xfac = max_radius/radius[0]; + float yfac = max_radius/radius[1]; + + for (y = 0; y < diameter[1]; y++) { + for (x = 0; x < diameter[0]; x++, m++) { + float xy[2] = {x*xfac - max_radius, y*yfac - max_radius}; float len = len_v2(xy); - *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, max_radius)); } } @@ -371,10 +392,11 @@ static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int d /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance) +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, ImagePaintTile *tile, const int size[2], float pressure, float distance) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); @@ -382,19 +404,19 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre rctf tex_mapping = painter->tex_mapping; struct ImagePool *pool = painter->pool; - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; + bool use_color_correction = cache->use_color_correction; + bool use_float = cache->use_float; + bool is_texbrush = cache->is_texbrush; int x, y, thread = 0; float brush_rgb[3]; /* allocate image buffer */ - ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); + ImBuf *ibuf = IMB_allocImBuf(size[0], size[1], 32, (use_float) ? IB_rectfloat : IB_rect); /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display); + paint_brush_color_get(scene, brush, use_color_correction, cache->invert, distance, pressure, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -403,8 +425,8 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre } /* fill image buffer */ - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++) { + for (y = 0; y < size[1]; y++) { + for (x = 0; x < size[0]; x++) { /* sample texture and multiply with brush color */ float texco[3], rgba[4]; @@ -424,13 +446,13 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre if (use_float) { /* write to float pixel */ - float *dstf = ibuf->rect_float + (y * size + x) * 4; + float *dstf = ibuf->rect_float + (y * size[0] + x) * 4; mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ dstf[3] = rgba[3]; } else { /* write to byte pixel */ - unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; + unsigned char *dst = (unsigned char *)ibuf->rect + (y * size[0] + x) * 4; rgb_float_to_uchar(dst, rgba); dst[3] = unit_float_to_uchar_clamp(rgba[3]); @@ -443,11 +465,12 @@ static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pre /* update rectangular section of the brush image */ static void brush_painter_imbuf_update( - BrushPainter *painter, ImBuf *oldtexibuf, + BrushPainter *painter, ImagePaintTile *tile, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt) { Scene *scene = painter->scene; Brush *brush = painter->brush; + BrushPainterCache *cache = &tile->cache; const char *display_device = scene->display_settings.display_device; struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); @@ -455,20 +478,20 @@ static void brush_painter_imbuf_update( rctf tex_mapping = painter->tex_mapping; struct ImagePool *pool = painter->pool; - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; + bool use_color_correction = cache->use_color_correction; + bool use_float = cache->use_float; + bool is_texbrush = cache->is_texbrush; bool use_texture_old = (oldtexibuf != NULL); int x, y, thread = 0; float brush_rgb[3]; - ImBuf *ibuf = painter->cache.ibuf; - ImBuf *texibuf = painter->cache.texibuf; + ImBuf *ibuf = cache->ibuf; + ImBuf *texibuf = cache->texibuf; /* get brush color */ if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); + paint_brush_color_get(scene, brush, use_color_correction, cache->invert, 0.0, 1.0, brush_rgb, display); } else { brush_rgb[0] = 1.0f; @@ -553,28 +576,28 @@ static void brush_painter_imbuf_update( /* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_imbuf_partial_update(BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const int diameter[2]) { - BrushPainterCache *cache = &painter->cache; + BrushPainterCache *cache = &tile->cache; ImBuf *oldtexibuf, *ibuf; int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; /* create brush image buffer if it didn't exist yet */ imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; if (!cache->ibuf) - cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + cache->ibuf = IMB_allocImBuf(diameter[0], diameter[1], 32, imbflag); ibuf = cache->ibuf; /* create new texture image buffer with coordinates relative to old */ oldtexibuf = cache->texibuf; - cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + cache->texibuf = IMB_allocImBuf(diameter[0], diameter[1], 32, imbflag); if (oldtexibuf) { srcx = srcy = 0; w = oldtexibuf->x; h = oldtexibuf->y; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + destx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]) + (diameter[0] / 2 - w / 2); + desty = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]) + (diameter[1] / 2 - h / 2); IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); } @@ -591,43 +614,43 @@ static void brush_painter_imbuf_partial_update(BrushPainter *painter, const floa /* blend existing texture in new position */ if ((x1 < x2) && (y1 < y2)) - brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); + brush_painter_imbuf_update(painter, tile, oldtexibuf, x1, y1, x2, y2, srcx, srcy); if (oldtexibuf) IMB_freeImBuf(oldtexibuf); /* sample texture in new areas */ if ((0 < x1) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, 0, 0, x1, ibuf->y, 0, 0); if ((x2 < ibuf->x) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); if ((x1 < x2) && (0 < y1)) - brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x1, 0, x2, y1, 0, 0); if ((x1 < x2) && (y2 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); + brush_painter_imbuf_update(painter, tile, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, ImBuf *canvas, const int diameter[2], const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) { - float invw = 1.0f / (float)s->canvas->x; - float invh = 1.0f / (float)s->canvas->y; + float invw = 1.0f / (float)canvas->x; + float invh = 1.0f / (float)canvas->y; int xmin, ymin, xmax, ymax; int ipos[2]; /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); + ipos[0] = (int)floorf((pos[0] - diameter[0] / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - diameter[1] / 2) + 1.0f); if (mapmode == MTEX_MAP_MODE_STENCIL) { /* map from view coordinates of brush to region coordinates */ UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); + UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter[0]) * invw, (ipos[1] + diameter[1]) * invh, &xmax, &ymax); /* output mapping from brush ibuf x/y to region coordinates */ mapping->xmin = xmin; mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)diameter; - mapping->ymax = (ymax - ymin) / (float)diameter; + mapping->xmax = (xmax - xmin) / (float)diameter[0]; + mapping->ymax = (ymax - ymin) / (float)diameter[1]; } else if (mapmode == MTEX_MAP_MODE_3D) { /* 3D mapping, just mapping to canvas 0..1 */ @@ -638,26 +661,26 @@ static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const } else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { /* view mapping */ - mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; - mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; + mapping->xmin = mouse[0] - diameter[0] * 0.5f + 0.5f; + mapping->ymin = mouse[1] - diameter[1] * 0.5f + 0.5f; mapping->xmax = 1.0f; mapping->ymax = 1.0f; } else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); - mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); + mapping->xmin = (int)(-diameter[0] * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); + mapping->ymin = (int)(-diameter[1] * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); mapping->xmax = 1.0f; mapping->ymax = 1.0f; } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, ImagePaintTile *tile, const float pos[2], const float mouse[2], float pressure, float distance, const float size[2]) { const Scene *scene = painter->scene; UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; Brush *brush = painter->brush; - BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * size; + const int diameter[2] = {2 * size[0], 2 * size[1]}; + BrushPainterCache *cache = &tile->cache; bool do_random = false; bool do_partial_update = false; @@ -673,7 +696,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai painter->pool = BKE_image_pool_new(); /* determine how can update based on textures used */ - if (painter->cache.is_texbrush) { + if (cache->is_texbrush) { if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { tex_rotation += ups->brush_rotation; } @@ -683,11 +706,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai do_partial_update = true; brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, + s, tile->canvas, diameter, tile->start_paintpos, pos, mouse, brush->mtex.brush_map_mode, &painter->tex_mapping); } - if (painter->cache.is_maskbrush) { + if (cache->is_maskbrush) { bool renew_maxmask = false; bool do_partial_update_mask = false; /* invalidate case for all mapping modes */ @@ -707,7 +730,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai renew_maxmask = true; } - if ((diameter != cache->lastdiameter) || + if (!equals_v2v2_int(diameter, cache->lastdiameter) || (mask_rotation != cache->last_mask_rotation) || renew_maxmask) { @@ -717,11 +740,11 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai } brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, + s, tile->canvas, diameter, tile->start_paintpos, pos, mouse, brush->mask_mtex.brush_map_mode, &painter->mask_mapping); if (do_partial_update_mask) - brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + brush_painter_mask_imbuf_partial_update(painter, tile, pos, diameter); else cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); cache->last_mask_rotation = mask_rotation; @@ -729,7 +752,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai } /* curve mask can only change if the size changes */ - if (diameter != cache->lastdiameter) { + if (!equals_v2v2_int(diameter, cache->lastdiameter)) { if (cache->curve_mask) { MEM_freeN(cache->curve_mask); cache->curve_mask = NULL; @@ -739,7 +762,7 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai } /* detect if we need to recreate image brush buffer */ - if ((diameter != cache->lastdiameter) || + if ((!equals_v2v2_int(diameter, cache->lastdiameter)) || (tex_rotation != cache->last_tex_rotation) || do_random || update_color) @@ -751,24 +774,24 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai if (do_partial_update) { /* do partial update of texture */ - brush_painter_imbuf_partial_update(painter, pos, diameter); + brush_painter_imbuf_partial_update(painter, tile, pos, diameter); } else { /* create brush from scratch */ - cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); + cache->ibuf = brush_painter_imbuf_new(painter, tile, diameter, pressure, distance); } - cache->lastdiameter = diameter; + copy_v2_v2_int(cache->lastdiameter, diameter); cache->last_tex_rotation = tex_rotation; cache->last_pressure = pressure; } else if (do_partial_update) { /* do only partial update of texture */ - int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); - int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); + int dx = (int)floorf(tile->last_paintpos[0]) - (int)floorf(pos[0]); + int dy = (int)floorf(tile->last_paintpos[1]) - (int)floorf(pos[1]); if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos, diameter); + brush_painter_imbuf_partial_update(painter, tile, pos, diameter); } } @@ -776,6 +799,52 @@ static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *pai painter->pool = NULL; } +static bool paint_2d_check_tile(ImagePaintState *s, int i) +{ + if (i == 0) + return true; + if (i >= s->num_tiles) + return false; + + if (s->tiles[i].state == PAINT2D_TILE_READY) + return true; + if (s->tiles[i].state == PAINT2D_TILE_FAILED) + return false; + + s->tiles[i].cache.lastdiameter[0] = -1; + + s->tiles[i].iuser.ok = true; + + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); + if (ibuf) { + if (ibuf->channels != 4) { + s->tiles[i].state = PAINT2D_TILE_FAILED; + } + else if ((s->tiles[0].canvas->rect && !ibuf->rect ) || + (s->tiles[0].canvas->rect_float && !ibuf->rect_float)) { + s->tiles[i].state = PAINT2D_TILE_FAILED; + } + else { + s->tiles[i].size[0] = ibuf->x; + s->tiles[i].size[1] = ibuf->y; + s->tiles[i].radius_fac[0] = ((float) ibuf->x) / s->tiles[0].size[0]; + s->tiles[i].radius_fac[1] = ((float) ibuf->y) / s->tiles[0].size[1]; + s->tiles[i].state = PAINT2D_TILE_READY; + } + } + else { + s->tiles[i].state = PAINT2D_TILE_FAILED; + } + + if (s->tiles[i].state == PAINT2D_TILE_FAILED) { + BKE_image_release_ibuf(s->image, ibuf, NULL); + return false; + } + + s->tiles[i].canvas = ibuf; + return true; +} + /* keep these functions in sync */ static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4]) { @@ -816,24 +885,24 @@ static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus } } -static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile) +static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short paint_tile) { - if (tile & PAINT_TILE_X) { + if (paint_tile & PAINT_TILE_X) { *x %= ibuf->x; if (*x < 0) *x += ibuf->x; } - if (tile & PAINT_TILE_Y) { + if (paint_tile & PAINT_TILE_Y) { *y %= ibuf->y; if (*y < 0) *y += ibuf->y; } } -static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w) +static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short paint_tile, float w) { float inrgb[4]; - if (tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); + if (paint_tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, paint_tile); /* need to also do clipping here always since tiled coordinates * are not always within bounds */ if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { @@ -847,9 +916,9 @@ static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, shor return w; } -static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) +static void paint_2d_lift_soften(ImagePaintState *s, ImagePaintTile *tile, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short paint_tile) { - bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); float threshold = s->brush->sharp_threshold; int x, y, xi, yi, xo, yo, xk, yk; float count; @@ -865,7 +934,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, in_off[1] = pos[1]; out_off[0] = out_off[1] = 0; - if (!tile) { + if (!paint_tile) { IMB_rectclip( ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); @@ -884,8 +953,8 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, yi = in_off[1] + y; count = 0.0; - if (tile) { - paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); + if (paint_tile) { + paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, paint_tile); if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); else @@ -901,7 +970,7 @@ static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, for (xk = 0; xk < kernel->side; xk++) { count += paint_2d_ibuf_add_if( ibuf, xi + xk - kernel->pixel_len, - yi + yk - kernel->pixel_len, outrgb, tile, + yi + yk - kernel->pixel_len, outrgb, paint_tile, kernel->wdata[xk + yk * kernel->side]); } } @@ -949,7 +1018,7 @@ static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, region->height = height; } -static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short tile) +static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short paint_tile) { int destx = region->destx; int desty = region->desty; @@ -960,13 +1029,13 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, int origw, origh, w, h, tot = 0; /* convert destination and source coordinates to be within image */ - if (tile & PAINT_TILE_X) { + if (paint_tile & PAINT_TILE_X) { destx = destx % dbuf->x; if (destx < 0) destx += dbuf->x; srcx = srcx % sbuf->x; if (srcx < 0) srcx += sbuf->x; } - if (tile & PAINT_TILE_Y) { + if (paint_tile & PAINT_TILE_Y) { desty = desty % dbuf->y; if (desty < 0) desty += dbuf->y; srcy = srcy % sbuf->y; @@ -982,23 +1051,23 @@ static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); /* do 3 other rects if needed */ - if ((tile & PAINT_TILE_X) && w < origw) + if ((paint_tile & PAINT_TILE_X) && w < origw) paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); - if ((tile & PAINT_TILE_Y) && h < origh) + if ((paint_tile & PAINT_TILE_Y) && h < origh) paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); - if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) + if ((paint_tile & PAINT_TILE_X) && (paint_tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); return tot; } -static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile) +static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint_tile) { ImagePaintRegion region[4]; int a, tot; paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); - tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); + tot = paint_2d_torus_split_region(region, ibufb, ibuf, paint_tile); for (a = 0; a < tot; a++) IMB_rectblend( @@ -1034,9 +1103,8 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[ static void paint_2d_do_making_brush( ImagePaintState *s, + ImagePaintTile *tile, ImagePaintRegion *region, - unsigned short *curveb, - unsigned short *texmaskb, ImBuf *frombuf, float mask_max, short blend, @@ -1055,14 +1123,14 @@ static void paint_2d_do_making_brush( int origx = region->destx - tx * IMAPAINT_TILE_SIZE; int origy = region->desty - ty * IMAPAINT_TILE_SIZE; - if (s->canvas->rect_float) - tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + if (tile->canvas->rect_float) + tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, tile->canvas, tx, ty, &mask, false); else - tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, tile->canvas, tx, ty, &mask, false); IMB_rectblend( - s->canvas, &tmpbuf, frombuf, mask, - curveb, texmaskb, mask_max, + tile->canvas, &tmpbuf, frombuf, mask, + tile->cache.curve_mask, tile->cache.tex_mask, mask_max, region->destx, region->desty, origx, origy, region->srcx, region->srcy, @@ -1074,9 +1142,8 @@ static void paint_2d_do_making_brush( typedef struct Paint2DForeachData { ImagePaintState *s; + ImagePaintTile *tile; ImagePaintRegion *region; - unsigned short *curveb; - unsigned short *texmaskb; ImBuf *frombuf; float mask_max; short blend; @@ -1091,19 +1158,21 @@ static void paint_2d_op_foreach_do( { Paint2DForeachData *data = (Paint2DForeachData *)data_v; paint_2d_do_making_brush( - data->s, data->region, data->curveb, - data->texmaskb, data->frombuf, data->mask_max, + data->s, data->tile, data->region, + data->frombuf, data->mask_max, data->blend, data->tilex, iter, data->tilew, iter); } -static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) +static int paint_2d_op(void *state, ImagePaintTile *tile, const float lastpos[2], const float pos[2]) { ImagePaintState *s = ((ImagePaintState *)state); ImBuf *clonebuf = NULL, *frombuf; + ImBuf *canvas = tile->canvas; + ImBuf *ibufb = tile->cache.ibuf; ImagePaintRegion region[4]; - short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); + short paint_tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); short blend = s->blend; const float *offset = s->brush->clone.offset; float liftpos[2]; @@ -1115,7 +1184,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign /* lift from canvas */ if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); + paint_2d_lift_soften(s, tile, canvas, ibufb, bpos, paint_tile); blend = IMB_BLEND_INTERPOLATE; } else if (s->tool == PAINT_TOOL_SMEAR) { @@ -1123,12 +1192,12 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign return 0; paint_2d_convert_brushco(ibufb, lastpos, blastpos); - paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); + paint_2d_lift_smear(canvas, ibufb, blastpos, paint_tile); blend = IMB_BLEND_INTERPOLATE; } else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { - liftpos[0] = pos[0] - offset[0] * s->canvas->x; - liftpos[1] = pos[1] - offset[1] * s->canvas->y; + liftpos[0] = pos[0] - offset[0] * canvas->x; + liftpos[1] = pos[1] - offset[1] * canvas->y; paint_2d_convert_brushco(ibufb, liftpos, bliftpos); clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); @@ -1136,9 +1205,9 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign frombuf = (clonebuf) ? clonebuf : ibufb; - if (tile) { + if (paint_tile) { paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); + tot = paint_2d_torus_split_region(region, canvas, frombuf, paint_tile); } else { paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); @@ -1148,7 +1217,7 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign /* blend into canvas */ for (a = 0; a < tot; a++) { ED_imapaint_dirty_region( - s->image, s->canvas, + s->image, canvas, region[a].destx, region[a].desty, region[a].width, region[a].height, true); @@ -1157,21 +1226,20 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign int tilex, tiley, tilew, tileh; imapaint_region_tiles( - s->canvas, region[a].destx, region[a].desty, + canvas, region[a].destx, region[a].desty, region[a].width, region[a].height, &tilex, &tiley, &tilew, &tileh); if (tiley == tileh) { paint_2d_do_making_brush( - s, ®ion[a], curveb, texmaskb, frombuf, + s, tile, ®ion[a], frombuf, mask_max, blend, tilex, tiley, tilew, tileh); } else { Paint2DForeachData data; data.s = s; + data.tile = tile; data.region = ®ion[a]; - data.curveb = curveb; - data.texmaskb = texmaskb; data.frombuf = frombuf; data.mask_max = mask_max; data.blend = blend; @@ -1190,7 +1258,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign else { /* no masking, composite brush directly onto canvas */ IMB_rectblend_threaded( - s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, + canvas, canvas, frombuf, NULL, + tile->cache.curve_mask, tile->cache.tex_mask, mask_max, region[a].destx, region[a].desty, region[a].destx, region[a].desty, region[a].srcx, region[a].srcy, @@ -1204,46 +1273,25 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign } -static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) +static int paint_2d_canvas_set(ImagePaintState *s) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - /* verify that we can paint and set canvas */ - if (ima == NULL) { - return 0; - } - else if (BKE_image_has_packedfile(ima) && ima->rr) { - s->warnpackedfile = ima->id.name + 2; - return 0; - } - else if (ibuf && ibuf->channels != 4) { - s->warnmultifile = ima->id.name + 2; - return 0; - } - else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) - return 0; - - s->image = ima; - s->canvas = ibuf; - /* set clone canvas */ if (s->tool == PAINT_TOOL_CLONE) { - ima = s->brush->clone.image; - ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + Image *ima = s->brush->clone.image; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { BKE_image_release_ibuf(ima, ibuf, NULL); - BKE_image_release_ibuf(s->image, s->canvas, NULL); return 0; } s->clonecanvas = ibuf; /* temporarily add float rect for cloning */ - if (s->canvas->rect_float && !s->clonecanvas->rect_float) { + if (s->tiles[0].canvas->rect_float && !s->clonecanvas->rect_float) { IMB_float_from_rect(s->clonecanvas); } - else if (!s->canvas->rect_float && !s->clonecanvas->rect) + else if (!s->tiles[0].canvas->rect_float && !s->clonecanvas->rect) IMB_rect_from_float(s->clonecanvas); } @@ -1255,7 +1303,9 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) static void paint_2d_canvas_free(ImagePaintState *s) { - BKE_image_release_ibuf(s->image, s->canvas, NULL); + for (int i = 0; i < s->num_tiles; i++) { + BKE_image_release_ibuf(s->image, s->tiles[i].canvas, NULL); + } BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); if (s->blurkernel) { @@ -1266,57 +1316,94 @@ static void paint_2d_canvas_free(ImagePaintState *s) image_undo_remove_masks(); } -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size) +static void paint_2d_transform_mouse(ImagePaintState *s, const float in[2], float out[2]) { - float newuv[2], olduv[2]; + UI_view2d_region_to_view(s->v2d, in[0], in[1], &out[0], &out[1]); +} + +static bool is_inside_tile(const int size[2], const float pos[2], const float brush[2]) +{ + return (pos[0] >= -brush[0]) && (pos[0] < size[0]+brush[0]) && + (pos[1] >= -brush[1]) && (pos[1] < size[1]+brush[1]); +} + +static void paint_2d_uv_to_coord(ImagePaintTile *tile, const float uv[2], float coord[2]) +{ + coord[0] = (uv[0] - tile->uv_ofs[0]) * tile->size[0]; + coord[1] = (uv[1] - tile->uv_ofs[1]) * tile->size[1]; +} + +void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float base_size) +{ + float new_uv[2], old_uv[2]; ImagePaintState *s = ps; BrushPainter *painter = s->painter; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); - if (!ibuf) - return; + const bool is_data = s->tiles[0].canvas->colormanage_flag & IMB_COLORMANAGE_IS_DATA; s->blend = s->brush->blend; if (eraser) s->blend = IMB_BLEND_ERASE_ALPHA; - UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); - UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); - - newuv[0] *= ibuf->x; - newuv[1] *= ibuf->y; - - olduv[0] *= ibuf->x; - olduv[1] *= ibuf->y; + UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &new_uv[0], &new_uv[1]); + UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &old_uv[0], &old_uv[1]); + float last_uv[2], start_uv[2]; + UI_view2d_region_to_view(s->v2d, 0.0f, 0.0f, &start_uv[0], &start_uv[1]); if (painter->firsttouch) { - float startuv[2]; - - UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); - /* paint exactly once on first touch */ - painter->startpaintpos[0] = startuv[0] * ibuf->x; - painter->startpaintpos[1] = startuv[1] * ibuf->y; - - painter->firsttouch = 0; - copy_v2_v2(painter->lastpaintpos, newuv); + copy_v2_v2(last_uv, new_uv); } else { - copy_v2_v2(painter->lastpaintpos, olduv); + copy_v2_v2(last_uv, old_uv); } - /* OCIO_TODO: float buffers are now always linear, so always use color correction - * this should probably be changed when texture painting color space is supported - */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); + float uv_brush_size[2] = {base_size / s->tiles[0].size[0], base_size / s->tiles[0].size[1]}; + + for (int i = 0; i < s->num_tiles; i++) { + ImagePaintTile *tile = &s->tiles[i]; + + /* First test: Project brush into UV space, clip against tile. */ + const int uv_size[2] = {1, 1}; + float local_new_uv[2], local_old_uv[2]; + sub_v2_v2v2(local_new_uv, new_uv, tile->uv_ofs); + sub_v2_v2v2(local_old_uv, old_uv, tile->uv_ofs); + if (!(is_inside_tile(uv_size, local_new_uv, uv_brush_size) || is_inside_tile(uv_size, local_old_uv, uv_brush_size))) + continue; + + /* Lazy tile loading to get size in pixels. */ + if (!paint_2d_check_tile(s, i)) + continue; + + float size[2]; + mul_v2_v2fl(size, tile->radius_fac, base_size); + + float new_coord[2], old_coord[2]; + paint_2d_uv_to_coord(tile, new_uv, new_coord); + paint_2d_uv_to_coord(tile, old_uv, old_coord); + if (painter->firsttouch) { + paint_2d_uv_to_coord(tile, start_uv, tile->start_paintpos); + } + paint_2d_uv_to_coord(tile, last_uv, tile->last_paintpos); + + /* Second check in pixel coordinates. */ + if (!(is_inside_tile(tile->size, new_coord, size) || is_inside_tile(tile->size, old_coord, size))) + continue; - brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &tile->iuser, NULL); - if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv)) - s->need_redraw = true; + /* OCIO_TODO: float buffers are now always linear, so always use color correction + * this should probably be changed when texture painting color space is supported + */ + brush_painter_2d_require_imbuf(painter->brush, tile, (ibuf->rect_float != NULL), !is_data); - BKE_image_release_ibuf(s->image, ibuf, NULL); + brush_painter_2d_refresh_cache(s, painter, tile, new_coord, mval, pressure, distance, size); + + if (paint_2d_op(s, tile, old_coord, new_coord)) + tile->need_redraw = true; + } + + painter->firsttouch = 0; } void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) @@ -1339,12 +1426,53 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) s->image = s->sima->image; s->symmetry = settings->imapaint.paint.symmetry_flags; - if (!paint_2d_canvas_set(s, s->image)) { - if (s->warnmultifile) - BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); - if (s->warnpackedfile) - BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); + if (s->image == NULL) { + MEM_freeN(s); + return NULL; + } + if (BKE_image_has_packedfile(s->image) && s->image->rr) { + BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); + MEM_freeN(s); + return 0; + } + + s->num_tiles = BLI_listbase_count(&s->image->tiles); + s->tiles = MEM_callocN(sizeof(ImagePaintTile) * s->num_tiles, "ImagePaintTile"); + s->tiles[0].iuser.ok = true; + + zero_v2(s->tiles[0].uv_ofs); + + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[0].iuser, NULL); + if (!ibuf) { + MEM_freeN(s->tiles); + MEM_freeN(s); + return NULL; + } + if (ibuf->channels != 4) { + BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); + MEM_freeN(s->tiles); + MEM_freeN(s); + return NULL; + } + + s->tiles[0].size[0] = ibuf->x; + s->tiles[0].size[1] = ibuf->y; + copy_v2_fl(s->tiles[0].radius_fac, 1.0f); + + s->tiles[0].canvas = ibuf; + s->tiles[0].state = PAINT2D_TILE_READY; + + /* Initialize offsets here, they're needed for the uv space clip test before lazy-loading the tile properly. */ + ImageTile *tile = BKE_image_get_tile(s->image, 0)->next; + for (int tile_idx = 1; tile; tile = tile->next, tile_idx++) { + s->tiles[tile_idx].iuser.tile = tile->tile_number; + s->tiles[tile_idx].uv_ofs[0] = (tile->tile_number % 10); + s->tiles[tile_idx].uv_ofs[1] = (tile->tile_number / 10); + } + + if (!paint_2d_canvas_set(s)) { + MEM_freeN(s->tiles); MEM_freeN(s); return NULL; } @@ -1365,18 +1493,26 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) { ImagePaintState *s = ps; - if (s->need_redraw) { - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + bool had_redraw = false; + for (int i = 0; i < s->num_tiles; i++) { + if (s->tiles[i].need_redraw) { + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); - imapaint_image_update(s->sima, s->image, ibuf, false); - ED_imapaint_clear_partial_redraw(); + imapaint_image_update(s->sima, s->image, ibuf, &s->tiles[i].iuser, false); - BKE_image_release_ibuf(s->image, ibuf, NULL); + BKE_image_release_ibuf(s->image, ibuf, NULL); - s->need_redraw = false; + s->tiles[i].need_redraw = false; + had_redraw = true; + } } - else if (!final) { - return; + + if (had_redraw) { + ED_imapaint_clear_partial_redraw(); + if (!s->sima || !s->sima->lock) + ED_region_tag_redraw(CTX_wm_region(C)); + else + WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); } if (final) { @@ -1387,12 +1523,6 @@ void paint_2d_redraw(const bContext *C, void *ps, bool final) WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); DEG_id_tag_update(&s->image->id, 0); } - else { - if (!s->sima || !s->sima->lock) - ED_region_tag_redraw(CTX_wm_region(C)); - else - WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); - } } void paint_2d_stroke_done(void *ps) @@ -1400,7 +1530,11 @@ void paint_2d_stroke_done(void *ps) ImagePaintState *s = ps; paint_2d_canvas_free(s); - brush_painter_2d_free(s->painter); + for (int i = 0; i < s->num_tiles; i++) { + brush_painter_cache_2d_free(&s->tiles[i].cache); + } + MEM_freeN(s->painter); + MEM_freeN(s->tiles); paint_brush_exit_tex(s->brush); MEM_freeN(s); @@ -1451,7 +1585,7 @@ static void paint_2d_fill_add_pixel_float( /* this function expects linear space color values */ void paint_2d_bucket_fill( const bContext *C, const float color[3], Brush *br, - const float mouse_init[2], + const float mouse_init[2], const float mouse_final[2], void *ps) { SpaceImage *sima = CTX_wm_space_image(C); @@ -1470,7 +1604,26 @@ void paint_2d_bucket_fill( if (!ima) return; - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + float uv_ofs[2]; + float image_init[2], image_final[2]; + paint_2d_transform_mouse(s, mouse_init, image_init); + int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_ofs); + if (mouse_final) { + paint_2d_transform_mouse(s, mouse_final, image_final); + sub_v2_v2(image_final, uv_ofs); + } + + ImageUser *iuser = &s->tiles[0].iuser; + for (int i = 0; i < s->num_tiles; i++) { + if (s->tiles[i].iuser.tile == tile_number) { + if (!paint_2d_check_tile(s, i)) + return; + iuser = &s->tiles[i].iuser; + break; + } + } + + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (!ibuf) return; @@ -1488,7 +1641,7 @@ void paint_2d_bucket_fill( color_f[3] = strength; } - if (!mouse_init || !br) { + if (!mouse_final || !br) { /* first case, no image UV, fill the whole image */ ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); @@ -1518,14 +1671,11 @@ void paint_2d_bucket_fill( BLI_bitmap *touched; size_t coordinate; int width = ibuf->x; - float image_init[2]; int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; float pixel_color[4]; /* We are comparing to sum of three squared values (assumed in range [0,1]), so need to multiply... */ float threshold_sq = br->fill_threshold * br->fill_threshold * 3; - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - x_px = image_init[0] * ibuf->x; y_px = image_init[1] * ibuf->y; @@ -1622,7 +1772,7 @@ void paint_2d_bucket_fill( BLI_stack_free(stack); } - imapaint_image_update(sima, ima, ibuf, false); + imapaint_image_update(sima, ima, ibuf, iuser, false); ED_imapaint_clear_partial_redraw(); BKE_image_release_ibuf(ima, ibuf, NULL); @@ -1652,13 +1802,29 @@ void paint_2d_gradient_fill( if (!ima) return; - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + paint_2d_transform_mouse(s, mouse_final, image_final); + paint_2d_transform_mouse(s, mouse_init, image_init); + + float uv_ofs[2]; + int tile_number = BKE_image_get_tile_from_pos(ima, image_init, image_init, uv_ofs); + sub_v2_v2(image_init, uv_ofs); + sub_v2_v2(image_final, uv_ofs); + + ImageUser *iuser = &s->tiles[0].iuser; + for (int i = 0; i < s->num_tiles; i++) { + if (s->tiles[i].iuser.tile == tile_number) { + if (!paint_2d_check_tile(s, i)) + return; + iuser = &s->tiles[i].iuser; + break; + } + } + + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (!ibuf) return; - UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); image_final[0] *= ibuf->x; image_final[1] *= ibuf->y; @@ -1739,7 +1905,7 @@ void paint_2d_gradient_fill( } } - imapaint_image_update(sima, ima, ibuf, false); + imapaint_image_update(sima, ima, ibuf, iuser, false); ED_imapaint_clear_partial_redraw(); BKE_image_release_ibuf(ima, ibuf, NULL); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 7badd30e6d0..50d42aed248 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -193,6 +193,8 @@ BLI_INLINE unsigned char f_to_char(const float val) * because 'partRedrawRect' and 'touch' values would not be thread safe */ typedef struct ProjPaintImage { Image *ima; + int tile; + ImageUser iuser; ImBuf *ibuf; ImagePaintPartialRedraw *partRedrawRect; volatile void **undoRect; /* only used to build undo tiles during painting */ @@ -468,6 +470,16 @@ BLI_INLINE const MPoly *ps_tri_index_to_mpoly(const ProjPaintState *ps, int tri_ /* Finish projection painting structs */ +static int project_paint_face_paint_tile(Image *ima, const float *uv) +{ + if (!ima || ima->source != IMA_SRC_TILED) + return 0; + /* Currently, faces are assumed to belong to one tile, so checking the first loop is enough. */ + int tx = (int) uv[0]; + int ty = (int) uv[1]; + return 10*ty + tx; +} + static TexPaintSlot *project_paint_face_paint_slot(const ProjPaintState *ps, int tri_index) { const MPoly *mp = ps_tri_index_to_mpoly(ps, tri_index); @@ -664,8 +676,18 @@ static bool project_paint_PickColor( interp_v2_v2v2v2(uv, UNPACK3(lt_tri_uv), w); ima = project_paint_face_paint_image(ps, tri_index); - ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */ - if (!ibuf) return 0; + int tile_number = project_paint_face_paint_tile(ima, lt_tri_uv[0]); + ImageUser iuser = {NULL}; + iuser.ok = 1; + iuser.tile = tile_number; + ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (!ibuf) { + iuser.tile = 0; + ibuf = BKE_image_acquire_ibuf(ima, &iuser, NULL); + if (!ibuf) { + return 0; + } + } if (interp) { float x, y; @@ -1052,6 +1074,8 @@ static bool check_seam( const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; Image *tpage = project_paint_face_paint_image(ps, tri_index); Image *orig_tpage = project_paint_face_paint_image(ps, orig_face); + int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]); + int orig_tile = project_paint_face_paint_tile(orig_tpage, orig_lt_tri_uv[0]); BLI_assert(i1_fidx != -1); @@ -1069,6 +1093,7 @@ static bool check_seam( /* first test if they have the same image */ if ((orig_tpage == tpage) && + (orig_tile == tile) && cmp_uv(orig_lt_tri_uv[orig_i1_fidx], lt_tri_uv[i1_fidx]) && cmp_uv(orig_lt_tri_uv[orig_i2_fidx], lt_tri_uv[i2_fidx])) { @@ -2921,6 +2946,7 @@ static void project_bucket_init( ImBuf *ibuf = NULL; Image *tpage_last = NULL, *tpage; ImBuf *tmpibuf = NULL; + int tile_last = 0; if (ps->image_tot == 1) { /* Simple loop, no context switching */ @@ -2938,17 +2964,33 @@ static void project_bucket_init( for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { tri_index = GET_INT_FROM_POINTER(node->link); + const MLoopTri *lt = &ps->mlooptri_eval[tri_index]; + const float *lt_tri_uv[3] = { PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt) }; + /* Image context switching */ tpage = project_paint_face_paint_image(ps, tri_index); - if (tpage_last != tpage) { + int tile = project_paint_face_paint_tile(tpage, lt_tri_uv[0]); + if (tpage_last != tpage || tile_last != tile) { tpage_last = tpage; + tile_last = tile; for (image_index = 0; image_index < ps->image_tot; image_index++) { - if (ps->projImages[image_index].ima == tpage_last) { - ibuf = ps->projImages[image_index].ibuf; + ProjPaintImage *projIma = &ps->projImages[image_index]; + if ((projIma->ima == tpage) && (projIma->tile == tile)) { + ibuf = projIma->ibuf; break; } } + if (ibuf == NULL) { + /* Failed to find the specific tile, fall back to the primary tile. */ + for (image_index = 0; image_index < ps->image_tot; image_index++) { + ProjPaintImage *projIma = &ps->projImages[image_index]; + if ((projIma->ima == tpage) && (projIma->tile == 0)) { + ibuf = projIma->ibuf; + break; + } + } + } } /* context switching done */ @@ -3624,22 +3666,36 @@ static bool project_paint_winclip( #endif //PROJ_DEBUG_WINCLIP +typedef struct PrepareImageEntry { + struct PrepareImageEntry *next, *prev; + Image *ima; + int tile; +} PrepareImageEntry; + static void project_paint_build_proj_ima( ProjPaintState *ps, MemArena *arena, - LinkNode *image_LinkList) + ListBase *used_images) { ProjPaintImage *projIma; - LinkNode *node; + PrepareImageEntry *entry; int i; /* build an array of images we use */ projIma = ps->projImages = BLI_memarena_alloc(arena, sizeof(ProjPaintImage) * ps->image_tot); - for (node = image_LinkList, i = 0; node; node = node->next, i++, projIma++) { + for (entry = used_images->first, i = 0; entry; entry = entry->next, i++, projIma++) { + memset(&projIma->iuser, 0, sizeof(ImageUser)); + projIma->iuser.ok = 1; + projIma->iuser.tile = entry->tile; int size; - projIma->ima = node->link; + projIma->ima = entry->ima; + projIma->tile = entry->tile; projIma->touch = 0; - projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, NULL, NULL); + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL); + if (!projIma->ibuf) { + projIma->iuser.tile = 0; + projIma->ibuf = BKE_image_acquire_ibuf(projIma->ima, &projIma->iuser, NULL); + } size = sizeof(void **) * IMAPAINT_TILE_NUMBER(projIma->ibuf->x) * IMAPAINT_TILE_NUMBER(projIma->ibuf->y); projIma->partRedrawRect = BLI_memarena_alloc(arena, sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); partial_redraw_array_init(projIma->partRedrawRect); @@ -3660,15 +3716,18 @@ static void project_paint_prepare_all_faces( const bool is_multi_view) { /* Image Vars - keep track of images we have used */ - LinkNodePair image_LinkList = {NULL, NULL}; + ListBase used_images = {NULL}; Image *tpage_last = NULL, *tpage; TexPaintSlot *slot_last = NULL; TexPaintSlot *slot = NULL; + int tile_last = -1, tile; const MLoopTri *lt; int image_index = -1, tri_index; int prev_poly = -1; + BLI_assert(ps->image_tot == 0); + for (tri_index = 0, lt = ps->mlooptri_eval; tri_index < ps->totlooptri_eval; tri_index++, lt++) { bool is_face_sel; @@ -3709,6 +3768,8 @@ static void project_paint_prepare_all_faces( ps->poly_to_loop_uv[lt->poly] = mloopuv_base; + tile = project_paint_face_paint_tile(tpage, mloopuv_base[lt->tri[0]].uv); + if (project_paint_clone_face_skip(ps, layer_clone, slot, tri_index)) { continue; } @@ -3766,17 +3827,24 @@ static void project_paint_prepare_all_faces( } } - if (tpage_last != tpage) { - - image_index = BLI_linklist_index(image_LinkList.list, tpage); + if (tpage_last != tpage || tile_last != tile) { + image_index = 0; + for (PrepareImageEntry *e = used_images.first; e; e = e->next, image_index++) { + if (e->ima == tpage && e->tile == tile) { + break; + } + } - if (image_index == -1 && BKE_image_has_ibuf(tpage, NULL)) { /* MemArena dosnt have an append func */ - BLI_linklist_append(&image_LinkList, tpage); - image_index = ps->image_tot; + if (image_index == ps->image_tot) { + PrepareImageEntry *e = MEM_callocN(sizeof(PrepareImageEntry), "PrepareImageEntry"); + e->ima = tpage; + e->tile = tile; + BLI_addtail(&used_images, e); ps->image_tot++; } tpage_last = tpage; + tile_last = tile; } if (image_index != -1) { @@ -3789,11 +3857,11 @@ static void project_paint_prepare_all_faces( /* build an array of images we use*/ if (ps->is_shared_user == false) { - project_paint_build_proj_ima(ps, arena, image_LinkList.list); + project_paint_build_proj_ima(ps, arena, &used_images); } /* we have built the array, discard the linked list */ - BLI_linklist_free(image_LinkList.list, NULL); + BLI_freelistN(&used_images); } /* run once per stroke before projection painting */ @@ -4036,7 +4104,7 @@ static bool project_image_refresh_tagged(ProjPaintState *ps) pr = &(projIma->partRedrawRect[i]); if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ set_imapaintpartial(pr); - imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); + imapaint_image_update(NULL, projIma->ima, projIma->ibuf, &projIma->iuser, true); redraw = 1; } @@ -5644,7 +5712,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) } ima = BKE_image_add_generated( bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color, false); + gen_type, color, false, false); return ima; } diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 7d3049434d6..eb4fb793967 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -181,7 +181,7 @@ typedef struct ImagePaintPartialRedraw { #define IMAPAINT_TILE_NUMBER(size) (((size) + IMAPAINT_TILE_SIZE - 1) >> IMAPAINT_TILE_BITS) bool image_texture_paint_poll(struct bContext *C); -void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint); +void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, struct ImageUser *iuser, short texpaint); struct ImagePaintPartialRedraw *get_imapaintpartial(void); void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr); void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th); @@ -193,7 +193,7 @@ void paint_2d_stroke( void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size); void paint_2d_bucket_fill( - const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], void *ps); + const struct bContext *C, const float color[3], struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); void paint_2d_gradient_fill( const struct bContext *C, struct Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps); void *paint_proj_new_stroke( diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index ebdf5342172..a3f91bf5090 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -1790,7 +1790,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar) /* if no clip, nothing to do */ if (!clip) { - ED_region_grid_draw(ar, zoomx, zoomy); + ED_region_grid_draw(ar, zoomx, zoomy, 0.0f, 0.0f); return; } @@ -1836,7 +1836,7 @@ void clip_draw_main(const bContext *C, SpaceClip *sc, ARegion *ar) draw_movieclip_muted(ar, width, height, zoomx, zoomy); } else { - ED_region_grid_draw(ar, zoomx, zoomy); + ED_region_grid_draw(ar, zoomx, zoomy, 0.0f, 0.0f); } if (width && height) { diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 1d8c6721b64..70358cc47fd 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -112,7 +112,7 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf, if (ibuf->zbuf || ibuf->zbuf_float) ofs += BLI_strncpy_rlen(str + ofs, IFACE_(" + Z"), len - ofs); - if (ima->source == IMA_SRC_SEQUENCE) { + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { const char *file = BLI_last_slash(ibuf->name); if (file == NULL) file = ibuf->name; diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 4cbe25462af..c49190a97af 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -49,6 +49,7 @@ #include "BLI_threads.h" #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BLI_listbase.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -479,7 +480,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene, float x1, float y1, int rec MEM_freeN(rectf); } -static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy) +static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy, const char *label) { int x, y; @@ -547,6 +548,24 @@ static void draw_image_buffer(const bContext *C, SpaceImage *sima, ARegion *ar, if (sima->flag & SI_USE_ALPHA) GPU_blend(false); } + + if (label && label[0]) { + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + BLF_size(blf_mono_font, 25 * U.pixelsize, U.dpi); + + int textwidth = BLF_width(blf_mono_font, label, strlen(label)) + 10; + float opacity; + float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur); + if (textwidth < 0.5f*(stepx - 10)) opacity = 1.0f; + else if (textwidth < (stepx - 10)) opacity = 2.0f - 2.0f*(textwidth / (stepx - 10)); + else opacity = 0.0f; + BLF_color4ub(blf_mono_font, 220, 220, 220, 150*opacity); + BLF_position(blf_mono_font, (int) (x + 10), (int) (y + 10), 0); + BLF_draw_ascii(blf_mono_font, label, strlen(label)); + + glDisable(GL_BLEND); + } } /* draw uv edit */ @@ -586,9 +605,13 @@ void draw_image_sample_line(SpaceImage *sima) immUniformArray4fv("colors", (float *)(float[][4]){{1.0f, 1.0f, 1.0f, 1.0f}, {0.0f, 0.0f, 0.0f, 1.0f}}, 2); immUniform1f("dash_width", 2.0f); + float co[2][2]; + add_v2_v2v2(co[0], hist->co[0], hist->draw_offset); + add_v2_v2v2(co[1], hist->co[1], hist->draw_offset); + immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(shdr_dashed_pos, hist->co[0]); - immVertex2fv(shdr_dashed_pos, hist->co[1]); + immVertex2fv(shdr_dashed_pos, co[0]); + immVertex2fv(shdr_dashed_pos, co[1]); immEnd(); immUnbindProgram(); @@ -633,6 +656,69 @@ static void draw_image_paint_helpers(const bContext *C, ARegion *ar, Scene *scen } } +static void draw_udim_tile_grid(unsigned int pos_attr, unsigned int color_attr, + ARegion *ar, int x, int y, + float stepx, float stepy, const float color[3]) +{ + float x1, y1; + UI_view2d_view_to_region_fl(&ar->v2d, x, y, &x1, &y1); + int gridpos[5][2] = {{0, 0}, {0, 1}, {1, 1}, {1, 0}, {0, 0}}; + for(int i = 0; i < 4; i++) { + immAttrib3fv(color_attr, color); + immVertex2f(pos_attr, x1 + gridpos[i][0]*stepx, y1 + gridpos[i][1]*stepy); + immAttrib3fv(color_attr, color); + immVertex2f(pos_attr, x1 + gridpos[i+1][0]*stepx, y1 + gridpos[i+1][1]*stepy); + } +} + +static void draw_image_tiles(ARegion *ar, SpaceImage *sima, Image *ima) +{ + int num_tiles; + if (ima) { + num_tiles = BLI_listbase_count(&ima->tiles); + } + else { + num_tiles = sima->tile_grid_shape[0] * sima->tile_grid_shape[1]; + } + + float stepx = BLI_rcti_size_x(&ar->v2d.mask) / BLI_rctf_size_x(&ar->v2d.cur); + float stepy = BLI_rcti_size_y(&ar->v2d.mask) / BLI_rctf_size_y(&ar->v2d.cur); + + GPUVertFormat *format = immVertexFormat(); + unsigned int pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + unsigned color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + immBegin(GPU_PRIM_LINES, 8*(num_tiles + 1)); + + float theme_color[3], selected_color[3]; + UI_GetThemeColorShade3fv(TH_BACK, 60.0f, theme_color); + UI_GetThemeColor3fv(TH_FACE_SELECT, selected_color); + + if (ima) { + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + int x = tile->tile_number % 10; + int y = tile->tile_number / 10; + draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color); + } + } + else { + for (int y = 0; y < sima->tile_grid_shape[1]; y++) { + for (int x = 0; x < sima->tile_grid_shape[0]; x++) { + draw_udim_tile_grid(pos, color, ar, x, y, stepx, stepy, theme_color); + } + } + } + + int cur_x = sima->curtile % 10, cur_y = sima->curtile / 10; + draw_udim_tile_grid(pos, color, ar, cur_x, cur_y, stepx, stepy, selected_color); + + immEnd(); + immUnbindProgram(); + + return; +} + /* draw main image region */ void draw_image_main(const bContext *C, ARegion *ar) @@ -692,14 +778,35 @@ void draw_image_main(const bContext *C, ARegion *ar) BKE_image_multiview_index(ima, &sima->iuser); } - ibuf = ED_space_image_acquire_buffer(sima, &lock); + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); + + int main_w = 0; + int main_h = 0; /* draw the image or grid */ if (ibuf == NULL) { - ED_region_grid_draw(ar, zoomx, zoomy); + if (ima) { + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + int x = tile->tile_number % 10; + int y = tile->tile_number / 10; + ED_region_grid_draw(ar, zoomx, zoomy, x, y); + } + } + else { + for (int y = 0; y < sima->tile_grid_shape[1]; y++) { + for (int x = 0; x < sima->tile_grid_shape[0]; x++) { + ED_region_grid_draw(ar, zoomx, zoomy, x, y); + } + } + } } else { - draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); + main_w = ibuf->x; + main_h = ibuf->y; + + char label[64]; + BKE_image_get_tile_label(ima, 0, label, sizeof(label)); + draw_image_buffer(C, sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy, label); if (sima->flag & SI_DRAW_METADATA) { int x, y; @@ -714,6 +821,28 @@ void draw_image_main(const bContext *C, ARegion *ar) ED_space_image_release_buffer(sima, ibuf, lock); + if (ima && ima->source == IMA_SRC_TILED) { + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->tile_number == 0) + continue; + + ibuf = ED_space_image_acquire_buffer(sima, &lock, tile->tile_number); + if (ibuf) { + int x_pos = tile->tile_number % 10; + int y_pos = tile->tile_number / 10; + char label[64]; + BKE_image_get_tile_label(ima, tile, label, sizeof(label)); + + float tile_zoomx = (zoomx * main_w) / ibuf->x; + float tile_zoomy = (zoomy * main_h) / ibuf->y; + draw_image_buffer(C, sima, ar, scene, ibuf, x_pos, y_pos, tile_zoomx, tile_zoomy, label); + } + ED_space_image_release_buffer(sima, ibuf, lock); + } + } + + draw_image_tiles(ar, sima, ima); + /* paint helpers */ if (show_paint) draw_image_paint_helpers(C, ar, scene, zoomx, zoomy); diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 4388accc534..140f4dcc097 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -105,7 +105,7 @@ void ED_space_image_set_mask(bContext *C, SpaceImage *sima, Mask *mask) } } -ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock) +ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock, int tile) { ImBuf *ibuf; @@ -115,7 +115,9 @@ ImBuf *ED_space_image_acquire_buffer(SpaceImage *sima, void **r_lock) return BIF_render_spare_imbuf(); else #endif + sima->iuser.tile = tile; ibuf = BKE_image_acquire_ibuf(sima->image, &sima->iuser, r_lock); + sima->iuser.tile = 0; if (ibuf) { if (ibuf->rect || ibuf->rect_float) @@ -142,7 +144,7 @@ bool ED_space_image_has_buffer(SpaceImage *sima) void *lock; bool has_buffer; - ibuf = ED_space_image_acquire_buffer(sima, &lock); + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); has_buffer = (ibuf != NULL); ED_space_image_release_buffer(sima, ibuf, lock); @@ -155,7 +157,8 @@ void ED_space_image_get_size(SpaceImage *sima, int *width, int *height) ImBuf *ibuf; void *lock; - ibuf = ED_space_image_acquire_buffer(sima, &lock); + /* TODO(lukas): Support tiled images with different sizes */ + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); if (ibuf && ibuf->x > 0 && ibuf->y > 0) { *width = ibuf->x; diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 5d9c496d584..143f2446b8d 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -97,6 +97,11 @@ void IMAGE_OT_read_viewlayers(struct wmOperatorType *ot); void IMAGE_OT_render_border(struct wmOperatorType *ot); void IMAGE_OT_clear_render_border(struct wmOperatorType *ot); +void IMAGE_OT_add_tile(struct wmOperatorType *ot); +void IMAGE_OT_remove_tile(struct wmOperatorType *ot); +void IMAGE_OT_fill_tile(struct wmOperatorType *ot); +void IMAGE_OT_select_tile(struct wmOperatorType *ot); + /* image_panels.c */ struct ImageUser *ntree_get_active_iuser(struct bNodeTree *ntree); void image_buttons_register(struct ARegionType *art); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index be91f7abf99..bcbfa1342d5 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -45,6 +45,9 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_string_utf8.h" +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_linklist.h" #include "BLT_translation.h" @@ -223,7 +226,7 @@ static bool space_image_file_exists_poll(bContext *C) bool ret = false; char name[FILE_MAX]; - ibuf = ED_space_image_acquire_buffer(sima, &lock); + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); if (ibuf) { BLI_strncpy(name, ibuf->name, FILE_MAX); BLI_path_abs(name, BKE_main_blendfile_path(bmain)); @@ -1150,6 +1153,52 @@ static int image_cmp_frame(const void *a, const void *b) return 0; } +static int image_get_udim(const char *filepath, LinkNodePair *udim_tiles) +{ + if (strstr(filepath, "1001") == NULL) { + return 0; + } + + char filename[FILE_MAX], dirname[FILE_MAXDIR]; + BLI_split_dirfile(filepath, dirname, filename, sizeof(dirname), sizeof(filename)); + + bool is_udim = true; + int max_udim = 0; + + unsigned short digits; + char base_head[FILE_MAX], base_tail[FILE_MAX]; + int id = BLI_stringdec(filename, base_head, base_tail, &digits); + if (id == 1001) { + struct direntry *dir; + uint totfile = BLI_filelist_dir_contents(dirname, &dir); + for (int i = 0; i < totfile; i++) { + if (!(dir[i].type & S_IFREG)) { + continue; + } + char head[FILE_MAX], tail[FILE_MAX]; + id = BLI_stringdec(dir[i].relname, head, tail, &digits); + + if (digits > 4 || + !(STREQLEN(base_head, head, FILE_MAX)) || + !(STREQLEN(base_tail, tail, FILE_MAX))) { + continue; + } + + if (id < 1001 || id >= 2000) { + is_udim = false; + break; + } + + BLI_linklist_append(udim_tiles, SET_INT_IN_POINTER(id - 1001)); + max_udim = max_ii(max_udim, id); + } + + BLI_filelist_free(dir, totfile); + } + + return is_udim? (max_udim - 1001) : 0; +} + /** * Return the start (offset) and the length of the sequence of continuous frames in the list of frames * @@ -1157,21 +1206,27 @@ static int image_cmp_frame(const void *a, const void *b) * \param ofs: [out] offset the first frame number in the sequence. * \return the number of contiguous frames in the sequence */ -static int image_sequence_get_len(ListBase *frames, int *ofs) +static int image_sequence_get_len(ImageFrameRange *frame_range, int *ofs, LinkNodePair *udim_tiles) { ImageFrame *frame; - BLI_listbase_sort(frames, image_cmp_frame); + BLI_listbase_sort(&frame_range->frames, image_cmp_frame); - frame = frames->first; + frame = frame_range->frames.first; if (frame) { int frame_curr = frame->framenr; (*ofs) = frame_curr; - while (frame && (frame->framenr == frame_curr)) { - frame_curr++; - frame = frame->next; + + if (udim_tiles && (frame_curr == 1001)) { + return 1 + image_get_udim(frame_range->filepath, udim_tiles); + } + else { + while (frame && (frame->framenr == frame_curr)) { + frame_curr++; + frame = frame->next; + } + return frame_curr - (*ofs); } - return frame_curr - (*ofs); } *ofs = 0; return 0; @@ -1179,7 +1234,8 @@ static int image_sequence_get_len(ListBase *frames, int *ofs) static Image *image_open_single( Main *bmain, wmOperator *op, const char *filepath, const char *relbase, - bool is_relative_path, bool use_multiview, int frame_seq_len) + bool is_relative_path, bool use_multiview, int frame_seq_len, + int frame_seq_ofs, LinkNodePair *udim_tiles) { bool exists = false; Image *ima = NULL; @@ -1215,7 +1271,15 @@ static Image *image_open_single( } if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) { - ima->source = IMA_SRC_SEQUENCE; + if (udim_tiles && frame_seq_ofs == 1001) { + ima->source = IMA_SRC_TILED; + for (LinkNode *node = udim_tiles->list; node; node = node->next) { + BKE_image_add_tile(ima, GET_INT_FROM_POINTER(node->link), NULL); + } + } + else { + ima->source = IMA_SRC_SEQUENCE; + } } } @@ -1238,6 +1302,7 @@ static int image_open_exec(bContext *C, wmOperator *op) const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview"); + const bool use_udim = RNA_boolean_get(op->ptr, "use_udim"); if (!op->customdata) image_open_init(C, op); @@ -1254,7 +1319,9 @@ static int image_open_exec(bContext *C, wmOperator *op) image_sequence_get_frame_ranges(op->ptr, &frame_ranges_all); for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range; frame_range = frame_range->next) { int frame_range_ofs; - int frame_range_seq_len = image_sequence_get_len(&frame_range->frames, &frame_range_ofs); + LinkNodePair udim_tiles = {NULL}; + int frame_range_seq_len = image_sequence_get_len(frame_range, &frame_range_ofs, + use_udim? &udim_tiles : NULL); BLI_freelistN(&frame_range->frames); char filepath_range[FILE_MAX]; @@ -1266,7 +1333,10 @@ static int image_open_exec(bContext *C, wmOperator *op) Image *ima_range = image_open_single( bmain, op, filepath_range, BKE_main_blendfile_path(bmain), - is_relative_path, use_multiview, frame_range_seq_len); + is_relative_path, use_multiview, frame_range_seq_len, frame_range_ofs, + use_udim? &udim_tiles : NULL); + + BLI_linklist_free(udim_tiles.list, NULL); /* take the first image */ if ((ima == NULL) && ima_range) { @@ -1278,10 +1348,17 @@ static int image_open_exec(bContext *C, wmOperator *op) BLI_freelistN(&frame_ranges_all); } else { + int sequence_len = 1; + LinkNodePair udim_tiles = {NULL}; + if (use_udim) { + sequence_len = image_get_udim(filepath, &udim_tiles); + } /* for drag & drop etc. */ ima = image_open_single( bmain, op, filepath, BKE_main_blendfile_path(bmain), - is_relative_path, use_multiview, 1); + is_relative_path, use_multiview, 1, sequence_len, &udim_tiles); + + BLI_linklist_free(udim_tiles.list, NULL); } if (ima == NULL) { @@ -1331,7 +1408,7 @@ static int image_open_exec(bContext *C, wmOperator *op) /* initialize because of new image */ if (iuser) { - iuser->frames = frame_seq_len; + iuser->frames = (ima->source == IMA_SRC_SEQUENCE)? frame_seq_len : 1; iuser->sfra = 1; iuser->framenr = 1; if (ima->source == IMA_SRC_MOVIE) { @@ -1464,6 +1541,7 @@ void IMAGE_OT_open(wmOperatorType *ot) RNA_def_boolean(ot->srna, "use_sequence_detection", true, "Detect Sequences", "Automatically detect animated sequences in selected images (based on file names)"); + RNA_def_boolean(ot->srna, "use_udim", true, "Detect UDIMs", "Detect selected UDIM files and load all matching tiles"); } /******************** Match movie length operator ********************/ @@ -1630,7 +1708,7 @@ static int save_image_options_init(Main *bmain, SaveImageOptions *simopts, Space const bool guess_path, const bool save_as_render) { void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); if (ibuf) { Image *ima = sima->image; @@ -1698,6 +1776,11 @@ static int save_image_options_init(Main *bmain, SaveImageOptions *simopts, Space BLI_snprintf(simopts->filepath, sizeof(simopts->filepath), "//%s", ima->id.name + 2); BLI_path_abs(simopts->filepath, is_prev_save ? G.ima : BKE_main_blendfile_path(bmain)); } + + /* append UDIM numbering if not present */ + if (ima->source == IMA_SRC_TILED && (BLI_stringdec(ima->name, NULL, NULL, NULL) != 1001)) { + strncat(simopts->filepath, ".1001", sizeof(simopts->filepath) - 6); + } } /* color management */ @@ -1811,18 +1894,16 @@ static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) * \note ``ima->name`` and ``ibuf->name`` should end up the same. * \note for multiview the first ``ibuf`` is important to get the settings. */ -static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath) +static bool save_image_single(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath, int tile) { Main *bmain = CTX_data_main(C); Image *ima = ED_space_image(sima); void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); Scene *scene; RenderResult *rr = NULL; bool ok = false; - WM_cursor_wait(1); - if (ibuf) { ImBuf *colormanaged_ibuf = NULL; const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id); @@ -2036,11 +2117,49 @@ cleanup: BKE_image_release_renderresult(scene, ima); } - WM_cursor_wait(0); - return ok; } +static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath) +{ + Image *ima = ED_space_image(sima); + + if (ima->source == IMA_SRC_TILED) { + if (BLI_stringdec(simopts->filepath, NULL, NULL, NULL) != 1001) { + BKE_reportf(op->reports, RPT_ERROR, + "When saving a tiled image, the path '%s' must contain the UDIM tag 1001", simopts->filepath); + return false; + } + } + + WM_cursor_wait(1); + + if (!save_image_single(C, sima, op, simopts, do_newpath, 0)) { + WM_cursor_wait(0); + return false; + } + + if (ima->source == IMA_SRC_TILED) { + char filepath[FILE_MAX]; + BLI_strncpy(filepath, simopts->filepath, sizeof(filepath)); + LISTBASE_FOREACH(ImageTile*, tile, &ima->tiles) { + if (tile->tile_number == 0) + continue; + + char head[FILE_MAX], tail[FILE_MAX]; + unsigned short numlen; + BLI_stringdec(filepath, head, tail, &numlen); + BLI_stringenc(simopts->filepath, head, tail, numlen, 1001 + tile->tile_number); + + save_image_single(C, sima, op, simopts, do_newpath, tile->tile_number); + } + BLI_strncpy(simopts->filepath, filepath, sizeof(simopts->filepath)); + } + + WM_cursor_wait(0); + return true; +} + static void image_save_as_free(wmOperator *op) { if (op->customdata) { @@ -2258,7 +2377,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) if (sima->image == NULL) return OPERATOR_CANCELLED; - if (sima->image->source != IMA_SRC_SEQUENCE) { + if (ELEM(sima->image->source, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { BKE_report(op->reports, RPT_ERROR, "Can only save sequence on image sequences"); return OPERATOR_CANCELLED; } @@ -2441,11 +2560,12 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d"); + bool tiled = RNA_boolean_get(op->ptr, "tiled"); if (!alpha) color[3] = 1.0f; - ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d); + ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d, tiled); if (!ima) { image_new_free(op); @@ -2529,6 +2649,9 @@ static void image_new_draw(bContext *UNUSED(C), wmOperator *op) uiItemL(col[0], "", ICON_NONE); uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE); + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "tiled", 0, NULL, ICON_NONE); + #if 0 if (is_multiview) { uiItemL(col[0], "", ICON_NONE); @@ -2577,6 +2700,8 @@ void IMAGE_OT_new(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views"); RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "tiled", 0, "Tiled", "Create a tiled image"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); } #undef IMA_DEF_NAME @@ -2704,7 +2829,7 @@ static bool image_pack_test(bContext *C, wmOperator *op) if (!as_png && BKE_image_has_packedfile(ima)) return 0; - if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { BKE_report(op->reports, RPT_ERROR, "Packing movies or image sequences not supported"); return 0; } @@ -2805,7 +2930,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op) if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; - if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -2833,7 +2958,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; - if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { + if (ELEM(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_TILED)) { BKE_report(op->reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); return OPERATOR_CANCELLED; } @@ -2903,9 +3028,12 @@ static void image_sample_draw(const bContext *C, ARegion *ar, void *arg_info) /* Returns color in linear space, matching ED_space_node_color_sample(). */ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], float r_col[3]) { + float uv[2]; + UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &uv[0], &uv[1]); + int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL); + void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); - float fx, fy; + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); bool ret = false; if (ibuf == NULL) { @@ -2913,12 +3041,10 @@ bool ED_space_image_color_sample(SpaceImage *sima, ARegion *ar, int mval[2], flo return false; } - UI_view2d_region_to_view(&ar->v2d, mval[0], mval[1], &fx, &fy); - - if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { + if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) { const float *fp; unsigned char *cp; - int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); + int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y); CLAMP(x, 0, ibuf->x - 1); CLAMP(y, 0, ibuf->y - 1); @@ -2944,10 +3070,15 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event { SpaceImage *sima = CTX_wm_space_image(C); ARegion *ar = CTX_wm_region(C); + Image *image = ED_space_image(sima); + + float uv[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]); + int tile = BKE_image_get_tile_from_pos(sima->image, uv, uv, NULL); + void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); ImageSampleInfo *info = op->customdata; - float fx, fy; Scene *scene = CTX_data_scene(C); CurveMapping *curve_mapping = scene->view_settings.curve_mapping; @@ -2957,13 +3088,10 @@ static void image_sample_apply(bContext *C, wmOperator *op, const wmEvent *event return; } - UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &fx, &fy); - - if (fx >= 0.0f && fy >= 0.0f && fx < 1.0f && fy < 1.0f) { + if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 1.0f && uv[1] < 1.0f) { const float *fp; unsigned char *cp; - int x = (int)(fx * ibuf->x), y = (int)(fy * ibuf->y); - Image *image = ED_space_image(sima); + int x = (int)(uv[0] * ibuf->x), y = (int)(uv[1] * ibuf->y); CLAMP(x, 0, ibuf->x - 1); CLAMP(y, 0, ibuf->y - 1); @@ -3171,12 +3299,29 @@ static int image_sample_line_exec(bContext *C, wmOperator *op) int x_end = RNA_int_get(op->ptr, "xend"); int y_end = RNA_int_get(op->ptr, "yend"); + float x1f, y1f, x2f, y2f; + UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f); + UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f); + + /* If the image has tiles, shift the positions accordingly. */ + Image *image = ED_space_image(sima); + int tile = 0, ix = 0, iy = 0; + if (image && image->source == IMA_SRC_TILED) { + ix = (int) x1f; + iy = (int) y1f; + CLAMP(ix, 0, 9); + + x1f -= ix; + x2f -= ix; + y1f -= iy; + y2f -= iy; + tile = 10*iy + ix; + } + void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, tile); Histogram *hist = &sima->sample_line_hist; - float x1f, y1f, x2f, y2f; - if (ibuf == NULL) { ED_space_image_release_buffer(sima, ibuf, lock); return OPERATOR_CANCELLED; @@ -3187,13 +3332,12 @@ static int image_sample_line_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - UI_view2d_region_to_view(&ar->v2d, x_start, y_start, &x1f, &y1f); - UI_view2d_region_to_view(&ar->v2d, x_end, y_end, &x2f, &y2f); - hist->co[0][0] = x1f; hist->co[0][1] = y1f; hist->co[1][0] = x2f; hist->co[1][1] = y2f; + hist->draw_offset[0] = ix; + hist->draw_offset[1] = iy; /* enable line drawing */ hist->flag |= HISTO_FLAG_SAMPLELINE; @@ -3807,3 +3951,227 @@ void IMAGE_OT_clear_render_border(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +/* ********************* Add tile operator ****************** */ + +static bool add_tile_poll(bContext *C) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = CTX_data_edit_image(C); + + return (ima && ima->source == IMA_SRC_TILED && (BKE_image_get_tile(ima, sima->curtile) == NULL)); +} + +static int add_tile_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = ED_space_image(sima); + + if (BKE_image_add_tile(ima, sima->curtile, NULL) == NULL) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_add_tile(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add tile"; + ot->description = "Adds a tile to the image"; + ot->idname = "IMAGE_OT_add_tile"; + + /* api callbacks */ + ot->poll = add_tile_poll; + ot->exec = add_tile_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************* Remove tile operator ****************** */ + +static bool remove_tile_poll(bContext *C) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = CTX_data_edit_image(C); + + return (ima && ima->source == IMA_SRC_TILED && BKE_image_get_tile(ima, sima->curtile)); +} + +static int remove_tile_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = ED_space_image(sima); + + ImageTile *tile = BKE_image_get_tile(ima, sima->curtile); + if (!BKE_image_remove_tile(ima, tile)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_remove_tile(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove tile"; + ot->description = "Removes a tile from the image"; + ot->idname = "IMAGE_OT_remove_tile"; + + /* api callbacks */ + ot->poll = remove_tile_poll; + ot->exec = remove_tile_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/* ********************* Fill tile operator ****************** */ + +static bool fill_tile_poll(bContext *C) +{ + Image *ima = CTX_data_edit_image(C); + SpaceImage *sima = CTX_wm_space_image(C); + + return (ima && ima->source == IMA_SRC_TILED && BKE_image_get_tile(ima, sima->curtile)); +} + +static int fill_tile_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = ED_space_image(sima); + + float color[4]; + RNA_float_get_array(op->ptr, "color", color); + int gen_type = RNA_enum_get(op->ptr, "generated_type"); + int width = RNA_int_get(op->ptr, "width"); + int height = RNA_int_get(op->ptr, "height"); + + ImageTile *tile = BKE_image_get_tile(ima, sima->curtile); + if (!BKE_image_fill_tile(ima, tile, width, height, color, gen_type)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); + + return OPERATOR_FINISHED; +} + +static int fill_tile_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = ED_space_image(sima); + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + if (ibuf) { + RNA_int_set(op->ptr, "width", ibuf->x); + RNA_int_set(op->ptr, "height", ibuf->y); + BKE_image_release_ibuf(ima, ibuf, NULL); + } + return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); +} + +static void fill_tile_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *split, *col[2]; + uiLayout *layout = op->layout; + PointerRNA ptr; + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + /* copy of WM_operator_props_dialog_popup() layout */ + + split = uiLayoutSplit(layout, 0.5f, false); + col[0] = uiLayoutColumn(split, false); + col[1] = uiLayoutColumn(split, false); + + uiItemL(col[0], IFACE_("Color"), ICON_NONE); + uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Width"), ICON_NONE); + uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Height"), ICON_NONE); + uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); + uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE); +} + +void IMAGE_OT_fill_tile(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Fill tile"; + ot->description = "Fill the current tile with a generated image"; + ot->idname = "IMAGE_OT_fill_tile"; + + /* api callbacks */ + ot->poll = fill_tile_poll; + ot->exec = fill_tile_exec; + ot->invoke = fill_tile_invoke; + ot->ui = fill_tile_draw; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + static float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + prop = RNA_def_float_color(ot->srna, "color", 4, NULL, 0.0f, FLT_MAX, "Color", "Default fill color", 0.0f, 1.0f); + RNA_def_property_subtype(prop, PROP_COLOR_GAMMA); + RNA_def_property_float_array_default(prop, default_color); + RNA_def_enum(ot->srna, "generated_type", rna_enum_image_generated_type_items, IMA_GENTYPE_BLANK, + "Generated Type", "Fill the image with a grid for UV map testing"); + prop = RNA_def_int(ot->srna, "width", 1024, 1, INT_MAX, "Width", "Image width", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); + prop = RNA_def_int(ot->srna, "height", 1024, 1, INT_MAX, "Height", "Image height", 1, 16384); + RNA_def_property_subtype(prop, PROP_PIXEL); +} + +/* ********************* Select tile operator ****************** */ + +static bool image_select_tile_poll(bContext *C) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = CTX_data_edit_image(C); + + if (ima) { + return (ima->source == IMA_SRC_TILED); + } + else { + return (sima->tile_grid_shape[0] > 1) || (sima->tile_grid_shape[1] > 1); + } +} + +static int image_select_tile_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + SpaceImage *sima = CTX_wm_space_image(C); + ARegion *ar = CTX_wm_region(C); + + float uv[2]; + UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &uv[0], &uv[1]); + + if (uv[0] >= 0.0f && uv[1] >= 0.0f && uv[0] < 10.0f) { + int tx = (int) uv[0]; + int ty = (int) uv[1]; + + sima->curtile = 10*ty + tx; + + WM_event_add_notifier(C, NC_WINDOW, NULL); + return OPERATOR_FINISHED; + } + + return OPERATOR_CANCELLED; +} + +void IMAGE_OT_select_tile(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select tile"; + ot->idname = "IMAGE_OT_select_tile"; + ot->description = "Use mouse to select a tile of the image"; + + /* api callbacks */ + ot->invoke = image_select_tile_invoke; + ot->poll = image_select_tile_poll; +}
\ No newline at end of file diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index d87bfbe00ce..b35e05a43a3 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -183,6 +183,9 @@ static SpaceLink *image_new(const ScrArea *UNUSED(area), const Scene *UNUSED(sce scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; + simage->tile_grid_shape[0] = 1; + simage->tile_grid_shape[1] = 1; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for image"); @@ -289,6 +292,11 @@ static void image_operatortypes(void) WM_operatortype_append(IMAGE_OT_read_viewlayers); WM_operatortype_append(IMAGE_OT_render_border); WM_operatortype_append(IMAGE_OT_clear_render_border); + + WM_operatortype_append(IMAGE_OT_add_tile); + WM_operatortype_append(IMAGE_OT_remove_tile); + WM_operatortype_append(IMAGE_OT_fill_tile); + WM_operatortype_append(IMAGE_OT_select_tile); } static void image_keymap(struct wmKeyConfig *keyconf) @@ -355,6 +363,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "IMAGE_OT_sample", ACTIONMOUSE, KM_PRESS, 0, 0); RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_curves_point_set", ACTIONMOUSE, KM_PRESS, KM_CTRL, 0)->ptr, "point", 0); RNA_enum_set(WM_keymap_add_item(keymap, "IMAGE_OT_curves_point_set", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0)->ptr, "point", 1); + WM_keymap_add_item(keymap, "IMAGE_OT_select_tile", ACTIONMOUSE, KM_PRESS, KM_ALT, 0); /* toggle editmode is handy to have while UV unwrapping */ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_mode_set", TABKEY, KM_PRESS, 0, 0); @@ -929,7 +938,8 @@ static void image_tools_region_draw(const bContext *C, ARegion *ar) SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); void *lock; - ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + /* TODO(lukas): Support tiles in scopes? */ + ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); /* XXX performance regression if name of scopes category changes! */ PanelCategoryStack *category = UI_panel_category_active_find(ar, "Scopes"); diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 9fea9eaf4e1..d8132e7c088 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -220,7 +220,7 @@ GPUNodeLink *GPU_attribute(CustomDataType type, const char *name); GPUNodeLink *GPU_uniform(float *num); GPUNodeLink *GPU_dynamic_uniform(float *num, GPUDynamicType dynamictype, void *data); GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype); -GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data); +GPUNodeLink *GPU_image(struct Image *ima, struct ImageUser *iuser, bool is_data, int tile); GPUNodeLink *GPU_cube_map(struct Image *ima, struct ImageUser *iuser, bool is_data); GPUNodeLink *GPU_image_preview(struct PreviewImage *prv); GPUNodeLink *GPU_texture(int size, float *pixels); diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index 3b9d2e08769..3a1a7190cc5 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -515,17 +515,19 @@ const char *GPU_builtin_name(GPUBuiltin builtin) } /* assign only one texid per buffer to avoid sampling the same texture twice */ -static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key) +static void codegen_set_texid(GHash *bindhash, GPUInput *input, int *texid, void *key1, int key2) { - if (BLI_ghash_haskey(bindhash, key)) { + GHashPair pair = {key1, SET_INT_IN_POINTER(key2 << 16)}; + if (BLI_ghash_haskey(bindhash, &pair)) { /* Reuse existing texid */ - input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, key)); + input->texid = GET_INT_FROM_POINTER(BLI_ghash_lookup(bindhash, &pair)); } else { /* Allocate new texid */ input->texid = *texid; (*texid)++; input->bindtex = true; + void *key = BLI_ghashutil_pairalloc(key1, SET_INT_IN_POINTER(key2)); BLI_ghash_insert(bindhash, key, SET_INT_IN_POINTER(input->texid)); } } @@ -538,7 +540,7 @@ static void codegen_set_unique_ids(ListBase *nodes) GPUOutput *output; int id = 1, texid = 0; - bindhash = BLI_ghash_ptr_new("codegen_set_unique_ids1 gh"); + bindhash = BLI_ghash_pair_new("codegen_set_unique_ids1 gh"); definehash = BLI_ghash_ptr_new("codegen_set_unique_ids2 gh"); for (node = nodes->first; node; node = node->next) { @@ -556,19 +558,19 @@ static void codegen_set_unique_ids(ListBase *nodes) * the same texture twice */ if (input->link) { /* input is texture from buffer */ - codegen_set_texid(bindhash, input, &texid, input->link); + codegen_set_texid(bindhash, input, &texid, input->link, 0); } else if (input->ima) { /* input is texture from image */ - codegen_set_texid(bindhash, input, &texid, input->ima); + codegen_set_texid(bindhash, input, &texid, input->ima, input->image_tile); } else if (input->prv) { /* input is texture from preview render */ - codegen_set_texid(bindhash, input, &texid, input->prv); + codegen_set_texid(bindhash, input, &texid, input->prv, 0); } else if (input->tex) { /* input is user created texture, check tex pointer */ - codegen_set_texid(bindhash, input, &texid, input->tex); + codegen_set_texid(bindhash, input, &texid, input->tex, 0); } /* make sure this pixel is defined exactly once */ @@ -594,7 +596,7 @@ static void codegen_set_unique_ids(ListBase *nodes) output->id = id++; } - BLI_ghash_free(bindhash, NULL, NULL); + BLI_ghash_free(bindhash, BLI_ghashutil_pairfree, NULL); BLI_ghash_free(definehash, NULL, NULL); } @@ -1355,6 +1357,7 @@ static void gpu_node_input_link(GPUNode *node, GPUNodeLink *link, const GPUType input->ima = link->ptr1; input->iuser = link->ptr2; input->image_isdata = link->image_isdata; + input->image_tile = link->val1; input->textarget = GL_TEXTURE_2D; input->textype = GPU_TEX2D; } @@ -1652,13 +1655,14 @@ GPUNodeLink *GPU_uniform_buffer(float *num, GPUType gputype) return link; } -GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data) +GPUNodeLink *GPU_image(Image *ima, ImageUser *iuser, bool is_data, int tile) { GPUNodeLink *link = GPU_node_link_create(); link->image = GPU_NODE_LINK_IMAGE_BLENDER; link->ptr1 = ima; link->ptr2 = iuser; + link->val1 = tile; link->image_isdata = is_data; return link; diff --git a/source/blender/gpu/intern/gpu_codegen.h b/source/blender/gpu/intern/gpu_codegen.h index 278843fc948..0523332adc4 100644 --- a/source/blender/gpu/intern/gpu_codegen.h +++ b/source/blender/gpu/intern/gpu_codegen.h @@ -93,6 +93,7 @@ struct GPUNodeLink { int texturesize; void *ptr1, *ptr2; + int val1; bool dynamic; GPUDynamicType dynamictype; @@ -137,6 +138,7 @@ typedef struct GPUInput { struct Image *ima; /* image */ struct ImageUser *iuser; /* image user */ + int image_tile; /* image tile */ struct PreviewImage *prv; /* preview images & icons */ bool image_isdata; /* image does not contain color data */ float *dynamicvec; /* vector data in case it is dynamic */ diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 965caba0955..6e3ad4127eb 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -224,16 +224,6 @@ float GPU_get_anisotropic(void) /* Set OpenGL state for an MTFace */ -static GPUTexture **gpu_get_image_gputexture(Image *ima, GLenum textarget) -{ - if (textarget == GL_TEXTURE_2D) - return &ima->gputexture[TEXTARGET_TEXTURE_2D]; - else if (textarget == GL_TEXTURE_CUBE_MAP) - return &ima->gputexture[TEXTARGET_TEXTURE_CUBE_MAP]; - - return NULL; -} - typedef struct VerifyThreadData { ImBuf *ibuf; float *srgb_frect; @@ -301,18 +291,22 @@ GPUTexture *GPU_texture_from_blender( return NULL; } + int gputex_type = (textarget == GL_TEXTURE_CUBE_MAP)? TEXTARGET_TEXTURE_CUBE_MAP : TEXTARGET_TEXTURE_2D; + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + /* Test if we already have a texture. */ - GPUTexture **tex = gpu_get_image_gputexture(ima, textarget); - if (*tex) { - return *tex; + GPUTexture *tex = tile->gputexture[gputex_type]; + if (tex) { + return tex; } /* Check if we have a valid image. If not, we return a dummy * texture with zero bindcode so we don't keep trying. */ unsigned int bindcode = 0; - if (ima->ok == 0) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; + if (tile->ok == 0) { + tex = GPU_texture_from_bindcode(textarget, bindcode); + tile->gputexture[gputex_type] = tex; + return tex; } /* currently, tpage refresh is used by ima sequences */ @@ -324,8 +318,9 @@ GPUTexture *GPU_texture_from_blender( /* check if we have a valid image buffer */ ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (ibuf == NULL) { - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; + tex = GPU_texture_from_bindcode(textarget, bindcode); + tile->gputexture[gputex_type] = tex; + return tex; } /* flag to determine whether deep format is used */ @@ -393,8 +388,9 @@ GPUTexture *GPU_texture_from_blender( BKE_image_release_ibuf(ima, ibuf, NULL); - *tex = GPU_texture_from_bindcode(textarget, bindcode); - return *tex; + tex = GPU_texture_from_bindcode(textarget, bindcode); + tile->gputexture[gputex_type] = tex; + return tex; } static void **gpu_gen_cube_map(unsigned int *rect, float *frect, int rectw, int recth, bool use_high_bit_depth) @@ -703,44 +699,43 @@ void GPU_paint_set_mipmap(Main *bmain, bool mipmap) if (mipmap) { for (Image *ima = bmain->image.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { + bool has_gputexture = false; + LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) { if (ima->tpageflag & IMA_MIPMAP_COMPLETE) { - if (ima->gputexture[TEXTARGET_TEXTURE_2D]) { - GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0); + GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; + if (tex) { + GPU_texture_bind(tex, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, gpu_get_mipmap_filter(0)); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]); + GPU_texture_unbind(tex); } } - else - GPU_free_image(ima); } - else - ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; + if (!has_gputexture) GPU_free_image(ima); } - } else { for (Image *ima = bmain->image.first; ima; ima = ima->id.next) { - if (BKE_image_has_opengl_texture(ima)) { - if (ima->gputexture[TEXTARGET_TEXTURE_2D]) { - GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0); + LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) { + GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; + if (tex) { + GPU_texture_bind(tex, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, gpu_get_mipmap_filter(1)); - GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]); + GPU_texture_unbind(tex); } } - else - ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; } } } /* check if image has been downscaled and do scaled partial update */ -static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, int y, int w, int h) +static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, ImageTile *tile, float *frect, int x, int y, int w, int h) { if (is_over_resolution_limit(GL_TEXTURE_2D, ibuf->x, ibuf->y)) { + GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; + int x_limit = smaller_power_of_2_limit(ibuf->x); int y_limit = smaller_power_of_2_limit(ibuf->y); @@ -760,7 +755,7 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, if (rectw + x > x_limit) rectw--; if (recth + y > y_limit) recth--; - GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0); + GPU_texture_bind(tex, 0); /* float rectangles are already continuous in memory so we can use IMB_scaleImBuf */ if (frect) { @@ -802,7 +797,7 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; } - GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]); + GPU_texture_unbind(tex); return true; } @@ -813,9 +808,11 @@ static bool gpu_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); + ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); + GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; if ((!GTS.gpu_mipmap && GPU_get_mipmap()) || - (ima->gputexture[TEXTARGET_TEXTURE_2D] == NULL) || + (tex == NULL) || (ibuf == NULL) || (w == 0) || (h == 0)) { @@ -833,13 +830,13 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i bool is_data = (ima->tpageflag & IMA_GLBIND_IS_DATA) != 0; IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h, is_data); - if (gpu_check_scaled_image(ibuf, ima, buffer, x, y, w, h)) { + if (gpu_check_scaled_image(ibuf, ima, tile, buffer, x, y, w, h)) { MEM_freeN(buffer); BKE_image_release_ibuf(ima, ibuf, NULL); return; } - GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0); + GPU_texture_bind(tex, 0); glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_FLOAT, buffer); MEM_freeN(buffer); @@ -853,18 +850,18 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; } - GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]); + GPU_texture_unbind(tex); BKE_image_release_ibuf(ima, ibuf, NULL); return; } - if (gpu_check_scaled_image(ibuf, ima, NULL, x, y, w, h)) { + if (gpu_check_scaled_image(ibuf, ima, tile, NULL, x, y, w, h)) { BKE_image_release_ibuf(ima, ibuf, NULL); return; } - GPU_texture_bind(ima->gputexture[TEXTARGET_TEXTURE_2D], 0); + GPU_texture_bind(tex, 0); glGetIntegerv(GL_UNPACK_ROW_LENGTH, &row_length); glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels); @@ -890,7 +887,7 @@ void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, i ima->tpageflag &= ~IMA_MIPMAP_COMPLETE; } - GPU_texture_unbind(ima->gputexture[TEXTARGET_TEXTURE_2D]); + GPU_texture_unbind(tex); } BKE_image_release_ibuf(ima, ibuf, NULL); @@ -1077,11 +1074,14 @@ void GPU_free_image(Image *ima) return; } - for (int i = 0; i < TEXTARGET_COUNT; i++) { - /* free glsl image binding */ - if (ima->gputexture[i]) { - GPU_texture_free(ima->gputexture[i]); - ima->gputexture[i] = NULL; + LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) { + for (int i = 0; i < TEXTARGET_COUNT; i++) { + /* free glsl image binding */ + GPUTexture *tex = tile->gputexture[i]; + if (tex) { + GPU_texture_free(tex); + tile->gputexture[i] = NULL; + } } } diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl index 464851bae21..fe57394f9af 100644 --- a/source/blender/gpu/shaders/gpu_shader_material.glsl +++ b/source/blender/gpu/shaders/gpu_shader_material.glsl @@ -1879,6 +1879,67 @@ void node_tex_image_smart(vec3 co, sampler2D ima, out vec4 color, out float alph node_tex_image_cubic(co, ima, color, alpha); } +void node_tex_image_tiled_map(vec3 co, out vec4 color, out vec3 map) +{ + float tx = floor(co.x); + float ty = floor(co.y); + + if (tx < 0 || ty < 0 || tx >= 10) + map = vec3(0, 0, -1); + else + map = vec3(co.x - tx, co.y - ty, 10*ty + tx); + + color = vec4(1.0, 0.0, 1.0, 1.0); +} + +void node_tex_image_tile_linear(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) +{ + if (map.z == tile_id) { + vec3 co = map.xyy; + node_tex_image_linear(co, ima, color, alpha); + } + else { + color = in_color; + alpha = color.a; + } +} + +void node_tex_image_tile_nearest(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) +{ + if (map.z == tile_id) { + vec3 co = map.xyy; + node_tex_image_nearest(co, ima, color, alpha); + } + else { + color = in_color; + alpha = color.a; + } +} + +void node_tex_image_tile_cubic(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) +{ + if (map.z == tile_id) { + vec3 co = map.xyy; + node_tex_image_cubic(co, ima, color, alpha); + } + else { + color = in_color; + alpha = color.a; + } +} + +void node_tex_image_tile_smart(vec3 map, float tile_id, sampler2D ima, vec4 in_color, out vec4 color, out float alpha) +{ + if (map.z == tile_id) { + vec3 co = map.xyy; + node_tex_image_smart(co, ima, color, alpha); + } + else { + color = in_color; + alpha = color.a; + } +} + void tex_box_sample_linear(vec3 texco, vec3 N, sampler2D ima, diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index 0c72d1b4f01..756210a306e 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -60,6 +60,7 @@ void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGe void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); bool IMB_moviecache_put_if_possible(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey); +void IMB_moviecache_remove(struct MovieCache *cache, void *userkey); bool IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index daf062f5499..9ad9f6b9b41 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -415,6 +415,14 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu return result; } +void IMB_moviecache_remove(MovieCache *cache, void *userkey) +{ + MovieCacheKey key; + key.cache_owner = cache; + key.userkey = userkey; + BLI_ghash_remove(cache->hash, &key, moviecache_keyfree, moviecache_valfree); +} + ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) { MovieCacheKey key; diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index 8ed38b0b05d..15db4100c75 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -132,6 +132,7 @@ typedef struct Histogram { /* sample line only */ /* image coords src -> dst */ float co[2][2]; + float draw_offset[2]; } Histogram; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index a7ec121efda..7a2d43a173f 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -58,6 +58,9 @@ typedef struct ImageUser { short pass; short pad; + int tile; /* current tile (for internal use) */ + int pad2; + short multi_index, view, layer; /* listbase indices, for menu browsing or retrieve buffer */ short flag; } ImageUser; @@ -85,6 +88,21 @@ typedef struct RenderSlot { struct RenderResult *render; } RenderSlot; +enum { + TEXTARGET_TEXTURE_2D = 0, + TEXTARGET_TEXTURE_CUBE_MAP = 1, + TEXTARGET_COUNT = 2 +}; + +typedef struct ImageTile { + struct ImageTile *next, *prev; + struct GPUTexture *gputexture[2]; /* TEXTARGET_COUNT */ + char ok; + char pad[3]; + int tile_number; + char label[64]; +} ImageTile; + /* iuser->flag */ #define IMA_ANIM_ALWAYS 1 #define IMA_ANIM_REFRESHED 2 @@ -92,11 +110,6 @@ typedef struct RenderSlot { #define IMA_NEED_FRAME_RECALC 8 #define IMA_SHOW_STEREO 16 -enum { - TEXTARGET_TEXTURE_2D = 0, - TEXTARGET_TEXTURE_CUBE_MAP = 1, - TEXTARGET_COUNT = 2 -}; typedef struct Image { ID id; @@ -104,7 +117,6 @@ typedef struct Image { char name[1024]; /* file path, 1024 = FILE_MAX */ struct MovieCache *cache; /* not written in file */ - struct GPUTexture *gputexture[2]; /* not written in file 2 = TEXTARGET_COUNT */ /* sources from: */ ListBase anims; @@ -119,16 +131,17 @@ typedef struct Image { /* texture page */ short tpageflag; - short pad2; - unsigned int pad3; + short pad3; + + int lastused; + + ListBase tiles; struct PackedFile *packedfile DNA_DEPRECATED; /* deprecated */ struct ListBase packedfiles; struct PreviewImage *preview; - int lastused; - short ok; - short pad4[3]; + int pad2; /* for generated images */ int gen_x, gen_y; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index d6d043b03ae..0af0ec5e105 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -922,6 +922,8 @@ typedef struct SpaceImage { int flag; + int tile_grid_shape[2]; + MaskSpaceInfo mask_info; } SpaceImage; diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 1759eb812e6..36aed870f04 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -59,6 +59,7 @@ static const EnumPropertyItem image_source_items[] = { {IMA_SRC_MOVIE, "MOVIE", 0, "Movie", "Movie file"}, {IMA_SRC_GENERATED, "GENERATED", 0, "Generated", "Generated image"}, {IMA_SRC_VIEWER, "VIEWER", 0, "Viewer", "Compositing node viewer"}, + {IMA_SRC_TILED, "TILED", 0, "Tiled", "Tiled image texture"}, {0, NULL, 0, NULL, NULL} }; @@ -190,6 +191,7 @@ static const EnumPropertyItem *rna_Image_source_itemf(bContext *UNUSED(C), Point RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_SEQUENCE); RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_MOVIE); RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_GENERATED); + RNA_enum_items_add_value(&item, &totitem, image_source_items, IMA_SRC_TILED); } RNA_enum_item_end(&item, &totitem); @@ -279,13 +281,6 @@ static void rna_Image_resolution_set(PointerRNA *ptr, const float *values) BKE_image_release_ibuf(im, ibuf, lock); } -static int rna_Image_bindcode_get(PointerRNA *ptr) -{ - Image *ima = (Image *)ptr->data; - GPUTexture *tex = ima->gputexture[TEXTARGET_TEXTURE_2D]; - return (tex) ? GPU_texture_opengl_bindcode(tex) : 0; -} - static int rna_Image_depth_get(PointerRNA *ptr) { Image *im = (Image *)ptr->data; @@ -491,6 +486,23 @@ static void rna_render_slots_active_index_range(PointerRNA *ptr, int *min, int * *max = max_ii(0, BLI_listbase_count(&image->renderslots) - 1); } +static ImageTile *rna_ImageTile_new(Image *image, int tile_number, const char *label) +{ + ImageTile *tile = BKE_image_add_tile(image, tile_number, label); + + WM_main_add_notifier(NC_IMAGE | ND_DRAW, NULL); + + return tile; +} + +static void rna_ImageTile_remove(ID *id, ImageTile *tile) +{ + Image *image = (Image *)id; + BKE_image_remove_tile(image, tile); + + WM_main_add_notifier(NC_IMAGE | ND_DRAW, NULL); +} + #else static void rna_def_imageuser(BlenderRNA *brna) @@ -555,6 +567,11 @@ static void rna_def_imageuser(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "view"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "View", "View in multilayer image"); + + prop = RNA_def_property(srna, "tile", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "tile"); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Tile", "Tile in tiled image"); } /* image.packed_files */ @@ -632,6 +649,56 @@ static void rna_def_render_slots(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_return(func, parm); } +static void rna_def_image_tile(BlenderRNA *brna) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *prop; + srna = RNA_def_struct(brna, "ImageTile", NULL); + RNA_def_struct_ui_text(srna, "Image Tile", "Properties of the image tile"); + + prop = RNA_def_property(srna, "label", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "label"); + RNA_def_property_ui_text(prop, "Label", "Tile label"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); + + prop = RNA_def_property(srna, "tile_number", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "tile_number"); + RNA_def_property_ui_text(prop, "Tile Number", "Number of the position that this tile covers"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + func = RNA_def_function(srna, "remove", "rna_ImageTile_remove"); + RNA_def_function_ui_description(func, "Remove this tile from the image"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); +} + +static void rna_def_image_tiles(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "ImageTiles"); + srna = RNA_def_struct(brna, "ImageTiles", NULL); + RNA_def_struct_sdna(srna, "Image"); + RNA_def_struct_ui_text(srna, "Image Tiles", "Collection of the image tiles"); + + func = RNA_def_function(srna, "new", "rna_ImageTile_new"); + RNA_def_function_ui_description(func, "Add a tile to the image"); + parm = RNA_def_int(func, "tile_number", 1, 1, INT_MAX, "", "Number of the newly created tile", 1, 100); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_string(func, "label", NULL, 0, "", "Optional label for the tile"); + parm = RNA_def_pointer(func, "result", "ImageTile", "", "Newly created image tile"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "get", "BKE_image_get_tile"); + RNA_def_function_ui_description(func, "Get a tile based on its tile number"); + parm = RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "", "Number of the tile", 1, 100); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "result", "ImageTile", "", "The tile"); + RNA_def_function_return(func, parm); +} + static void rna_def_image(BlenderRNA *brna) { StructRNA *srna; @@ -782,18 +849,18 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Display Aspect", "Display Aspect for this image, does not affect rendering"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); - prop = RNA_def_property(srna, "bindcode", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_funcs(prop, "rna_Image_bindcode_get", NULL, NULL); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Bindcode", "OpenGL bindcode"); - RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); - prop = RNA_def_property(srna, "render_slots", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "RenderSlot"); RNA_def_property_collection_sdna(prop, NULL, "renderslots", NULL); RNA_def_property_ui_text(prop, "Render Slots", "Render slots of the image"); rna_def_render_slots(brna, prop); + prop = RNA_def_property(srna, "tiles", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "ImageTile"); + RNA_def_property_collection_sdna(prop, NULL, "tiles", NULL); + RNA_def_property_ui_text(prop, "Image Tiles", "Tiles of the image"); + rna_def_image_tiles(brna, prop); + /* * Image.has_data and Image.depth are temporary, * Update import_obj.py when they are replaced (Arystan) @@ -875,6 +942,7 @@ static void rna_def_image(BlenderRNA *brna) void RNA_def_image(BlenderRNA *brna) { rna_def_render_slot(brna); + rna_def_image_tile(brna); rna_def_image(brna); rna_def_imageuser(brna); rna_def_image_packed_files(brna); diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index c7a3e103281..7030ec865ef 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -181,8 +181,8 @@ static void rna_Image_unpack(Image *image, Main *bmain, ReportList *reports, int if (!BKE_image_has_packedfile(image)) { BKE_report(reports, RPT_ERROR, "Image not packed"); } - else if (BKE_image_is_animated(image)) { - BKE_report(reports, RPT_ERROR, "Unpacking movies or image sequences not supported"); + else if (BKE_image_has_multiple_ibufs(image)) { + BKE_report(reports, RPT_ERROR, "Unpacking movies, image sequences and tiled images not supported"); return; } else { @@ -220,9 +220,10 @@ static void rna_Image_scale(Image *image, ReportList *reports, int width, int he } } -static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int filter, int mag) +static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int tile_number, int filter, int mag) { - GPUTexture *tex = image->gputexture[TEXTARGET_TEXTURE_2D]; + ImageTile *tile = BKE_image_get_tile(image, tile_number); + GPUTexture *tex = tile->gputexture[TEXTARGET_TEXTURE_2D]; int error = GL_NO_ERROR; if (tex) @@ -231,6 +232,7 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f ImageUser iuser = {NULL}; iuser.framenr = frame; iuser.ok = true; + iuser.tile = tile_number; void *lock; ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); @@ -259,7 +261,7 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f glDeleteTextures(1, (GLuint *)&bindcode); } else { - image->gputexture[TEXTARGET_TEXTURE_2D] = GPU_texture_from_bindcode(GL_TEXTURE_2D, bindcode); + tile->gputexture[TEXTARGET_TEXTURE_2D] = GPU_texture_from_bindcode(GL_TEXTURE_2D, bindcode); } BKE_image_release_ibuf(image, ibuf, lock); @@ -267,14 +269,15 @@ static int rna_Image_gl_load(Image *image, ReportList *reports, int frame, int f return error; } -static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int filter, int mag) +static int rna_Image_gl_touch(Image *image, ReportList *reports, int frame, int tile_number, int filter, int mag) { int error = GL_NO_ERROR; BKE_image_tag_time(image); - if (image->gputexture[TEXTARGET_TEXTURE_2D] == NULL) - error = rna_Image_gl_load(image, reports, frame, filter, mag); + ImageTile *tile = BKE_image_get_tile(image, tile_number); + if (tile->gputexture[TEXTARGET_TEXTURE_2D] == NULL) + error = rna_Image_gl_load(image, reports, frame, tile_number, filter, mag); return error; } @@ -359,6 +362,8 @@ void RNA_api_image(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_int(func, "frame", 0, 0, INT_MAX, "Frame", "Frame of image sequence or movie", 0, INT_MAX); + RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile", + "Tile of a tiled image", 0, INT_MAX); RNA_def_int(func, "filter", GL_LINEAR_MIPMAP_NEAREST, -INT_MAX, INT_MAX, "Filter", "The texture minifying function to use if the image wasn't loaded", -INT_MAX, INT_MAX); RNA_def_int(func, "mag", GL_LINEAR, -INT_MAX, INT_MAX, "Magnification", @@ -372,6 +377,8 @@ void RNA_api_image(StructRNA *srna) RNA_def_function_flag(func, FUNC_USE_REPORTS); RNA_def_int(func, "frame", 0, 0, INT_MAX, "Frame", "Frame of image sequence or movie", 0, INT_MAX); + RNA_def_int(func, "tile_number", 0, 0, INT_MAX, "Tile", + "Tile of a tiled image", 0, INT_MAX); RNA_def_int(func, "filter", GL_LINEAR_MIPMAP_NEAREST, -INT_MAX, INT_MAX, "Filter", "The texture minifying function", -INT_MAX, INT_MAX); RNA_def_int(func, "mag", GL_LINEAR, -INT_MAX, INT_MAX, "Magnification", diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index febe74f63c9..974503bfaab 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -330,13 +330,13 @@ static Lamp *rna_Main_lights_new(Main *bmain, const char *name, int type) return lamp; } -static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, bool alpha, bool float_buffer, bool stereo3d) +static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, bool alpha, bool float_buffer, bool stereo3d, bool tiled) { char safe_name[MAX_ID_NAME - 2]; rna_idname_validate(name, safe_name); float color[4] = {0.0, 0.0, 0.0, 1.0}; - Image *image = BKE_image_add_generated(bmain, width, height, safe_name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d); + Image *image = BKE_image_add_generated(bmain, width, height, safe_name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d, tiled); id_us_min(&image->id); return image; } @@ -1004,6 +1004,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_boolean(func, "alpha", 0, "Alpha", "Use alpha channel"); RNA_def_boolean(func, "float_buffer", 0, "Float Buffer", "Create an image with floating point color"); RNA_def_boolean(func, "stereo3d", 0, "Stereo 3D", "Create left and right views"); + RNA_def_boolean(func, "tiled", 0, "Tiled", "Create a tiled image"); /* return type */ parm = RNA_def_pointer(func, "image", "Image", "", "New image data-block"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 2f009238851..1bdaa569604 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -1069,7 +1069,7 @@ static const EnumPropertyItem *rna_SpaceImageEditor_draw_channels_itemf( void *lock; int zbuf, alpha, totitem = 0; - ibuf = ED_space_image_acquire_buffer(sima, &lock); + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); alpha = ibuf && (ibuf->channels == 4); zbuf = ibuf && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels == 1)); @@ -1174,7 +1174,8 @@ static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct Pointe ImBuf *ibuf; void *lock; - ibuf = ED_space_image_acquire_buffer(sima, &lock); + /* TODO(lukas): Support tiles in scopes? */ + ibuf = ED_space_image_acquire_buffer(sima, &lock, 0); if (ibuf) { ED_space_image_scopes_update(C, sima, ibuf, true); WM_main_add_notifier(NC_IMAGE, sima->image); @@ -2235,6 +2236,14 @@ static void rna_def_space_image_uv(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw Modified Edges", "Draw edges after modifiers are applied"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "tile_grid_shape", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "tile_grid_shape"); + RNA_def_property_array(prop, 2); + RNA_def_property_int_default(prop, 1); + RNA_def_property_range(prop, 1, 10); + RNA_def_property_ui_text(prop, "Tile Grid Shape", "How many tiles will be shown in the background"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "show_other_objects", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_DRAW_OTHER); RNA_def_property_ui_text(prop, "Draw Other Objects", "Draw other selected objects that share the same image"); @@ -3376,6 +3385,12 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_float_funcs(prop, "rna_SpaceImageEditor_zoom_get", NULL, NULL); RNA_def_property_ui_text(prop, "Zoom", "Zoom factor"); + prop = RNA_def_property(srna, "current_tile", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "curtile"); + RNA_def_property_range(prop, 0, 1000); + RNA_def_property_ui_text(prop, "Current Tile", "The currently selected tile"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + /* image draw */ prop = RNA_def_property(srna, "show_repeat", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_DRAW_TILE); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c index 41860df5670..47001c5ffdc 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_environment.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_environment.c @@ -75,9 +75,9 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat, bNode *node, bNodeE node_shader_gpu_tex_mapping(mat, node, in, out); if (tex->projection == SHD_PROJ_EQUIRECTANGULAR) - GPU_stack_link(mat, node, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata)); + GPU_stack_link(mat, node, "node_tex_environment_equirectangular", in, out, GPU_image(ima, iuser, isdata, 0)); else - GPU_stack_link(mat, node, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata)); + GPU_stack_link(mat, node, "node_tex_environment_mirror_ball", in, out, GPU_image(ima, iuser, isdata, 0)); ImBuf *ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (ibuf && (ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA) == 0 && diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.c index 9782df2638f..d68a67ab11d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.c @@ -67,6 +67,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat "tex_box_sample_cubic", "tex_box_sample_smart" }; + static const char *names_tiled[] = { + "node_tex_image_tile_linear", + "node_tex_image_tile_nearest", + "node_tex_image_tile_cubic", + "node_tex_image_tile_smart" + }; Image *ima = (Image *)node->id; ImageUser *iuser = NULL; @@ -98,46 +104,59 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, bNode *node, bNodeExecDat node_shader_gpu_tex_mapping(mat, node, in, out); - switch (tex->projection) { - case SHD_PROJ_FLAT: - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata)); - break; - case SHD_PROJ_BOX: - GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), - GPU_builtin(GPU_INVERSE_VIEW_MATRIX), - &norm); - GPU_link(mat, "direction_transform_m4v3", norm, - GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), - &norm); - GPU_link(mat, gpu_node_name, in[0].link, - norm, - GPU_image(ima, iuser, isdata), - &col1, - &col2, - &col3); - if (do_color_correction) { - GPU_link(mat, "srgb_to_linearrgb", col1, &col1); - GPU_link(mat, "srgb_to_linearrgb", col2, &col2); - GPU_link(mat, "srgb_to_linearrgb", col3, &col3); - } - GPU_link(mat, "node_tex_image_box", in[0].link, - norm, - col1, col2, col3, - GPU_image(ima, iuser, isdata), - GPU_uniform(&blend), - &out[0].link, - &out[1].link); - break; - case SHD_PROJ_SPHERE: - GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); - GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link); - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata)); - break; - case SHD_PROJ_TUBE: - GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); - GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link); - GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata)); - break; + if (ima->source == IMA_SRC_TILED) { + GPUNodeLink *map; + GPU_link(mat, "node_tex_image_tiled_map", in[0].link, &out[0].link, &map); + LISTBASE_FOREACH(ImageTile *, tile, &ima->tiles) { + float tile_number = tile->tile_number; + GPU_link(mat, names_tiled[tex->interpolation], + map, GPU_uniform(&tile_number), + GPU_image(ima, iuser, isdata, tile->tile_number), + out[0].link, &out[0].link, &out[1].link); + } + } + else { + switch (tex->projection) { + case SHD_PROJ_FLAT: + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0)); + break; + case SHD_PROJ_BOX: + GPU_link(mat, "direction_transform_m4v3", GPU_builtin(GPU_VIEW_NORMAL), + GPU_builtin(GPU_INVERSE_VIEW_MATRIX), + &norm); + GPU_link(mat, "direction_transform_m4v3", norm, + GPU_builtin(GPU_INVERSE_OBJECT_MATRIX), + &norm); + GPU_link(mat, gpu_node_name, in[0].link, + norm, + GPU_image(ima, iuser, isdata, 0), + &col1, + &col2, + &col3); + if (do_color_correction) { + GPU_link(mat, "srgb_to_linearrgb", col1, &col1); + GPU_link(mat, "srgb_to_linearrgb", col2, &col2); + GPU_link(mat, "srgb_to_linearrgb", col3, &col3); + } + GPU_link(mat, "node_tex_image_box", in[0].link, + norm, + col1, col2, col3, + GPU_image(ima, iuser, isdata, 0), + GPU_uniform(&blend), + &out[0].link, + &out[1].link); + break; + case SHD_PROJ_SPHERE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_sphere", in[0].link, &in[0].link); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0)); + break; + case SHD_PROJ_TUBE: + GPU_link(mat, "point_texco_remap_square", in[0].link, &in[0].link); + GPU_link(mat, "point_map_to_tube", in[0].link, &in[0].link); + GPU_stack_link(mat, node, gpu_node_name, in, out, GPU_image(ima, iuser, isdata, 0)); + break; + } } if (do_color_correction && (tex->projection != SHD_PROJ_BOX)) { |