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.
356 lines
9.8 KiB
C++
356 lines
9.8 KiB
C++
/* SPDX-FileCopyrightText: 2008-2022 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup freestyle
|
|
* \brief Fredo's stroke shaders
|
|
*/
|
|
|
|
#include "AdvancedStrokeShaders.h"
|
|
#include "StrokeIterators.h"
|
|
|
|
#include "../system/PseudoNoise.h"
|
|
#include "../system/RandGen.h"
|
|
|
|
#include "BLI_sys_types.h"
|
|
|
|
namespace Freestyle {
|
|
|
|
/////////////////////////////////////////
|
|
//
|
|
// CALLIGRAPHICS SHADER
|
|
//
|
|
/////////////////////////////////////////
|
|
|
|
CalligraphicShader::CalligraphicShader(real iMinThickness,
|
|
real iMaxThickness,
|
|
const Vec2f &iOrientation,
|
|
bool clamp)
|
|
{
|
|
_minThickness = iMinThickness;
|
|
_maxThickness = iMaxThickness;
|
|
_orientation = iOrientation;
|
|
_orientation.normalize();
|
|
_clamp = clamp;
|
|
}
|
|
|
|
int CalligraphicShader::shade(Stroke &ioStroke) const
|
|
{
|
|
Interface0DIterator v;
|
|
Functions0D::VertexOrientation2DF0D fun;
|
|
StrokeVertex *sv;
|
|
for (v = ioStroke.verticesBegin(); !v.isEnd(); ++v) {
|
|
real thickness;
|
|
if (fun(v) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
Vec2f vertexOri(fun.result);
|
|
Vec2r ori2d(-vertexOri[1], vertexOri[0]);
|
|
ori2d.normalizeSafe();
|
|
real scal = ori2d * _orientation;
|
|
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
|
if (_clamp && (scal < 0)) {
|
|
scal = 0.0;
|
|
sv->attribute().setColor(1, 1, 1);
|
|
}
|
|
else {
|
|
scal = fabs(scal);
|
|
sv->attribute().setColor(0, 0, 0);
|
|
}
|
|
thickness = _minThickness + scal * (_maxThickness - _minThickness);
|
|
if (thickness < 0.0) {
|
|
thickness = 0.0;
|
|
}
|
|
sv->attribute().setThickness(thickness / 2.0, thickness / 2.0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
//
|
|
// SPATIAL NOISE SHADER
|
|
//
|
|
/////////////////////////////////////////
|
|
|
|
static const uint NB_VALUE_NOISE = 512;
|
|
|
|
SpatialNoiseShader::SpatialNoiseShader(
|
|
float iAmount, float ixScale, int nbOctave, bool smooth, bool pureRandom)
|
|
{
|
|
_amount = iAmount;
|
|
if (ixScale == 0) {
|
|
_xScale = 0;
|
|
}
|
|
else {
|
|
_xScale = 1.0 / ixScale / real(NB_VALUE_NOISE);
|
|
}
|
|
_nbOctave = nbOctave;
|
|
_smooth = smooth;
|
|
_pureRandom = pureRandom;
|
|
}
|
|
|
|
int SpatialNoiseShader::shade(Stroke &ioStroke) const
|
|
{
|
|
Interface0DIterator v, v2;
|
|
v = ioStroke.verticesBegin();
|
|
Vec2r p(v->getProjectedX(), v->getProjectedY());
|
|
v2 = v;
|
|
++v2;
|
|
Vec2r p0(v2->getProjectedX(), v2->getProjectedY());
|
|
p0 = p + 2 * (p - p0);
|
|
StrokeVertex *sv;
|
|
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
|
real initU = sv->strokeLength() * real(NB_VALUE_NOISE);
|
|
if (_pureRandom) {
|
|
initU += RandGen::drand48() * real(NB_VALUE_NOISE);
|
|
}
|
|
|
|
Functions0D::VertexOrientation2DF0D fun;
|
|
while (!v.isEnd()) {
|
|
sv = dynamic_cast<StrokeVertex *>(&(*v));
|
|
Vec2r p(sv->getPoint());
|
|
if (fun(v) < 0) {
|
|
return -1;
|
|
}
|
|
Vec2r vertexOri(fun.result);
|
|
Vec2r ori2d(vertexOri[0], vertexOri[1]);
|
|
ori2d = Vec2r(p - p0);
|
|
ori2d.normalizeSafe();
|
|
|
|
PseudoNoise mynoise;
|
|
real bruit;
|
|
|
|
if (_smooth) {
|
|
bruit = mynoise.turbulenceSmooth(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave);
|
|
}
|
|
else {
|
|
bruit = mynoise.turbulenceLinear(_xScale * sv->curvilinearAbscissa() + initU, _nbOctave);
|
|
}
|
|
|
|
Vec2r noise(-ori2d[1] * _amount * bruit, ori2d[0] * _amount * bruit);
|
|
|
|
sv->setPoint(p[0] + noise[0], p[1] + noise[1]);
|
|
p0 = p;
|
|
|
|
++v;
|
|
}
|
|
|
|
ioStroke.UpdateLength();
|
|
|
|
return 0;
|
|
}
|
|
|
|
/////////////////////////////////////////
|
|
//
|
|
// SMOOTHING SHADER
|
|
//
|
|
/////////////////////////////////////////
|
|
|
|
SmoothingShader::SmoothingShader(int iNbIteration,
|
|
real iFactorPoint,
|
|
real ifactorCurvature,
|
|
real iFactorCurvatureDifference,
|
|
real iAnisoPoint,
|
|
real iAnisoNormal,
|
|
real iAnisoCurvature,
|
|
real iCarricatureFactor)
|
|
{
|
|
_nbIterations = iNbIteration;
|
|
_factorCurvature = ifactorCurvature;
|
|
_factorCurvatureDifference = iFactorCurvatureDifference;
|
|
_anisoNormal = iAnisoNormal;
|
|
_anisoCurvature = iAnisoCurvature;
|
|
_carricatureFactor = iCarricatureFactor;
|
|
_factorPoint = iFactorPoint;
|
|
_anisoPoint = iAnisoPoint;
|
|
}
|
|
|
|
int SmoothingShader::shade(Stroke &ioStroke) const
|
|
{
|
|
// cerr << " Smoothing a stroke " << endl;
|
|
|
|
Smoother smoother(ioStroke);
|
|
smoother.smooth(_nbIterations,
|
|
_factorPoint,
|
|
_factorCurvature,
|
|
_factorCurvatureDifference,
|
|
_anisoPoint,
|
|
_anisoNormal,
|
|
_anisoCurvature,
|
|
_carricatureFactor);
|
|
return 0;
|
|
}
|
|
|
|
// SMOOTHER
|
|
////////////////////////////
|
|
|
|
Smoother::Smoother(Stroke &ioStroke)
|
|
{
|
|
_stroke = &ioStroke;
|
|
|
|
_nbVertices = ioStroke.vertices_size();
|
|
_vertex = new Vec2r[_nbVertices];
|
|
_curvature = new real[_nbVertices];
|
|
_normal = new Vec2r[_nbVertices];
|
|
StrokeInternal::StrokeVertexIterator v, vend;
|
|
int i = 0;
|
|
for (v = ioStroke.strokeVerticesBegin(), vend = ioStroke.strokeVerticesEnd(); v != vend;
|
|
++v, ++i) {
|
|
_vertex[i] = (v)->getPoint();
|
|
}
|
|
Vec2r vec_tmp(_vertex[0] - _vertex[_nbVertices - 1]);
|
|
_isClosedCurve = (vec_tmp.norm() < M_EPSILON);
|
|
|
|
_safeTest = (_nbVertices > 4);
|
|
}
|
|
|
|
Smoother::~Smoother()
|
|
{
|
|
delete[] _vertex;
|
|
delete[] _curvature;
|
|
delete[] _normal;
|
|
}
|
|
|
|
void Smoother::smooth(int nbIteration,
|
|
real iFactorPoint,
|
|
real ifactorCurvature,
|
|
real iFactorCurvatureDifference,
|
|
real iAnisoPoint,
|
|
real iAnisoNormal,
|
|
real iAnisoCurvature,
|
|
real iCarricatureFactor)
|
|
{
|
|
_factorCurvature = ifactorCurvature;
|
|
_factorCurvatureDifference = iFactorCurvatureDifference;
|
|
_anisoNormal = iAnisoNormal;
|
|
_anisoCurvature = iAnisoCurvature;
|
|
_carricatureFactor = iCarricatureFactor;
|
|
_factorPoint = iFactorPoint;
|
|
_anisoPoint = iAnisoPoint;
|
|
|
|
for (int i = 0; i < nbIteration; ++i) {
|
|
iteration();
|
|
}
|
|
copyVertices();
|
|
}
|
|
|
|
static real edgeStopping(real x, real sigma)
|
|
{
|
|
if (sigma == 0.0) {
|
|
return 1.0;
|
|
}
|
|
return exp(-x * x / (sigma * sigma));
|
|
}
|
|
|
|
void Smoother::iteration()
|
|
{
|
|
computeCurvature();
|
|
for (int i = 1; i < (_nbVertices - 1); ++i) {
|
|
real motionNormal = _factorCurvature * _curvature[i] *
|
|
edgeStopping(_curvature[i], _anisoNormal);
|
|
|
|
real diffC1 = _curvature[i] - _curvature[i - 1];
|
|
real diffC2 = _curvature[i] - _curvature[i + 1];
|
|
real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 +
|
|
edgeStopping(diffC2, _anisoCurvature) *
|
|
diffC2; //_factorCurvatureDifference;
|
|
motionCurvature *= _factorCurvatureDifference;
|
|
// motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2);
|
|
if (_safeTest) {
|
|
_vertex[i] = Vec2r(_vertex[i] + (motionNormal + motionCurvature) * _normal[i]);
|
|
}
|
|
Vec2r v1(_vertex[i - 1] - _vertex[i]);
|
|
Vec2r v2(_vertex[i + 1] - _vertex[i]);
|
|
real d1 = v1.norm();
|
|
real d2 = v2.norm();
|
|
_vertex[i] = Vec2r(
|
|
_vertex[i] + _factorPoint * edgeStopping(d2, _anisoPoint) * (_vertex[i - 1] - _vertex[i]) +
|
|
_factorPoint * edgeStopping(d1, _anisoPoint) * (_vertex[i + 1] - _vertex[i]));
|
|
}
|
|
|
|
if (_isClosedCurve) {
|
|
real motionNormal = _factorCurvature * _curvature[0] *
|
|
edgeStopping(_curvature[0], _anisoNormal);
|
|
|
|
real diffC1 = _curvature[0] - _curvature[_nbVertices - 2];
|
|
real diffC2 = _curvature[0] - _curvature[1];
|
|
real motionCurvature = edgeStopping(diffC1, _anisoCurvature) * diffC1 +
|
|
edgeStopping(diffC2, _anisoCurvature) *
|
|
diffC2; //_factorCurvatureDifference;
|
|
motionCurvature *= _factorCurvatureDifference;
|
|
// motionCurvature = _factorCurvatureDifference * (diffC1 + diffC2);
|
|
_vertex[0] = Vec2r(_vertex[0] + (motionNormal + motionCurvature) * _normal[0]);
|
|
_vertex[_nbVertices - 1] = _vertex[0];
|
|
}
|
|
}
|
|
|
|
void Smoother::computeCurvature()
|
|
{
|
|
int i;
|
|
Vec2r BA, BC, normalCurvature;
|
|
for (i = 1; i < (_nbVertices - 1); ++i) {
|
|
BA = _vertex[i - 1] - _vertex[i];
|
|
BC = _vertex[i + 1] - _vertex[i];
|
|
real lba = BA.norm(), lbc = BC.norm();
|
|
BA.normalizeSafe();
|
|
BC.normalizeSafe();
|
|
normalCurvature = BA + BC;
|
|
|
|
_normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]);
|
|
_normal[i].normalizeSafe();
|
|
|
|
_curvature[i] = normalCurvature * _normal[i];
|
|
if (lba + lbc > M_EPSILON) {
|
|
_curvature[i] /= (0.5 * lba + lbc);
|
|
}
|
|
}
|
|
_curvature[0] = _curvature[1];
|
|
_curvature[_nbVertices - 1] = _curvature[_nbVertices - 2];
|
|
Vec2r di(_vertex[1] - _vertex[0]);
|
|
_normal[0] = Vec2r(-di[1], di[0]);
|
|
_normal[0].normalizeSafe();
|
|
di = _vertex[_nbVertices - 1] - _vertex[_nbVertices - 2];
|
|
_normal[_nbVertices - 1] = Vec2r(-di[1], di[0]);
|
|
_normal[_nbVertices - 1].normalizeSafe();
|
|
|
|
if (_isClosedCurve) {
|
|
BA = _vertex[_nbVertices - 2] - _vertex[0];
|
|
BC = _vertex[1] - _vertex[0];
|
|
real lba = BA.norm(), lbc = BC.norm();
|
|
BA.normalizeSafe();
|
|
BC.normalizeSafe();
|
|
normalCurvature = BA + BC;
|
|
|
|
_normal[i] = Vec2r(-(BC - BA)[1], (BC - BA)[0]);
|
|
_normal[i].normalizeSafe();
|
|
|
|
_curvature[i] = normalCurvature * _normal[i];
|
|
if (lba + lbc > M_EPSILON) {
|
|
_curvature[i] /= (0.5 * lba + lbc);
|
|
}
|
|
|
|
_normal[_nbVertices - 1] = _normal[0];
|
|
_curvature[_nbVertices - 1] = _curvature[0];
|
|
}
|
|
}
|
|
|
|
void Smoother::copyVertices()
|
|
{
|
|
int i = 0;
|
|
StrokeInternal::StrokeVertexIterator v, vend;
|
|
for (v = _stroke->strokeVerticesBegin(), vend = _stroke->strokeVerticesEnd(); v != vend; ++v) {
|
|
const Vec2r p0((v)->getPoint());
|
|
const Vec2r p1(_vertex[i]);
|
|
Vec2r p(p0 + _carricatureFactor * (p1 - p0));
|
|
|
|
(v)->setPoint(p[0], p[1]);
|
|
++i;
|
|
}
|
|
_stroke->UpdateLength();
|
|
}
|
|
|
|
} /* namespace Freestyle */
|