Listing the "Blender Foundation" as copyright holder implied the Blender Foundation holds copyright to files which may include work from many developers. While keeping copyright on headers makes sense for isolated libraries, Blender's own code may be refactored or moved between files in a way that makes the per file copyright holders less meaningful. Copyright references to the "Blender Foundation" have been replaced with "Blender Authors", with the exception of `./extern/` since these this contains libraries which are more isolated, any changed to license headers there can be handled on a case-by-case basis. Some directories in `./intern/` have also been excluded: - `./intern/cycles/` it's own `AUTHORS` file is planned. - `./intern/opensubdiv/`. An "AUTHORS" file has been added, using the chromium projects authors file as a template. Design task: #110784 Ref !110783.
1121 lines
27 KiB
C++
1121 lines
27 KiB
C++
/* SPDX-FileCopyrightText: 2008-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup freestyle
|
|
*/
|
|
|
|
extern "C" {
|
|
#include <Python.h>
|
|
}
|
|
|
|
#include <cfloat>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
#include "AppCanvas.h"
|
|
#include "AppConfig.h"
|
|
#include "AppView.h"
|
|
#include "Controller.h"
|
|
|
|
#include "../image/Image.h"
|
|
|
|
#include "../scene_graph/NodeDrawingStyle.h"
|
|
#include "../scene_graph/NodeShape.h"
|
|
#include "../scene_graph/NodeTransform.h"
|
|
#include "../scene_graph/NodeViewLayer.h"
|
|
#include "../scene_graph/ScenePrettyPrinter.h"
|
|
#include "../scene_graph/VertexRep.h"
|
|
|
|
#include "../stroke/PSStrokeRenderer.h"
|
|
#include "../stroke/StrokeTesselator.h"
|
|
#include "../stroke/StyleModule.h"
|
|
#include "../stroke/TextStrokeRenderer.h"
|
|
|
|
#include "../system/PythonInterpreter.h"
|
|
#include "../system/StringUtils.h"
|
|
|
|
#include "../view_map/SteerableViewMap.h"
|
|
#include "../view_map/ViewMap.h"
|
|
#include "../view_map/ViewMapTesselator.h"
|
|
|
|
#include "../winged_edge/Curvature.h"
|
|
#include "../winged_edge/WEdge.h"
|
|
#include "../winged_edge/WXEdgeBuilder.h"
|
|
#include "../winged_edge/WingedEdgeBuilder.h"
|
|
|
|
#include "../blender_interface/BlenderFileLoader.h"
|
|
#include "../blender_interface/BlenderStrokeRenderer.h"
|
|
#include "../blender_interface/BlenderStyleModule.h"
|
|
|
|
#include "BKE_global.h"
|
|
#include "BLI_path_util.h"
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "DNA_freestyle_types.h"
|
|
|
|
#include "FRS_freestyle.h"
|
|
|
|
namespace Freestyle {
|
|
|
|
Controller::Controller()
|
|
{
|
|
const string sep(Config::DIR_SEP);
|
|
#if 0
|
|
const string filename = Config::Path::getInstance()->getHomeDir() + sep + Config::OPTIONS_DIR +
|
|
sep + Config::OPTIONS_CURRENT_DIRS_FILE;
|
|
_current_dirs = new ConfigIO(filename, Config::APPLICATION_NAME + "CurrentDirs", true);
|
|
#endif
|
|
|
|
_RootNode = new NodeGroup;
|
|
_RootNode->addRef();
|
|
|
|
#if 0
|
|
_SilhouetteNode = nullptr;
|
|
_ProjectedSilhouette = nullptr;
|
|
_VisibleProjectedSilhouette = nullptr;
|
|
|
|
_DebugNode = new NodeGroup;
|
|
_DebugNode->addRef();
|
|
#endif
|
|
|
|
_winged_edge = nullptr;
|
|
|
|
_pView = nullptr;
|
|
_pRenderMonitor = nullptr;
|
|
|
|
_edgeTesselationNature = (Nature::SILHOUETTE | Nature::BORDER | Nature::CREASE);
|
|
|
|
_ProgressBar = new ProgressBar;
|
|
_SceneNumFaces = 0;
|
|
#if 0
|
|
_minEdgeSize = DBL_MAX;
|
|
#endif
|
|
_EPSILON = 1.0e-6;
|
|
_bboxDiag = 0;
|
|
|
|
_ViewMap = nullptr;
|
|
|
|
_Canvas = nullptr;
|
|
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_adaptive_traditional;
|
|
//_VisibilityAlgo = ViewMapBuilder::ray_casting;
|
|
|
|
_Canvas = new AppCanvas;
|
|
|
|
_inter = new PythonInterpreter();
|
|
_EnableViewMapCache = false;
|
|
_EnableQI = true;
|
|
_EnableFaceSmoothness = false;
|
|
_ComputeRidges = true;
|
|
_ComputeSteerableViewMap = false;
|
|
_ComputeSuggestive = true;
|
|
_ComputeMaterialBoundaries = true;
|
|
_sphereRadius = 1.0;
|
|
_creaseAngle = 134.43;
|
|
prevSceneHash = -1.0;
|
|
|
|
init_options();
|
|
}
|
|
|
|
Controller::~Controller()
|
|
{
|
|
if (nullptr != _RootNode) {
|
|
int ref = _RootNode->destroy();
|
|
if (0 == ref) {
|
|
delete _RootNode;
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
if (nullptr != _SilhouetteNode) {
|
|
int ref = _SilhouetteNode->destroy();
|
|
if (0 == ref) {
|
|
delete _SilhouetteNode;
|
|
}
|
|
}
|
|
|
|
if (nullptr != _DebugNode) {
|
|
int ref = _DebugNode->destroy();
|
|
if (0 == ref) {
|
|
delete _DebugNode;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (_winged_edge) {
|
|
delete _winged_edge;
|
|
_winged_edge = nullptr;
|
|
}
|
|
|
|
if (nullptr != _ViewMap) {
|
|
delete _ViewMap;
|
|
_ViewMap = nullptr;
|
|
}
|
|
|
|
if (nullptr != _Canvas) {
|
|
delete _Canvas;
|
|
_Canvas = nullptr;
|
|
}
|
|
|
|
if (_inter) {
|
|
delete _inter;
|
|
_inter = nullptr;
|
|
}
|
|
|
|
if (_ProgressBar) {
|
|
delete _ProgressBar;
|
|
_ProgressBar = nullptr;
|
|
}
|
|
|
|
// delete _current_dirs;
|
|
}
|
|
|
|
void Controller::setView(AppView *iView)
|
|
{
|
|
if (nullptr == iView) {
|
|
return;
|
|
}
|
|
|
|
_pView = iView;
|
|
_Canvas->setViewer(_pView);
|
|
}
|
|
|
|
void Controller::setRenderMonitor(RenderMonitor *iRenderMonitor)
|
|
{
|
|
_pRenderMonitor = iRenderMonitor;
|
|
}
|
|
|
|
void Controller::setPassDiffuse(float *buf, int width, int height)
|
|
{
|
|
AppCanvas *app_canvas = dynamic_cast<AppCanvas *>(_Canvas);
|
|
BLI_assert(app_canvas != nullptr);
|
|
app_canvas->setPassDiffuse(buf, width, height);
|
|
}
|
|
|
|
void Controller::setPassZ(float *buf, int width, int height)
|
|
{
|
|
AppCanvas *app_canvas = dynamic_cast<AppCanvas *>(_Canvas);
|
|
BLI_assert(app_canvas != nullptr);
|
|
app_canvas->setPassZ(buf, width, height);
|
|
}
|
|
|
|
void Controller::setContext(bContext *C)
|
|
{
|
|
PythonInterpreter *py_inter = dynamic_cast<PythonInterpreter *>(_inter);
|
|
py_inter->setContext(C);
|
|
}
|
|
|
|
bool Controller::hitViewMapCache()
|
|
{
|
|
if (!_EnableViewMapCache) {
|
|
return false;
|
|
}
|
|
if (sceneHashFunc.match()) {
|
|
return (nullptr != _ViewMap);
|
|
}
|
|
sceneHashFunc.store();
|
|
return false;
|
|
}
|
|
|
|
int Controller::LoadMesh(Render *re, ViewLayer *view_layer, Depsgraph *depsgraph)
|
|
{
|
|
BlenderFileLoader loader(re, view_layer, depsgraph);
|
|
|
|
loader.setRenderMonitor(_pRenderMonitor);
|
|
|
|
_Chrono.start();
|
|
|
|
NodeGroup *blenderScene = loader.Load();
|
|
|
|
if (blenderScene == nullptr) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Cannot load scene" << endl;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
if (blenderScene->numberOfChildren() < 1) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Empty scene" << endl;
|
|
}
|
|
blenderScene->destroy();
|
|
delete blenderScene;
|
|
return 1;
|
|
}
|
|
|
|
real duration = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Scene loaded" << endl;
|
|
printf("Mesh cleaning : %lf\n", duration);
|
|
printf("View map cache : %s\n", _EnableViewMapCache ? "enabled" : "disabled");
|
|
}
|
|
_SceneNumFaces += loader.numFacesRead();
|
|
|
|
#if 0
|
|
if (loader.minEdgeSize() < _minEdgeSize) {
|
|
_minEdgeSize = loader.minEdgeSize();
|
|
}
|
|
#endif
|
|
|
|
#if 0 // DEBUG
|
|
ScenePrettyPrinter spp;
|
|
blenderScene->accept(spp);
|
|
#endif
|
|
|
|
_RootNode->AddChild(blenderScene);
|
|
_RootNode->UpdateBBox(); // FIXME: Correct that by making a Renderer to compute the bbox
|
|
|
|
_pView->setModel(_RootNode);
|
|
//_pView->FitBBox();
|
|
|
|
if (_pRenderMonitor->testBreak()) {
|
|
return 0;
|
|
}
|
|
|
|
if (_EnableViewMapCache) {
|
|
|
|
NodeCamera *cam;
|
|
if (g_freestyle.proj[3][3] != 0.0) {
|
|
cam = new NodeOrthographicCamera;
|
|
}
|
|
else {
|
|
cam = new NodePerspectiveCamera;
|
|
}
|
|
double proj[16];
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
proj[i * 4 + j] = g_freestyle.proj[i][j];
|
|
}
|
|
}
|
|
cam->setProjectionMatrix(proj);
|
|
_RootNode->AddChild(cam);
|
|
_RootNode->AddChild(new NodeViewLayer(*re->scene, *view_layer));
|
|
|
|
sceneHashFunc.reset();
|
|
// blenderScene->accept(sceneHashFunc);
|
|
_RootNode->accept(sceneHashFunc);
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Scene hash : " << sceneHashFunc.toString() << endl;
|
|
}
|
|
if (hitViewMapCache()) {
|
|
ClearRootNode();
|
|
return 0;
|
|
}
|
|
|
|
delete _ViewMap;
|
|
_ViewMap = nullptr;
|
|
}
|
|
|
|
_Chrono.start();
|
|
|
|
WXEdgeBuilder wx_builder;
|
|
wx_builder.setRenderMonitor(_pRenderMonitor);
|
|
blenderScene->accept(wx_builder);
|
|
_winged_edge = wx_builder.getWingedEdge();
|
|
|
|
duration = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
printf("WEdge building : %lf\n", duration);
|
|
}
|
|
|
|
#if 0
|
|
_pView->setDebug(_DebugNode);
|
|
|
|
// delete stuff
|
|
if (0 != ws_builder) {
|
|
delete ws_builder;
|
|
ws_builder = 0;
|
|
}
|
|
|
|
soc QFileInfo qfi(iFileName);
|
|
soc string basename((const char *)qfi.fileName().toAscii().data());
|
|
char cleaned[FILE_MAX];
|
|
BLI_strncpy(cleaned, iFileName, FILE_MAX);
|
|
BLI_path_normalize(cleaned);
|
|
string basename = string(cleaned);
|
|
#endif
|
|
|
|
_ListOfModels.emplace_back("Blender_models");
|
|
|
|
_Scene3dBBox = _RootNode->bbox();
|
|
|
|
_bboxDiag = (_RootNode->bbox().getMax() - _RootNode->bbox().getMin()).norm();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Triangles nb : " << _SceneNumFaces << " imported, " << _winged_edge->getNumFaces()
|
|
<< " retained" << endl;
|
|
cout << "Bounding Box : " << _bboxDiag << endl;
|
|
}
|
|
|
|
ClearRootNode();
|
|
|
|
_SceneNumFaces = _winged_edge->getNumFaces();
|
|
if (_SceneNumFaces == 0) {
|
|
DeleteWingedEdge();
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void Controller::CloseFile()
|
|
{
|
|
WShape::setCurrentId(0);
|
|
_ListOfModels.clear();
|
|
|
|
// We deallocate the memory:
|
|
ClearRootNode();
|
|
DeleteWingedEdge();
|
|
DeleteViewMap();
|
|
|
|
// clears the canvas
|
|
_Canvas->Clear();
|
|
|
|
// soc: reset passes
|
|
setPassDiffuse(nullptr, 0, 0);
|
|
setPassZ(nullptr, 0, 0);
|
|
}
|
|
|
|
void Controller::ClearRootNode()
|
|
{
|
|
_pView->DetachModel();
|
|
if (nullptr != _RootNode) {
|
|
int ref = _RootNode->destroy();
|
|
if (0 == ref) {
|
|
_RootNode->addRef();
|
|
}
|
|
_RootNode->clearBBox();
|
|
}
|
|
}
|
|
|
|
void Controller::DeleteWingedEdge()
|
|
{
|
|
if (_winged_edge) {
|
|
delete _winged_edge;
|
|
_winged_edge = nullptr;
|
|
}
|
|
|
|
// clears the grid
|
|
_Grid.clear();
|
|
_Scene3dBBox.clear();
|
|
_SceneNumFaces = 0;
|
|
#if 0
|
|
_minEdgeSize = DBL_MAX;
|
|
#endif
|
|
}
|
|
|
|
void Controller::DeleteViewMap(bool freeCache)
|
|
{
|
|
#if 0
|
|
_pView->DetachSilhouette();
|
|
if (nullptr != _SilhouetteNode) {
|
|
int ref = _SilhouetteNode->destroy();
|
|
if (0 == ref) {
|
|
delete _SilhouetteNode;
|
|
_SilhouetteNode = nullptr;
|
|
}
|
|
}
|
|
|
|
if (nullptr != _ProjectedSilhouette) {
|
|
int ref = _ProjectedSilhouette->destroy();
|
|
if (0 == ref) {
|
|
delete _ProjectedSilhouette;
|
|
_ProjectedSilhouette = nullptr;
|
|
}
|
|
}
|
|
if (nullptr != _VisibleProjectedSilhouette) {
|
|
int ref = _VisibleProjectedSilhouette->destroy();
|
|
if (0 == ref) {
|
|
delete _VisibleProjectedSilhouette;
|
|
_VisibleProjectedSilhouette = nullptr;
|
|
}
|
|
}
|
|
|
|
_pView->DetachDebug();
|
|
if (nullptr != _DebugNode) {
|
|
int ref = _DebugNode->destroy();
|
|
if (0 == ref) {
|
|
_DebugNode->addRef();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (nullptr != _ViewMap) {
|
|
if (freeCache || !_EnableViewMapCache) {
|
|
delete _ViewMap;
|
|
_ViewMap = nullptr;
|
|
prevSceneHash = -1.0;
|
|
}
|
|
else {
|
|
_ViewMap->Clean();
|
|
}
|
|
}
|
|
}
|
|
|
|
void Controller::ComputeViewMap()
|
|
{
|
|
if (_ListOfModels.empty()) {
|
|
return;
|
|
}
|
|
|
|
DeleteViewMap(true);
|
|
|
|
// retrieve the 3D viewpoint and transformations information
|
|
//----------------------------------------------------------
|
|
// Save the viewpoint context at the view level in order
|
|
// to be able to restore it later:
|
|
|
|
// Restore the context of view:
|
|
// we need to perform all these operations while the
|
|
// 3D context is on.
|
|
Vec3f vp(UNPACK3(g_freestyle.viewpoint));
|
|
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "mv" << endl;
|
|
}
|
|
#endif
|
|
real mv[4][4];
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
mv[i][j] = g_freestyle.mv[i][j];
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << mv[i][j] << " ";
|
|
}
|
|
#endif
|
|
}
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << endl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "\nproj" << endl;
|
|
}
|
|
#endif
|
|
real proj[4][4];
|
|
for (int i = 0; i < 4; i++) {
|
|
for (int j = 0; j < 4; j++) {
|
|
proj[i][j] = g_freestyle.proj[i][j];
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << proj[i][j] << " ";
|
|
}
|
|
#endif
|
|
}
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << endl;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
int viewport[4];
|
|
for (int i = 0; i < 4; i++) {
|
|
viewport[i] = g_freestyle.viewport[i];
|
|
}
|
|
|
|
#if 0
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "\nfocal:" << _pView->GetFocalLength() << endl << endl;
|
|
}
|
|
#endif
|
|
|
|
// Flag the WXEdge structure for silhouette edge detection:
|
|
//----------------------------------------------------------
|
|
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "\n=== Detecting silhouette edges ===" << endl;
|
|
}
|
|
_Chrono.start();
|
|
|
|
edgeDetector.setViewpoint(vp);
|
|
edgeDetector.enableOrthographicProjection(proj[3][3] != 0.0);
|
|
edgeDetector.enableRidgesAndValleysFlag(_ComputeRidges);
|
|
edgeDetector.enableSuggestiveContours(_ComputeSuggestive);
|
|
edgeDetector.enableMaterialBoundaries(_ComputeMaterialBoundaries);
|
|
edgeDetector.enableFaceSmoothness(_EnableFaceSmoothness);
|
|
edgeDetector.setCreaseAngle(_creaseAngle);
|
|
edgeDetector.setSphereRadius(_sphereRadius);
|
|
edgeDetector.setSuggestiveContourKrDerivativeEpsilon(_suggestiveContourKrDerivativeEpsilon);
|
|
edgeDetector.setRenderMonitor(_pRenderMonitor);
|
|
edgeDetector.processShapes(*_winged_edge);
|
|
|
|
real duration = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
printf("Feature lines : %lf\n", duration);
|
|
}
|
|
|
|
if (_pRenderMonitor->testBreak()) {
|
|
return;
|
|
}
|
|
|
|
// Builds the view map structure from the flagged WSEdge structure:
|
|
//----------------------------------------------------------
|
|
ViewMapBuilder vmBuilder;
|
|
vmBuilder.setEnableQI(_EnableQI);
|
|
vmBuilder.setViewpoint(vp);
|
|
vmBuilder.setTransform(
|
|
mv, proj, viewport, _pView->GetFocalLength(), _pView->GetAspect(), _pView->GetFovyRadian());
|
|
vmBuilder.setFrustum(_pView->znear(), _pView->zfar());
|
|
vmBuilder.setGrid(&_Grid);
|
|
vmBuilder.setRenderMonitor(_pRenderMonitor);
|
|
|
|
#if 0
|
|
// Builds a tessellated form of the silhouette for display purpose:
|
|
//---------------------------------------------------------------
|
|
ViewMapTesselator3D sTesselator3d;
|
|
ViewMapTesselator2D sTesselator2d;
|
|
sTesselator2d.setNature(_edgeTesselationNature);
|
|
sTesselator3d.setNature(_edgeTesselationNature);
|
|
#endif
|
|
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "\n=== Building the view map ===" << endl;
|
|
}
|
|
_Chrono.start();
|
|
// Build View Map
|
|
_ViewMap = vmBuilder.BuildViewMap(
|
|
*_winged_edge, _VisibilityAlgo, _EPSILON, _Scene3dBBox, _SceneNumFaces);
|
|
_ViewMap->setScene3dBBox(_Scene3dBBox);
|
|
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
printf("ViewMap edge count : %i\n", _ViewMap->viewedges_size());
|
|
}
|
|
|
|
#if 0
|
|
// Tesselate the 3D edges:
|
|
_SilhouetteNode = sTesselator3d.Tesselate(_ViewMap);
|
|
_SilhouetteNode->addRef();
|
|
|
|
// Tesselate 2D edges
|
|
_ProjectedSilhouette = sTesselator2d.Tesselate(_ViewMap);
|
|
_ProjectedSilhouette->addRef();
|
|
#endif
|
|
|
|
duration = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
printf("ViewMap building : %lf\n", duration);
|
|
}
|
|
|
|
#if 0
|
|
_pView->AddSilhouette(_SilhouetteNode);
|
|
_pView->AddSilhouette(_WRoot);
|
|
_pView->Add2DSilhouette(_ProjectedSilhouette);
|
|
_pView->Add2DVisibleSilhouette(_VisibleProjectedSilhouette);
|
|
_pView->AddDebug(_DebugNode);
|
|
#endif
|
|
|
|
// Draw the steerable density map:
|
|
//--------------------------------
|
|
if (_ComputeSteerableViewMap) {
|
|
ComputeSteerableViewMap();
|
|
}
|
|
// Reset Style modules modification flags
|
|
resetModified(true);
|
|
|
|
DeleteWingedEdge();
|
|
}
|
|
|
|
void Controller::ComputeSteerableViewMap()
|
|
{
|
|
#if 0 // soc
|
|
if ((!_Canvas) || (!_ViewMap)) {
|
|
return;
|
|
}
|
|
|
|
// Build 4 nodes containing the edges in the 4 directions
|
|
NodeGroup *ng[Canvas::NB_STEERABLE_VIEWMAP];
|
|
uint i;
|
|
real c =
|
|
32.0f /
|
|
255.0f; // see SteerableViewMap::readSteerableViewMapPixel() for information about this 32.
|
|
for (i = 0; i < Canvas::NB_STEERABLE_VIEWMAP; ++i) {
|
|
ng[i] = new NodeGroup;
|
|
}
|
|
NodeShape *completeNS = new NodeShape;
|
|
completeNS->material().setDiffuse(c, c, c, 1);
|
|
ng[Canvas::NB_STEERABLE_VIEWMAP - 1]->AddChild(completeNS);
|
|
SteerableViewMap *svm = _Canvas->getSteerableViewMap();
|
|
svm->Reset();
|
|
|
|
ViewMap::fedges_container &fedges = _ViewMap->FEdges();
|
|
LineRep *fRep;
|
|
NodeShape *ns;
|
|
for (ViewMap::fedges_container::iterator f = fedges.begin(), fend = fedges.end(); f != fend; ++f)
|
|
{
|
|
if ((*f)->viewedge()->qi() != 0) {
|
|
continue;
|
|
}
|
|
fRep = new LineRep((*f)->vertexA()->point2d(), (*f)->vertexB()->point2d());
|
|
completeNS->AddRep(fRep); // add to the complete map anyway
|
|
double *oweights = svm->AddFEdge(*f);
|
|
for (i = 0; i < (Canvas::NB_STEERABLE_VIEWMAP - 1); ++i) {
|
|
ns = new NodeShape;
|
|
double wc = oweights[i] * c;
|
|
if (oweights[i] == 0) {
|
|
continue;
|
|
}
|
|
ns->material().setDiffuse(wc, wc, wc, 1);
|
|
ns->AddRep(fRep);
|
|
ng[i]->AddChild(ns);
|
|
}
|
|
}
|
|
|
|
GrayImage *img[Canvas::NB_STEERABLE_VIEWMAP];
|
|
//#ifdef WIN32
|
|
QGLBasicWidget offscreenBuffer(_pView, "SteerableViewMap", _pView->width(), _pView->height());
|
|
QPixmap pm;
|
|
QImage qimg;
|
|
for (i = 0; i < Canvas::NB_STEERABLE_VIEWMAP; ++i) {
|
|
offscreenBuffer.AddNode(ng[i]);
|
|
# if 0
|
|
img[i] = new GrayImage(_pView->width(), _pView->height());
|
|
offscreenBuffer.readPixels(0, 0, _pView->width(), _pView->height(), img[i]->getArray());
|
|
# endif
|
|
pm = offscreenBuffer.renderPixmap(_pView->width(), _pView->height());
|
|
|
|
if (pm.isNull()) {
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "BuildViewMap Warning: couldn't render the steerable ViewMap" << endl;
|
|
}
|
|
}
|
|
// pm.save(QString("steerable") + QString::number(i) + QString(".bmp"), "BMP");
|
|
// FIXME!! Lost of time !
|
|
qimg = pm.toImage();
|
|
// FIXME !! again!
|
|
img[i] = new GrayImage(_pView->width(), _pView->height());
|
|
for (uint y = 0; y < img[i]->height(); ++y) {
|
|
for (uint x = 0; x < img[i]->width(); ++x) {
|
|
// img[i]->setPixel(x, y, float(qGray(qimg.pixel(x, y))) / 255.0f);
|
|
img[i]->setPixel(x, y, float(qGray(qimg.pixel(x, y))));
|
|
// float c = qGray(qimg.pixel(x, y));
|
|
// img[i]->setPixel(x, y, qGray(qimg.pixel(x, y)));
|
|
}
|
|
}
|
|
offscreenBuffer.DetachNode(ng[i]);
|
|
ng[i]->destroy();
|
|
delete ng[i];
|
|
// check
|
|
# if 0
|
|
qimg = QImage(_pView->width(), _pView->height(), 32);
|
|
for (uint y = 0; y < img[i]->height(); ++y) {
|
|
for (uint x = 0; x < img[i]->width(); ++x) {
|
|
float v = img[i]->pixel(x, y);
|
|
qimg.setPixel(x, y, qRgb(v, v, v));
|
|
}
|
|
}
|
|
qimg.save(QString("newsteerable") + QString::number(i) + QString(".bmp"), "BMP");
|
|
# endif
|
|
}
|
|
|
|
svm->buildImagesPyramids(img, false, 0, 1.0f);
|
|
#endif
|
|
}
|
|
|
|
void Controller::saveSteerableViewMapImages()
|
|
{
|
|
SteerableViewMap *svm = _Canvas->getSteerableViewMap();
|
|
if (!svm) {
|
|
cerr << "the Steerable ViewMap has not been computed yet" << endl;
|
|
return;
|
|
}
|
|
svm->saveSteerableViewMap();
|
|
}
|
|
|
|
void Controller::toggleVisibilityAlgo()
|
|
{
|
|
if (_VisibilityAlgo == ViewMapBuilder::ray_casting) {
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_fast;
|
|
}
|
|
else if (_VisibilityAlgo == ViewMapBuilder::ray_casting_fast) {
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_very_fast;
|
|
}
|
|
else {
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting;
|
|
}
|
|
}
|
|
|
|
void Controller::setVisibilityAlgo(int algo)
|
|
{
|
|
switch (algo) {
|
|
case FREESTYLE_ALGO_REGULAR:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting;
|
|
break;
|
|
case FREESTYLE_ALGO_FAST:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_fast;
|
|
break;
|
|
case FREESTYLE_ALGO_VERYFAST:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_very_fast;
|
|
break;
|
|
case FREESTYLE_ALGO_CULLED_ADAPTIVE_TRADITIONAL:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_culled_adaptive_traditional;
|
|
break;
|
|
case FREESTYLE_ALGO_ADAPTIVE_TRADITIONAL:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_adaptive_traditional;
|
|
break;
|
|
case FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_culled_adaptive_cumulative;
|
|
break;
|
|
case FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE:
|
|
_VisibilityAlgo = ViewMapBuilder::ray_casting_adaptive_cumulative;
|
|
break;
|
|
}
|
|
}
|
|
|
|
int Controller::getVisibilityAlgo()
|
|
{
|
|
switch (_VisibilityAlgo) {
|
|
case ViewMapBuilder::ray_casting:
|
|
return FREESTYLE_ALGO_REGULAR;
|
|
case ViewMapBuilder::ray_casting_fast:
|
|
return FREESTYLE_ALGO_FAST;
|
|
case ViewMapBuilder::ray_casting_very_fast:
|
|
return FREESTYLE_ALGO_VERYFAST;
|
|
case ViewMapBuilder::ray_casting_culled_adaptive_traditional:
|
|
return FREESTYLE_ALGO_CULLED_ADAPTIVE_TRADITIONAL;
|
|
case ViewMapBuilder::ray_casting_adaptive_traditional:
|
|
return FREESTYLE_ALGO_ADAPTIVE_TRADITIONAL;
|
|
case ViewMapBuilder::ray_casting_culled_adaptive_cumulative:
|
|
return FREESTYLE_ALGO_CULLED_ADAPTIVE_CUMULATIVE;
|
|
case ViewMapBuilder::ray_casting_adaptive_cumulative:
|
|
return FREESTYLE_ALGO_ADAPTIVE_CUMULATIVE;
|
|
}
|
|
|
|
// ray_casting_adaptive_traditional is the most exact replacement
|
|
// for legacy code
|
|
return FREESTYLE_ALGO_ADAPTIVE_TRADITIONAL;
|
|
}
|
|
|
|
void Controller::setViewMapCache(bool iBool)
|
|
{
|
|
_EnableViewMapCache = iBool;
|
|
}
|
|
|
|
bool Controller::getViewMapCache() const
|
|
{
|
|
return _EnableViewMapCache;
|
|
}
|
|
|
|
void Controller::setQuantitativeInvisibility(bool iBool)
|
|
{
|
|
_EnableQI = iBool;
|
|
}
|
|
|
|
bool Controller::getQuantitativeInvisibility() const
|
|
{
|
|
return _EnableQI;
|
|
}
|
|
|
|
void Controller::setFaceSmoothness(bool iBool)
|
|
{
|
|
_EnableFaceSmoothness = iBool;
|
|
}
|
|
|
|
bool Controller::getFaceSmoothness() const
|
|
{
|
|
return _EnableFaceSmoothness;
|
|
}
|
|
|
|
void Controller::setComputeRidgesAndValleysFlag(bool b)
|
|
{
|
|
_ComputeRidges = b;
|
|
}
|
|
|
|
bool Controller::getComputeRidgesAndValleysFlag() const
|
|
{
|
|
return _ComputeRidges;
|
|
}
|
|
|
|
void Controller::setComputeSuggestiveContoursFlag(bool b)
|
|
{
|
|
_ComputeSuggestive = b;
|
|
}
|
|
|
|
bool Controller::getComputeSuggestiveContoursFlag() const
|
|
{
|
|
return _ComputeSuggestive;
|
|
}
|
|
|
|
void Controller::setComputeMaterialBoundariesFlag(bool b)
|
|
{
|
|
_ComputeMaterialBoundaries = b;
|
|
}
|
|
|
|
bool Controller::getComputeMaterialBoundariesFlag() const
|
|
{
|
|
return _ComputeMaterialBoundaries;
|
|
}
|
|
|
|
void Controller::setComputeSteerableViewMapFlag(bool iBool)
|
|
{
|
|
_ComputeSteerableViewMap = iBool;
|
|
}
|
|
|
|
bool Controller::getComputeSteerableViewMapFlag() const
|
|
{
|
|
return _ComputeSteerableViewMap;
|
|
}
|
|
|
|
int Controller::DrawStrokes()
|
|
{
|
|
if (_ViewMap == nullptr) {
|
|
return 0;
|
|
}
|
|
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "\n=== Stroke drawing ===" << endl;
|
|
}
|
|
_Chrono.start();
|
|
_Canvas->Draw();
|
|
real d = _Chrono.stop();
|
|
int strokeCount = _Canvas->getStrokeCount();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Strokes generation : " << d << endl;
|
|
cout << "Stroke count : " << strokeCount << endl;
|
|
}
|
|
resetModified();
|
|
DeleteViewMap();
|
|
return strokeCount;
|
|
}
|
|
|
|
void Controller::ResetRenderCount()
|
|
{
|
|
_render_count = 0;
|
|
}
|
|
|
|
Render *Controller::RenderStrokes(Render *re, bool render)
|
|
{
|
|
int totmesh = 0;
|
|
_Chrono.start();
|
|
BlenderStrokeRenderer *blenderRenderer = new BlenderStrokeRenderer(re, ++_render_count);
|
|
if (render) {
|
|
_Canvas->Render(blenderRenderer);
|
|
totmesh = blenderRenderer->GenerateScene();
|
|
}
|
|
real d = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Temporary scene generation: " << d << endl;
|
|
}
|
|
_Chrono.start();
|
|
Render *freestyle_render = blenderRenderer->RenderScene(re, render);
|
|
d = _Chrono.stop();
|
|
if (G.debug & G_DEBUG_FREESTYLE) {
|
|
cout << "Stroke rendering : " << d << endl;
|
|
|
|
uintptr_t mem_in_use = MEM_get_memory_in_use();
|
|
uintptr_t peak_memory = MEM_get_peak_memory();
|
|
|
|
float megs_used_memory = (mem_in_use) / (1024.0 * 1024.0);
|
|
float megs_peak_memory = (peak_memory) / (1024.0 * 1024.0);
|
|
|
|
printf("%d objs, mem %.2fM (peak %.2fM)\n", totmesh, megs_used_memory, megs_peak_memory);
|
|
}
|
|
delete blenderRenderer;
|
|
|
|
return freestyle_render;
|
|
}
|
|
|
|
void Controller::InsertStyleModule(uint index, const char *iFileName)
|
|
{
|
|
if (!BLI_path_extension_check(iFileName, ".py")) {
|
|
cerr << "Error: Cannot load \"" << string(iFileName) << "\", unknown extension" << endl;
|
|
return;
|
|
}
|
|
|
|
StyleModule *sm = new StyleModule(iFileName, _inter);
|
|
_Canvas->InsertStyleModule(index, sm);
|
|
}
|
|
|
|
void Controller::InsertStyleModule(uint index, const char *iName, const char *iBuffer)
|
|
{
|
|
StyleModule *sm = new BufferedStyleModule(iBuffer, iName, _inter);
|
|
_Canvas->InsertStyleModule(index, sm);
|
|
}
|
|
|
|
void Controller::InsertStyleModule(uint index, const char *iName, Text *iText)
|
|
{
|
|
StyleModule *sm = new BlenderStyleModule(iText, iName, _inter);
|
|
_Canvas->InsertStyleModule(index, sm);
|
|
}
|
|
|
|
void Controller::AddStyleModule(const char * /*iFileName*/)
|
|
{
|
|
//_pStyleWindow->Add(iFileName);
|
|
}
|
|
|
|
void Controller::RemoveStyleModule(uint index)
|
|
{
|
|
_Canvas->RemoveStyleModule(index);
|
|
}
|
|
|
|
void Controller::Clear()
|
|
{
|
|
_Canvas->Clear();
|
|
}
|
|
|
|
void Controller::ReloadStyleModule(uint index, const char *iFileName)
|
|
{
|
|
StyleModule *sm = new StyleModule(iFileName, _inter);
|
|
_Canvas->ReplaceStyleModule(index, sm);
|
|
}
|
|
|
|
void Controller::SwapStyleModules(uint i1, uint i2)
|
|
{
|
|
_Canvas->SwapStyleModules(i1, i2);
|
|
}
|
|
|
|
void Controller::toggleLayer(uint index, bool iDisplay)
|
|
{
|
|
_Canvas->setVisible(index, iDisplay);
|
|
}
|
|
|
|
void Controller::setModified(uint index, bool iMod)
|
|
{
|
|
//_pStyleWindow->setModified(index, iMod);
|
|
_Canvas->setModified(index, iMod);
|
|
updateCausalStyleModules(index + 1);
|
|
}
|
|
|
|
void Controller::updateCausalStyleModules(uint index)
|
|
{
|
|
vector<uint> vec;
|
|
_Canvas->causalStyleModules(vec, index);
|
|
for (vector<uint>::const_iterator it = vec.begin(); it != vec.end(); it++) {
|
|
//_pStyleWindow->setModified(*it, true);
|
|
_Canvas->setModified(*it, true);
|
|
}
|
|
}
|
|
|
|
void Controller::resetModified(bool iMod)
|
|
{
|
|
//_pStyleWindow->resetModified(iMod);
|
|
_Canvas->resetModified(iMod);
|
|
}
|
|
|
|
NodeGroup *Controller::BuildRep(vector<ViewEdge *>::iterator vedges_begin,
|
|
vector<ViewEdge *>::iterator vedges_end)
|
|
{
|
|
ViewMapTesselator2D tesselator2D;
|
|
FrsMaterial mat;
|
|
mat.setDiffuse(1, 1, 0.3, 1);
|
|
tesselator2D.setFrsMaterial(mat);
|
|
|
|
return tesselator2D.Tesselate(vedges_begin, vedges_end);
|
|
}
|
|
|
|
void Controller::toggleEdgeTesselationNature(Nature::EdgeNature iNature)
|
|
{
|
|
_edgeTesselationNature ^= (iNature);
|
|
ComputeViewMap();
|
|
}
|
|
|
|
void Controller::setModelsDir(const string & /*dir*/)
|
|
{
|
|
//_current_dirs->setValue("models/dir", dir);
|
|
}
|
|
|
|
string Controller::getModelsDir() const
|
|
{
|
|
string dir = ".";
|
|
//_current_dirs->getValue("models/dir", dir);
|
|
return dir;
|
|
}
|
|
|
|
void Controller::setModulesDir(const string & /*dir*/)
|
|
{
|
|
//_current_dirs->setValue("modules/dir", dir);
|
|
}
|
|
|
|
string Controller::getModulesDir() const
|
|
{
|
|
string dir = ".";
|
|
//_current_dirs->getValue("modules/dir", dir);
|
|
return dir;
|
|
}
|
|
|
|
void Controller::resetInterpreter()
|
|
{
|
|
if (_inter) {
|
|
_inter->reset();
|
|
}
|
|
}
|
|
|
|
void Controller::displayDensityCurves(int x, int y)
|
|
{
|
|
SteerableViewMap *svm = _Canvas->getSteerableViewMap();
|
|
if (!svm) {
|
|
return;
|
|
}
|
|
|
|
uint i, j;
|
|
using densityCurve = vector<Vec3r>;
|
|
vector<densityCurve> curves(svm->getNumberOfOrientations() + 1);
|
|
vector<densityCurve> curvesDirection(svm->getNumberOfPyramidLevels());
|
|
|
|
// collect the curves values
|
|
uint nbCurves = svm->getNumberOfOrientations() + 1;
|
|
uint nbPoints = svm->getNumberOfPyramidLevels();
|
|
if (!nbPoints) {
|
|
return;
|
|
}
|
|
|
|
// build the density/nbLevels curves for each orientation
|
|
for (i = 0; i < nbCurves; ++i) {
|
|
for (j = 0; j < nbPoints; ++j) {
|
|
curves[i].push_back(Vec3r(j, svm->readSteerableViewMapPixel(i, j, x, y), 0));
|
|
}
|
|
}
|
|
// build the density/nbOrientations curves for each level
|
|
for (i = 0; i < nbPoints; ++i) {
|
|
for (j = 0; j < nbCurves; ++j) {
|
|
curvesDirection[i].push_back(Vec3r(j, svm->readSteerableViewMapPixel(j, i, x, y), 0));
|
|
}
|
|
}
|
|
|
|
// display the curves
|
|
#if 0
|
|
for (i = 0; i < nbCurves; ++i) {
|
|
_pDensityCurvesWindow->setOrientationCurve(
|
|
i, Vec2d(0, 0), Vec2d(nbPoints, 1), curves[i], "scale", "density");
|
|
}
|
|
for (i = 1; i <= 8; ++i) {
|
|
_pDensityCurvesWindow->setLevelCurve(
|
|
i, Vec2d(0, 0), Vec2d(nbCurves, 1), curvesDirection[i], "orientation", "density");
|
|
}
|
|
_pDensityCurvesWindow->show();
|
|
#endif
|
|
}
|
|
|
|
void Controller::init_options()
|
|
{
|
|
// from AppOptionsWindow.cpp
|
|
// Default init options
|
|
|
|
Config::Path *cpath = Config::Path::getInstance();
|
|
|
|
// Directories
|
|
TextureManager::Options::setPatternsPath(cpath->getPatternsPath());
|
|
TextureManager::Options::setBrushesPath(cpath->getModelsPath());
|
|
|
|
// ViewMap Format
|
|
setComputeSteerableViewMapFlag(false);
|
|
|
|
// Visibility
|
|
setQuantitativeInvisibility(true);
|
|
|
|
// soc: initialize canvas
|
|
_Canvas->init();
|
|
|
|
// soc: initialize passes
|
|
setPassDiffuse(nullptr, 0, 0);
|
|
setPassZ(nullptr, 0, 0);
|
|
}
|
|
|
|
} /* namespace Freestyle */
|