/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/freestyle/intern/blender_interface/FRS_freestyle.cpp * \ingroup freestyle */ #include #include #include #include "../application/AppCanvas.h" #include "../application/AppConfig.h" #include "../application/AppView.h" #include "../application/Controller.h" #include "BlenderStrokeRenderer.h" using namespace std; using namespace Freestyle; extern "C" { #include "MEM_guardedalloc.h" #include "DNA_camera_types.h" #include "DNA_freestyle_types.h" #include "DNA_group_types.h" #include "DNA_material_types.h" #include "DNA_text_types.h" #include "BKE_freestyle.h" #include "BKE_global.h" #include "BKE_library.h" #include "BKE_linestyle.h" #include "BKE_main.h" #include "BKE_text.h" #include "BKE_context.h" #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_callbacks.h" #include "BPY_extern.h" #include "renderpipeline.h" #include "pixelblending.h" #include "FRS_freestyle.h" #define DEFAULT_SPHERE_RADIUS 1.0f #define DEFAULT_DKR_EPSILON 0.0f // Freestyle configuration static bool freestyle_is_initialized = false; static Config::Path *pathconfig = NULL; static Controller *controller = NULL; static AppView *view = NULL; // line set buffer for copy & paste static FreestyleLineSet lineset_buffer; static bool lineset_copied = false; // camera information float freestyle_viewpoint[3]; float freestyle_mv[4][4]; float freestyle_proj[4][4]; int freestyle_viewport[4]; // current scene Scene *freestyle_scene; static void load_post_callback(struct Main *main, struct ID *id, void *arg) { lineset_copied = false; } static bCallbackFuncStore load_post_callback_funcstore = { NULL, NULL, /* next, prev */ load_post_callback, /* func */ NULL, /* arg */ 0 /* alloc */ }; //======================================================= // Initialization //======================================================= void FRS_initialize() { if (freestyle_is_initialized) return; pathconfig = new Config::Path; controller = new Controller(); view = new AppView; controller->setView(view); controller->Clear(); freestyle_scene = NULL; lineset_copied = false; BLI_callback_add(&load_post_callback_funcstore, BLI_CB_EVT_LOAD_POST); freestyle_is_initialized = 1; } void FRS_set_context(bContext *C) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "FRS_set_context: context 0x" << C << " scene 0x" << CTX_data_scene(C) << endl; } controller->setContext(C); } void FRS_exit() { delete pathconfig; delete controller; delete view; } //======================================================= // Rendering //======================================================= static void init_view(Render *re) { int width = re->winx; int height = re->winy; int xmin = re->disprect.xmin; int ymin = re->disprect.ymin; int xmax = re->disprect.xmax; int ymax = re->disprect.ymax; float thickness = 1.0f; switch (re->r.line_thickness_mode) { case R_LINE_THICKNESS_ABSOLUTE: thickness = re->r.unit_line_thickness * (re->r.size / 100.f); break; case R_LINE_THICKNESS_RELATIVE: thickness = height / 480.f; break; } freestyle_viewport[0] = freestyle_viewport[1] = 0; freestyle_viewport[2] = width; freestyle_viewport[3] = height; view->setWidth(width); view->setHeight(height); view->setBorder(xmin, ymin, xmax, ymax); view->setThickness(thickness); if (G.debug & G_DEBUG_FREESTYLE) { cout << "\n=== Dimensions of the 2D image coordinate system ===" << endl; cout << "Width : " << width << endl; cout << "Height : " << height << endl; if (re->r.mode & R_BORDER) cout << "Border : (" << xmin << ", " << ymin << ") - (" << xmax << ", " << ymax << ")" << endl; cout << "Unit line thickness : " << thickness << " pixel(s)" << endl; } } static void init_camera(Render *re) { // It is assumed that imported meshes are in the camera coordinate system. // Therefore, the view point (i.e., camera position) is at the origin, and // the the model-view matrix is simply the identity matrix. freestyle_viewpoint[0] = 0.0; freestyle_viewpoint[1] = 0.0; freestyle_viewpoint[2] = 0.0; unit_m4(freestyle_mv); copy_m4_m4(freestyle_proj, re->winmat); #if 0 print_m4("mv", freestyle_mv); print_m4("proj", freestyle_proj); #endif } static char *escape_quotes(char *name) { char *s = (char *)MEM_mallocN(strlen(name) * 2 + 1, "escape_quotes"); char *p = s; while (*name) { if (*name == '\'') *(p++) = '\\'; *(p++) = *(name++); } *p = '\0'; return s; } static Text *create_lineset_handler(Main *bmain, char *layer_name, char *lineset_name) { char *s1 = escape_quotes(layer_name); char *s2 = escape_quotes(lineset_name); Text *text = BKE_text_add(bmain, lineset_name); BKE_text_write(text, "import parameter_editor; parameter_editor.process('"); BKE_text_write(text, s1); BKE_text_write(text, "', '"); BKE_text_write(text, s2); BKE_text_write(text, "')\n"); MEM_freeN(s1); MEM_freeN(s2); return text; } struct edge_type_condition { int edge_type, value; }; // examines the conditions and returns true if the target edge type needs to be computed static bool test_edge_type_conditions(struct edge_type_condition *conditions, int num_edge_types, bool logical_and, int target, bool distinct) { int target_condition = 0; int num_non_target_positive_conditions = 0; int num_non_target_negative_conditions = 0; for (int i = 0; i < num_edge_types; i++) { if (conditions[i].edge_type == target) target_condition = conditions[i].value; else if (conditions[i].value > 0) ++num_non_target_positive_conditions; else if (conditions[i].value < 0) ++num_non_target_negative_conditions; } if (distinct) { // In this case, the 'target' edge type is assumed to appear on distinct edge // of its own and never together with other edge types. if (logical_and) { if (num_non_target_positive_conditions > 0) return false; if (target_condition > 0) return true; if (target_condition < 0) return false; if (num_non_target_negative_conditions > 0) return true; } else { if (target_condition > 0) return true; if (num_non_target_negative_conditions > 0) return true; if (target_condition < 0) return false; if (num_non_target_positive_conditions > 0) return false; } } else { // In this case, the 'target' edge type may appear together with other edge types. if (target_condition > 0) return true; if (target_condition < 0) return true; if (logical_and) { if (num_non_target_positive_conditions > 0) return false; if (num_non_target_negative_conditions > 0) return true; } else { if (num_non_target_negative_conditions > 0) return true; if (num_non_target_positive_conditions > 0) return false; } } return true; } static void prepare(Main *bmain, Render *re, SceneRenderLayer *srl) { // load mesh re->i.infostr = "Freestyle: Mesh loading"; re->stats_draw(re->sdh, &re->i); re->i.infostr = NULL; if (controller->LoadMesh(re, srl)) // returns if scene cannot be loaded or if empty return; if (re->test_break(re->tbh)) return; // add style modules FreestyleConfig *config = &srl->freestyleConfig; if (G.debug & G_DEBUG_FREESTYLE) { cout << "\n=== Rendering options ===" << endl; } int layer_count = 0; switch (config->mode) { case FREESTYLE_CONTROL_SCRIPT_MODE: if (G.debug & G_DEBUG_FREESTYLE) { cout << "Modules :" << endl; } for (FreestyleModuleConfig *module_conf = (FreestyleModuleConfig *)config->modules.first; module_conf; module_conf = module_conf->next) { if (module_conf->script && module_conf->is_displayed) { const char *id_name = module_conf->script->id.name + 2; if (G.debug & G_DEBUG_FREESTYLE) { cout << " " << layer_count + 1 << ": " << id_name; if (module_conf->script->name) cout << " (" << module_conf->script->name << ")"; cout << endl; } controller->InsertStyleModule(layer_count, id_name, module_conf->script); controller->toggleLayer(layer_count, true); layer_count++; } } if (G.debug & G_DEBUG_FREESTYLE) { cout << endl; } controller->setComputeRidgesAndValleysFlag((config->flags & FREESTYLE_RIDGES_AND_VALLEYS_FLAG) ? true : false); controller->setComputeSuggestiveContoursFlag((config->flags & FREESTYLE_SUGGESTIVE_CONTOURS_FLAG) ? true : false); controller->setComputeMaterialBoundariesFlag((config->flags & FREESTYLE_MATERIAL_BOUNDARIES_FLAG) ? true : false); break; case FREESTYLE_CONTROL_EDITOR_MODE: int use_ridges_and_valleys = 0; int use_suggestive_contours = 0; int use_material_boundaries = 0; struct edge_type_condition conditions[] = { {FREESTYLE_FE_SILHOUETTE, 0}, {FREESTYLE_FE_BORDER, 0}, {FREESTYLE_FE_CREASE, 0}, {FREESTYLE_FE_RIDGE_VALLEY, 0}, {FREESTYLE_FE_SUGGESTIVE_CONTOUR, 0}, {FREESTYLE_FE_MATERIAL_BOUNDARY, 0}, {FREESTYLE_FE_CONTOUR, 0}, {FREESTYLE_FE_EXTERNAL_CONTOUR, 0}, {FREESTYLE_FE_EDGE_MARK, 0} }; int num_edge_types = sizeof(conditions) / sizeof(struct edge_type_condition); if (G.debug & G_DEBUG_FREESTYLE) { cout << "Linesets:" << endl; } for (FreestyleLineSet *lineset = (FreestyleLineSet *)config->linesets.first; lineset; lineset = lineset->next) { if (lineset->flags & FREESTYLE_LINESET_ENABLED) { if (G.debug & G_DEBUG_FREESTYLE) { cout << " " << layer_count+1 << ": " << lineset->name << " - " << (lineset->linestyle ? (lineset->linestyle->id.name + 2) : "") << endl; } Text *text = create_lineset_handler(bmain, srl->name, lineset->name); controller->InsertStyleModule(layer_count, lineset->name, text); controller->toggleLayer(layer_count, true); if (!(lineset->selection & FREESTYLE_SEL_EDGE_TYPES) || !lineset->edge_types) { ++use_ridges_and_valleys; ++use_suggestive_contours; ++use_material_boundaries; } else { // conditions for feature edge selection by edge types for (int i = 0; i < num_edge_types; i++) { if (!(lineset->edge_types & conditions[i].edge_type)) conditions[i].value = 0; // no condition specified else if (!(lineset->exclude_edge_types & conditions[i].edge_type)) conditions[i].value = 1; // condition: X else conditions[i].value = -1; // condition: NOT X } // logical operator for the selection conditions bool logical_and = ((lineset->flags & FREESTYLE_LINESET_FE_AND) != 0); // negation operator if (lineset->flags & FREESTYLE_LINESET_FE_NOT) { // convert an Exclusive condition into an Inclusive equivalent using De Morgan's laws: // NOT (X OR Y) --> (NOT X) AND (NOT Y) // NOT (X AND Y) --> (NOT X) OR (NOT Y) for (int i = 0; i < num_edge_types; i++) conditions[i].value *= -1; logical_and = !logical_and; } if (test_edge_type_conditions(conditions, num_edge_types, logical_and, FREESTYLE_FE_RIDGE_VALLEY, true)) { ++use_ridges_and_valleys; } if (test_edge_type_conditions(conditions, num_edge_types, logical_and, FREESTYLE_FE_SUGGESTIVE_CONTOUR, true)) { ++use_suggestive_contours; } if (test_edge_type_conditions(conditions, num_edge_types, logical_and, FREESTYLE_FE_MATERIAL_BOUNDARY, true)) { ++use_material_boundaries; } } layer_count++; } } controller->setComputeRidgesAndValleysFlag(use_ridges_and_valleys > 0); controller->setComputeSuggestiveContoursFlag(use_suggestive_contours > 0); controller->setComputeMaterialBoundariesFlag(use_material_boundaries > 0); break; } // set parameters if (config->flags & FREESTYLE_ADVANCED_OPTIONS_FLAG) { controller->setSphereRadius(config->sphere_radius); controller->setSuggestiveContourKrDerivativeEpsilon(config->dkr_epsilon); } else { controller->setSphereRadius(DEFAULT_SPHERE_RADIUS); controller->setSuggestiveContourKrDerivativeEpsilon(DEFAULT_DKR_EPSILON); } controller->setFaceSmoothness((config->flags & FREESTYLE_FACE_SMOOTHNESS_FLAG) ? true : false); controller->setCreaseAngle(RAD2DEGF(config->crease_angle)); controller->setVisibilityAlgo((config->flags & FREESTYLE_CULLING) ? FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE : FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE); if (G.debug & G_DEBUG_FREESTYLE) { cout << "Crease angle : " << controller->getCreaseAngle() << endl; cout << "Sphere radius : " << controller->getSphereRadius() << endl; cout << "Face smoothness : " << (controller->getFaceSmoothness() ? "enabled" : "disabled") << endl; cout << "Redges and valleys : " << (controller->getComputeRidgesAndValleysFlag() ? "enabled" : "disabled") << endl; cout << "Suggestive contours : " << (controller->getComputeSuggestiveContoursFlag() ? "enabled" : "disabled") << endl; cout << "Suggestive contour Kr derivative epsilon : " << controller->getSuggestiveContourKrDerivativeEpsilon() << endl; cout << "Material boundaries : " << (controller->getComputeMaterialBoundariesFlag() ? "enabled" : "disabled") << endl; cout << endl; } // set diffuse and z depth passes RenderLayer *rl = RE_GetRenderLayer(re->result, srl->name); bool diffuse = false, z = false; for (RenderPass *rpass = (RenderPass *)rl->passes.first; rpass; rpass = rpass->next) { switch (rpass->passtype) { case SCE_PASS_DIFFUSE: controller->setPassDiffuse(rpass->rect, rpass->rectx, rpass->recty); diffuse = true; break; case SCE_PASS_Z: controller->setPassZ(rpass->rect, rpass->rectx, rpass->recty); z = true; break; } } if (G.debug & G_DEBUG_FREESTYLE) { cout << "Passes :" << endl; cout << " Diffuse = " << (diffuse ? "enabled" : "disabled") << endl; cout << " Z = " << (z ? "enabled" : "disabled") << endl; } // compute view map re->i.infostr = "Freestyle: View map creation"; re->stats_draw(re->sdh, &re->i); re->i.infostr = NULL; controller->ComputeViewMap(); } void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_render) { RenderLayer *rl; float *src, *dest, *pixSrc, *pixDest; int x, y, rectx, recty; if (freestyle_render == NULL || freestyle_render->result == NULL) return; rl = render_get_active_layer( freestyle_render, freestyle_render->result ); if (!rl || rl->rectf == NULL) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "Cannot find Freestyle result image" << endl; } return; } src = rl->rectf; #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "src: " << rl->rectx << " x " << rl->recty << endl; } #endif rl = RE_GetRenderLayer(re->result, srl->name); if (!rl || rl->rectf == NULL) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "No layer to composite to" << endl; } return; } dest = rl->rectf; #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "dest: " << rl->rectx << " x " << rl->recty << endl; } #endif rectx = re->rectx; recty = re->recty; for (y = 0; y < recty; y++) { for (x = 0; x < rectx; x++) { pixSrc = src + 4 * (rectx * y + x); if (pixSrc[3] > 0.0) { pixDest = dest + 4 * (rectx * y + x); addAlphaOverFloat(pixDest, pixSrc); } } } } static int displayed_layer_count(SceneRenderLayer *srl) { int count = 0; switch (srl->freestyleConfig.mode) { case FREESTYLE_CONTROL_SCRIPT_MODE: for (FreestyleModuleConfig *module = (FreestyleModuleConfig *)srl->freestyleConfig.modules.first; module; module = module->next) { if (module->script && module->is_displayed) count++; } break; case FREESTYLE_CONTROL_EDITOR_MODE: for (FreestyleLineSet *lineset = (FreestyleLineSet *)srl->freestyleConfig.linesets.first; lineset; lineset = lineset->next) { if (lineset->flags & FREESTYLE_LINESET_ENABLED) count++; } break; } return count; } int FRS_is_freestyle_enabled(SceneRenderLayer *srl) { return (!(srl->layflag & SCE_LAY_DISABLE) && srl->layflag & SCE_LAY_FRS && displayed_layer_count(srl) > 0); } void FRS_init_stroke_rendering(Render *re) { if (G.debug & G_DEBUG_FREESTYLE) { cout << endl; cout << "#===============================================================" << endl; cout << "# Freestyle" << endl; cout << "#===============================================================" << endl; } init_view(re); init_camera(re); controller->ResetRenderCount(); } Render *FRS_do_stroke_rendering(Render *re, SceneRenderLayer *srl, int render) { Main *freestyle_bmain = re->freestyle_bmain; Render *freestyle_render = NULL; Text *text, *next_text; if (!render) return controller->RenderStrokes(re, false); RenderMonitor monitor(re); controller->setRenderMonitor(&monitor); if (G.debug & G_DEBUG_FREESTYLE) { cout << endl; cout << "----------------------------------------------------------" << endl; cout << "| " << (re->scene->id.name + 2) << "|" << srl->name << endl; cout << "----------------------------------------------------------" << endl; } // prepare Freestyle: // - load mesh // - add style modules // - set parameters // - compute view map prepare(freestyle_bmain, re, srl); if (re->test_break(re->tbh)) { controller->CloseFile(); if (G.debug & G_DEBUG_FREESTYLE) { cout << "Break" << endl; } } else { // render and composite Freestyle result if (controller->_ViewMap) { // render strokes re->i.infostr = "Freestyle: Stroke rendering"; re->stats_draw(re->sdh, &re->i); re->i.infostr = NULL; freestyle_scene = re->scene; controller->DrawStrokes(); freestyle_render = controller->RenderStrokes(re, true); controller->CloseFile(); freestyle_scene = NULL; // composite result FRS_composite_result(re, srl, freestyle_render); RE_FreeRenderResult(freestyle_render->result); freestyle_render->result = NULL; } } // Free temp main (currently only text blocks are stored there) for (text = (Text *)freestyle_bmain->text.first; text; text = next_text) { next_text = (Text *) text->id.next; BKE_text_unlink(freestyle_bmain, text); BKE_libblock_free(freestyle_bmain, text); } return freestyle_render; } void FRS_finish_stroke_rendering(Render *re) { // clear canvas controller->Clear(); } //======================================================= // Freestyle Panel Configuration //======================================================= void FRS_copy_active_lineset(FreestyleConfig *config) { FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config); if (lineset) { lineset_buffer.linestyle = lineset->linestyle; lineset_buffer.flags = lineset->flags; lineset_buffer.selection = lineset->selection; lineset_buffer.qi = lineset->qi; lineset_buffer.qi_start = lineset->qi_start; lineset_buffer.qi_end = lineset->qi_end; lineset_buffer.edge_types = lineset->edge_types; lineset_buffer.exclude_edge_types = lineset->exclude_edge_types; lineset_buffer.group = lineset->group; strcpy(lineset_buffer.name, lineset->name); lineset_copied = true; } } void FRS_paste_active_lineset(FreestyleConfig *config) { if (!lineset_copied) return; FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config); if (lineset) { if (lineset->linestyle) lineset->linestyle->id.us--; lineset->linestyle = lineset_buffer.linestyle; if (lineset->linestyle) lineset->linestyle->id.us++; lineset->flags = lineset_buffer.flags; lineset->selection = lineset_buffer.selection; lineset->qi = lineset_buffer.qi; lineset->qi_start = lineset_buffer.qi_start; lineset->qi_end = lineset_buffer.qi_end; lineset->edge_types = lineset_buffer.edge_types; lineset->exclude_edge_types = lineset_buffer.exclude_edge_types; if (lineset->group) { lineset->group->id.us--; lineset->group = NULL; } if (lineset_buffer.group) { lineset->group = lineset_buffer.group; lineset->group->id.us++; } strcpy(lineset->name, lineset_buffer.name); BKE_freestyle_lineset_unique_name(config, lineset); lineset->flags |= FREESTYLE_LINESET_CURRENT; } } void FRS_delete_active_lineset(FreestyleConfig *config) { FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config); if (lineset) { BKE_freestyle_lineset_delete(config, lineset); } } void FRS_move_active_lineset_up(FreestyleConfig *config) { FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config); if (lineset) { BLI_remlink(&config->linesets, lineset); BLI_insertlinkbefore(&config->linesets, lineset->prev, lineset); } } void FRS_move_active_lineset_down(FreestyleConfig *config) { FreestyleLineSet *lineset = BKE_freestyle_lineset_get_active(config); if (lineset) { BLI_remlink(&config->linesets, lineset); BLI_insertlinkafter(&config->linesets, lineset->next, lineset); } } // Testing Material *FRS_create_stroke_material(Main *bmain, struct FreestyleLineStyle *linestyle) { bNodeTree *nt = (linestyle->use_nodes) ? linestyle->nodetree : NULL; Material *ma = BlenderStrokeRenderer::GetStrokeShader(bmain, nt, true); ma->id.us = 0; return ma; } } // extern "C"