Merge branch 'master' into xr-dev
This commit is contained in:
commit
b33aaf5a2c
|
@ -889,8 +889,8 @@ if(WITH_PYTHON)
|
|||
# Do this before main 'platform_*' checks,
|
||||
# because UNIX will search for the old Python paths which may not exist.
|
||||
# giving errors about missing paths before this case is met.
|
||||
if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.9")
|
||||
message(FATAL_ERROR "At least Python 3.9 is required to build, but found Python ${PYTHON_VERSION}")
|
||||
if(DEFINED PYTHON_VERSION AND "${PYTHON_VERSION}" VERSION_LESS "3.10")
|
||||
message(FATAL_ERROR "At least Python 3.10 is required to build, but found Python ${PYTHON_VERSION}")
|
||||
endif()
|
||||
|
||||
file(GLOB RESULT "${CMAKE_SOURCE_DIR}/release/scripts/addons")
|
||||
|
|
|
@ -366,7 +366,7 @@ CLANG_FORMAT_VERSION_MEX="14.0"
|
|||
|
||||
PYTHON_VERSION="3.10.2"
|
||||
PYTHON_VERSION_SHORT="3.10"
|
||||
PYTHON_VERSION_MIN="3.9"
|
||||
PYTHON_VERSION_MIN="3.10"
|
||||
PYTHON_VERSION_MEX="3.12"
|
||||
PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT
|
||||
PYTHON_FORCE_BUILD=false
|
||||
|
|
|
@ -24,56 +24,103 @@
|
|||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
layout(local_size_x=WORK_GROUP_SIZE, local_size_y=1, local_size_z=1) in;
|
||||
layout(local_size_x = WORK_GROUP_SIZE, local_size_y = 1, local_size_z = 1) in;
|
||||
layout(std430) buffer;
|
||||
|
||||
// source and destination buffers
|
||||
|
||||
uniform int srcOffset = 0;
|
||||
uniform int dstOffset = 0;
|
||||
layout(binding=0) buffer src_buffer { float srcVertexBuffer[]; };
|
||||
layout(binding=1) buffer dst_buffer { float dstVertexBuffer[]; };
|
||||
layout(binding = 0) buffer src_buffer
|
||||
{
|
||||
float srcVertexBuffer[];
|
||||
};
|
||||
layout(binding = 1) buffer dst_buffer
|
||||
{
|
||||
float dstVertexBuffer[];
|
||||
};
|
||||
|
||||
// derivative buffers (if needed)
|
||||
// derivative buffers (if needed)
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
uniform ivec3 duDesc;
|
||||
uniform ivec3 dvDesc;
|
||||
layout(binding=2) buffer du_buffer { float duBuffer[]; };
|
||||
layout(binding=3) buffer dv_buffer { float dvBuffer[]; };
|
||||
layout(binding = 2) buffer du_buffer
|
||||
{
|
||||
float duBuffer[];
|
||||
};
|
||||
layout(binding = 3) buffer dv_buffer
|
||||
{
|
||||
float dvBuffer[];
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
uniform ivec3 duuDesc;
|
||||
uniform ivec3 duvDesc;
|
||||
uniform ivec3 dvvDesc;
|
||||
layout(binding=10) buffer duu_buffer { float duuBuffer[]; };
|
||||
layout(binding=11) buffer duv_buffer { float duvBuffer[]; };
|
||||
layout(binding=12) buffer dvv_buffer { float dvvBuffer[]; };
|
||||
layout(binding = 10) buffer duu_buffer
|
||||
{
|
||||
float duuBuffer[];
|
||||
};
|
||||
layout(binding = 11) buffer duv_buffer
|
||||
{
|
||||
float duvBuffer[];
|
||||
};
|
||||
layout(binding = 12) buffer dvv_buffer
|
||||
{
|
||||
float dvvBuffer[];
|
||||
};
|
||||
#endif
|
||||
|
||||
// stencil buffers
|
||||
// stencil buffers
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
|
||||
|
||||
uniform int batchStart = 0;
|
||||
uniform int batchEnd = 0;
|
||||
layout(binding=4) buffer stencilSizes { int _sizes[]; };
|
||||
layout(binding=5) buffer stencilOffsets { int _offsets[]; };
|
||||
layout(binding=6) buffer stencilIndices { int _indices[]; };
|
||||
layout(binding=7) buffer stencilWeights { float _weights[]; };
|
||||
layout(binding = 4) buffer stencilSizes
|
||||
{
|
||||
int _sizes[];
|
||||
};
|
||||
layout(binding = 5) buffer stencilOffsets
|
||||
{
|
||||
int _offsets[];
|
||||
};
|
||||
layout(binding = 6) buffer stencilIndices
|
||||
{
|
||||
int _indices[];
|
||||
};
|
||||
layout(binding = 7) buffer stencilWeights
|
||||
{
|
||||
float _weights[];
|
||||
};
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
layout(binding=8) buffer stencilDuWeights { float _duWeights[]; };
|
||||
layout(binding=9) buffer stencilDvWeights { float _dvWeights[]; };
|
||||
#endif
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
layout(binding = 8) buffer stencilDuWeights
|
||||
{
|
||||
float _duWeights[];
|
||||
};
|
||||
layout(binding = 9) buffer stencilDvWeights
|
||||
{
|
||||
float _dvWeights[];
|
||||
};
|
||||
# endif
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
layout(binding=13) buffer stencilDuuWeights { float _duuWeights[]; };
|
||||
layout(binding=14) buffer stencilDuvWeights { float _duvWeights[]; };
|
||||
layout(binding=15) buffer stencilDvvWeights { float _dvvWeights[]; };
|
||||
#endif
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
layout(binding = 13) buffer stencilDuuWeights
|
||||
{
|
||||
float _duuWeights[];
|
||||
};
|
||||
layout(binding = 14) buffer stencilDuvWeights
|
||||
{
|
||||
float _duvWeights[];
|
||||
};
|
||||
layout(binding = 15) buffer stencilDvvWeights
|
||||
{
|
||||
float _dvvWeights[];
|
||||
};
|
||||
# endif
|
||||
|
||||
uint getGlobalInvocationIndex()
|
||||
{
|
||||
|
@ -87,24 +134,36 @@ uint getGlobalInvocationIndex()
|
|||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_PATCHES)
|
||||
|
||||
layout(binding=4) buffer patchArray_buffer { OsdPatchArray patchArrayBuffer[]; };
|
||||
layout(binding=5) buffer patchCoord_buffer { OsdPatchCoord patchCoords[]; };
|
||||
layout(binding=6) buffer patchIndex_buffer { int patchIndexBuffer[]; };
|
||||
layout(binding=7) buffer patchParam_buffer { OsdPatchParam patchParamBuffer[]; };
|
||||
layout(binding = 4) buffer patchArray_buffer
|
||||
{
|
||||
OsdPatchArray patchArrayBuffer[];
|
||||
};
|
||||
layout(binding = 5) buffer patchCoord_buffer
|
||||
{
|
||||
OsdPatchCoord patchCoords[];
|
||||
};
|
||||
layout(binding = 6) buffer patchIndex_buffer
|
||||
{
|
||||
int patchIndexBuffer[];
|
||||
};
|
||||
layout(binding = 7) buffer patchParam_buffer
|
||||
{
|
||||
OsdPatchParam patchParamBuffer[];
|
||||
};
|
||||
|
||||
OsdPatchCoord GetPatchCoord(int coordIndex)
|
||||
{
|
||||
return patchCoords[coordIndex];
|
||||
return patchCoords[coordIndex];
|
||||
}
|
||||
|
||||
OsdPatchArray GetPatchArray(int arrayIndex)
|
||||
{
|
||||
return patchArrayBuffer[arrayIndex];
|
||||
return patchArrayBuffer[arrayIndex];
|
||||
}
|
||||
|
||||
OsdPatchParam GetPatchParam(int patchIndex)
|
||||
{
|
||||
return patchParamBuffer[patchIndex];
|
||||
return patchParamBuffer[patchIndex];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -112,141 +171,149 @@ OsdPatchParam GetPatchParam(int patchIndex)
|
|||
//------------------------------------------------------------------------------
|
||||
|
||||
struct Vertex {
|
||||
float vertexData[LENGTH];
|
||||
float vertexData[LENGTH];
|
||||
};
|
||||
|
||||
void clear(out Vertex v) {
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = 0;
|
||||
}
|
||||
void clear(out Vertex v)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Vertex readVertex(int index) {
|
||||
Vertex v;
|
||||
int vertexIndex = srcOffset + index * SRC_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
|
||||
}
|
||||
return v;
|
||||
Vertex readVertex(int index)
|
||||
{
|
||||
Vertex v;
|
||||
int vertexIndex = srcOffset + index * SRC_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] = srcVertexBuffer[vertexIndex + i];
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void writeVertex(int index, Vertex v) {
|
||||
int vertexIndex = dstOffset + index * DST_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
|
||||
}
|
||||
void writeVertex(int index, Vertex v)
|
||||
{
|
||||
int vertexIndex = dstOffset + index * DST_STRIDE;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dstVertexBuffer[vertexIndex + i] = v.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void addWithWeight(inout Vertex v, const Vertex src, float weight) {
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] += weight * src.vertexData[i];
|
||||
}
|
||||
void addWithWeight(inout Vertex v, const Vertex src, float weight)
|
||||
{
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
v.vertexData[i] += weight * src.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
void writeDu(int index, Vertex du) {
|
||||
int duIndex = duDesc.x + index * duDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duBuffer[duIndex + i] = du.vertexData[i];
|
||||
}
|
||||
void writeDu(int index, Vertex du)
|
||||
{
|
||||
int duIndex = duDesc.x + index * duDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duBuffer[duIndex + i] = du.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void writeDv(int index, Vertex dv) {
|
||||
int dvIndex = dvDesc.x + index * dvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvBuffer[dvIndex + i] = dv.vertexData[i];
|
||||
}
|
||||
void writeDv(int index, Vertex dv)
|
||||
{
|
||||
int dvIndex = dvDesc.x + index * dvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvBuffer[dvIndex + i] = dv.vertexData[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
void writeDuu(int index, Vertex duu) {
|
||||
int duuIndex = duuDesc.x + index * duuDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duuBuffer[duuIndex + i] = duu.vertexData[i];
|
||||
}
|
||||
void writeDuu(int index, Vertex duu)
|
||||
{
|
||||
int duuIndex = duuDesc.x + index * duuDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duuBuffer[duuIndex + i] = duu.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void writeDuv(int index, Vertex duv) {
|
||||
int duvIndex = duvDesc.x + index * duvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duvBuffer[duvIndex + i] = duv.vertexData[i];
|
||||
}
|
||||
void writeDuv(int index, Vertex duv)
|
||||
{
|
||||
int duvIndex = duvDesc.x + index * duvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
duvBuffer[duvIndex + i] = duv.vertexData[i];
|
||||
}
|
||||
}
|
||||
|
||||
void writeDvv(int index, Vertex dvv) {
|
||||
int dvvIndex = dvvDesc.x + index * dvvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvvBuffer[dvvIndex + i] = dvv.vertexData[i];
|
||||
}
|
||||
void writeDvv(int index, Vertex dvv)
|
||||
{
|
||||
int dvvIndex = dvvDesc.x + index * dvvDesc.z;
|
||||
for (int i = 0; i < LENGTH; ++i) {
|
||||
dvvBuffer[dvvIndex + i] = dvv.vertexData[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_KERNEL_EVAL_STENCILS)
|
||||
|
||||
void main() {
|
||||
int current = int(getGlobalInvocationIndex()) + batchStart;
|
||||
void main()
|
||||
{
|
||||
int current = int(getGlobalInvocationIndex()) + batchStart;
|
||||
|
||||
if (current>=batchEnd) {
|
||||
return;
|
||||
}
|
||||
if (current >= batchEnd) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vertex dst;
|
||||
clear(dst);
|
||||
Vertex dst;
|
||||
clear(dst);
|
||||
|
||||
int offset = _offsets[current],
|
||||
size = _sizes[current];
|
||||
int offset = _offsets[current], size = _sizes[current];
|
||||
|
||||
for (int stencil = 0; stencil < size; ++stencil) {
|
||||
int vindex = offset + stencil;
|
||||
addWithWeight(
|
||||
dst, readVertex(_indices[vindex]), _weights[vindex]);
|
||||
}
|
||||
for (int stencil = 0; stencil < size; ++stencil) {
|
||||
int vindex = offset + stencil;
|
||||
addWithWeight(dst, readVertex(_indices[vindex]), _weights[vindex]);
|
||||
}
|
||||
|
||||
writeVertex(current, dst);
|
||||
writeVertex(current, dst);
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
Vertex du, dv;
|
||||
clear(du);
|
||||
clear(dv);
|
||||
for (int i=0; i<size; ++i) {
|
||||
// expects the compiler optimizes readVertex out here.
|
||||
Vertex src = readVertex(_indices[offset+i]);
|
||||
addWithWeight(du, src, _duWeights[offset+i]);
|
||||
addWithWeight(dv, src, _dvWeights[offset+i]);
|
||||
}
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
Vertex du, dv;
|
||||
clear(du);
|
||||
clear(dv);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
// expects the compiler optimizes readVertex out here.
|
||||
Vertex src = readVertex(_indices[offset + i]);
|
||||
addWithWeight(du, src, _duWeights[offset + i]);
|
||||
addWithWeight(dv, src, _dvWeights[offset + i]);
|
||||
}
|
||||
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
#endif
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
Vertex duu, duv, dvv;
|
||||
clear(duu);
|
||||
clear(duv);
|
||||
clear(dvv);
|
||||
for (int i=0; i<size; ++i) {
|
||||
// expects the compiler optimizes readVertex out here.
|
||||
Vertex src = readVertex(_indices[offset+i]);
|
||||
addWithWeight(duu, src, _duuWeights[offset+i]);
|
||||
addWithWeight(duv, src, _duvWeights[offset+i]);
|
||||
addWithWeight(dvv, src, _dvvWeights[offset+i]);
|
||||
}
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
# endif
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
Vertex duu, duv, dvv;
|
||||
clear(duu);
|
||||
clear(duv);
|
||||
clear(dvv);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
// expects the compiler optimizes readVertex out here.
|
||||
Vertex src = readVertex(_indices[offset + i]);
|
||||
addWithWeight(duu, src, _duuWeights[offset + i]);
|
||||
addWithWeight(duv, src, _duvWeights[offset + i]);
|
||||
addWithWeight(dvv, src, _dvvWeights[offset + i]);
|
||||
}
|
||||
|
||||
if (duuDesc.y > 0) { // length
|
||||
writeDuu(current, duu);
|
||||
}
|
||||
if (duvDesc.y > 0) {
|
||||
writeDuv(current, duv);
|
||||
}
|
||||
if (dvvDesc.y > 0) {
|
||||
writeDvv(current, dvv);
|
||||
}
|
||||
#endif
|
||||
if (duuDesc.y > 0) { // length
|
||||
writeDuu(current, duu);
|
||||
}
|
||||
if (duvDesc.y > 0) {
|
||||
writeDuv(current, duv);
|
||||
}
|
||||
if (dvvDesc.y > 0) {
|
||||
writeDvv(current, dvv);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -256,61 +323,61 @@ void main() {
|
|||
|
||||
// PERFORMANCE: stride could be constant, but not as significant as length
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
|
||||
int current = int(gl_GlobalInvocationID.x);
|
||||
int current = int(gl_GlobalInvocationID.x);
|
||||
|
||||
OsdPatchCoord coord = GetPatchCoord(current);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
OsdPatchCoord coord = GetPatchCoord(current);
|
||||
OsdPatchArray array = GetPatchArray(coord.arrayIndex);
|
||||
OsdPatchParam param = GetPatchParam(coord.patchIndex);
|
||||
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
int patchType = OsdPatchParamIsRegular(param) ? array.regDesc : array.desc;
|
||||
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(patchType, param,
|
||||
coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
float wP[20], wDu[20], wDv[20], wDuu[20], wDuv[20], wDvv[20];
|
||||
int nPoints = OsdEvaluatePatchBasis(
|
||||
patchType, param, coord.s, coord.t, wP, wDu, wDv, wDuu, wDuv, wDvv);
|
||||
|
||||
Vertex dst, du, dv, duu, duv, dvv;
|
||||
clear(dst);
|
||||
clear(du);
|
||||
clear(dv);
|
||||
clear(duu);
|
||||
clear(duv);
|
||||
clear(dvv);
|
||||
Vertex dst, du, dv, duu, duv, dvv;
|
||||
clear(dst);
|
||||
clear(du);
|
||||
clear(dv);
|
||||
clear(duu);
|
||||
clear(duv);
|
||||
clear(dvv);
|
||||
|
||||
int indexBase = array.indexBase + array.stride *
|
||||
(coord.patchIndex - array.primitiveIdBase);
|
||||
int indexBase = array.indexBase + array.stride * (coord.patchIndex - array.primitiveIdBase);
|
||||
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
addWithWeight(dst, readVertex(index), wP[cv]);
|
||||
addWithWeight(du, readVertex(index), wDu[cv]);
|
||||
addWithWeight(dv, readVertex(index), wDv[cv]);
|
||||
addWithWeight(duu, readVertex(index), wDuu[cv]);
|
||||
addWithWeight(duv, readVertex(index), wDuv[cv]);
|
||||
addWithWeight(dvv, readVertex(index), wDvv[cv]);
|
||||
}
|
||||
writeVertex(current, dst);
|
||||
for (int cv = 0; cv < nPoints; ++cv) {
|
||||
int index = patchIndexBuffer[indexBase + cv];
|
||||
addWithWeight(dst, readVertex(index), wP[cv]);
|
||||
addWithWeight(du, readVertex(index), wDu[cv]);
|
||||
addWithWeight(dv, readVertex(index), wDv[cv]);
|
||||
addWithWeight(duu, readVertex(index), wDuu[cv]);
|
||||
addWithWeight(duv, readVertex(index), wDuv[cv]);
|
||||
addWithWeight(dvv, readVertex(index), wDvv[cv]);
|
||||
}
|
||||
writeVertex(current, dst);
|
||||
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
#endif
|
||||
#if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
if (duuDesc.y > 0) { // length
|
||||
writeDuu(current, duu);
|
||||
}
|
||||
if (duvDesc.y > 0) { // length
|
||||
writeDuv(current, duv);
|
||||
}
|
||||
if (dvvDesc.y > 0) {
|
||||
writeDvv(current, dvv);
|
||||
}
|
||||
#endif
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_1ST_DERIVATIVES)
|
||||
if (duDesc.y > 0) { // length
|
||||
writeDu(current, du);
|
||||
}
|
||||
if (dvDesc.y > 0) {
|
||||
writeDv(current, dv);
|
||||
}
|
||||
# endif
|
||||
# if defined(OPENSUBDIV_GLSL_COMPUTE_USE_2ND_DERIVATIVES)
|
||||
if (duuDesc.y > 0) { // length
|
||||
writeDuu(current, duu);
|
||||
}
|
||||
if (duvDesc.y > 0) { // length
|
||||
writeDuv(current, duv);
|
||||
}
|
||||
if (dvvDesc.y > 0) {
|
||||
writeDvv(current, dvv);
|
||||
}
|
||||
# endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,8 +26,8 @@ if sys.platform[:3] == "win":
|
|||
env["SystemDrive"] = os.environ.get("SystemDrive", "")
|
||||
env["SystemRoot"] = os.environ.get("SystemRoot", "")
|
||||
|
||||
inkscape_bin = os.environ.get("INKSCAPE_BIN", "inkscape")
|
||||
blender_bin = os.environ.get("BLENDER_BIN", "blender")
|
||||
inkscape_bin = "inkscape"
|
||||
blender_bin = "blender"
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
inkscape_app_path = '/Applications/Inkscape.app/Contents/MacOS/inkscape'
|
||||
|
@ -36,6 +36,11 @@ if sys.platform == 'darwin':
|
|||
blender_app_path = '/Applications/Blender.app/Contents/MacOS/Blender'
|
||||
if os.path.exists(blender_app_path):
|
||||
blender_bin = blender_app_path
|
||||
else:
|
||||
blender_bin = "Blender"
|
||||
|
||||
inkscape_bin = os.environ.get("INKSCAPE_BIN", inkscape_bin)
|
||||
blender_bin = os.environ.get("BLENDER_BIN", blender_bin)
|
||||
|
||||
cmd = (
|
||||
inkscape_bin,
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 2d12637a69df7643484a8a3655b7eeb6faa170a7
|
||||
Subproject commit 2a5095eed3028e91624d27ca93e4c65f572b809d
|
|
@ -1 +1 @@
|
|||
Subproject commit 089aef61debbece2baff6516e33fc7491629b1d0
|
||||
Subproject commit bb62f10715a871d7069d2b2c74b2efc97c3c350c
|
|
@ -387,6 +387,11 @@ def _template_items_uv_select_mode(params):
|
|||
]
|
||||
else:
|
||||
return [
|
||||
# TODO(@campbellbarton): should this be kept?
|
||||
# Seems it was included in the new key-map by accident, check on removing
|
||||
# although it's not currently used for anything else.
|
||||
op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}),
|
||||
|
||||
*_template_items_editmode_mesh_select_mode(params),
|
||||
# Hack to prevent fall-through, when sync select isn't enabled (and the island button isn't visible).
|
||||
("mesh.select_mode", {"type": 'FOUR', "value": 'PRESS'}, None),
|
||||
|
@ -861,7 +866,6 @@ def km_mask_editing(params):
|
|||
items.extend([
|
||||
("mask.select", {"type": 'RIGHTMOUSE', "value": 'PRESS'},
|
||||
{"properties": [("deselect_all", not params.legacy)]}),
|
||||
("transform.translate", {"type": 'EVT_TWEAK_R', "value": 'ANY'}, None),
|
||||
])
|
||||
|
||||
items.extend([
|
||||
|
@ -1205,7 +1209,6 @@ def km_uv_editor(params):
|
|||
if not params.legacy else
|
||||
op_menu("IMAGE_MT_uvs_snap", {"type": 'S', "value": 'PRESS', "shift": True})
|
||||
),
|
||||
op_menu("IMAGE_MT_uvs_select_mode", {"type": 'TAB', "value": 'PRESS', "ctrl": True}),
|
||||
*_template_items_proportional_editing(
|
||||
params, connected=False, toggle_data_path='tool_settings.use_proportional_edit'),
|
||||
("transform.translate", {"type": params.select_tweak, "value": 'ANY'}, None),
|
||||
|
@ -1370,7 +1373,6 @@ def km_view3d(params):
|
|||
*(() if not params.use_pie_click_drag else
|
||||
(("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'CLICK'}, None),)),
|
||||
("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
|
||||
("view3d.navigate", {"type": 'ACCENT_GRAVE', "value": 'PRESS', "shift": True}, None),
|
||||
# Numpad views.
|
||||
("view3d.view_camera", {"type": 'NUMPAD_0', "value": 'PRESS'}, None),
|
||||
("view3d.view_axis", {"type": 'NUMPAD_1', "value": 'PRESS'},
|
||||
|
@ -2062,14 +2064,19 @@ def km_node_editor(params):
|
|||
("node.translate_attach",
|
||||
{"type": 'EVT_TWEAK_L', "value": 'ANY'},
|
||||
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
|
||||
("node.translate_attach",
|
||||
{"type": params.select_tweak, "value": 'ANY'},
|
||||
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
|
||||
# Avoid duplicating the previous item.
|
||||
*([] if params.select_tweak == 'EVT_TWEAK_L' else (
|
||||
("node.translate_attach", {"type": params.select_tweak, "value": 'ANY'},
|
||||
{"properties": [("TRANSFORM_OT_translate", [("view2d_edge_pan", True)])]}),
|
||||
)),
|
||||
("transform.translate", {"type": 'G', "value": 'PRESS'}, {"properties": [("view2d_edge_pan", True)]}),
|
||||
("transform.translate", {"type": 'EVT_TWEAK_L', "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
|
||||
("transform.translate", {"type": params.select_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
|
||||
# Avoid duplicating the previous item.
|
||||
*([] if params.select_tweak == 'EVT_TWEAK_L' else (
|
||||
("transform.translate", {"type": params.select_tweak, "value": 'ANY'},
|
||||
{"properties": [("release_confirm", True), ("view2d_edge_pan", True)]}),
|
||||
)),
|
||||
("transform.rotate", {"type": 'R', "value": 'PRESS'}, None),
|
||||
("transform.resize", {"type": 'S', "value": 'PRESS'}, None),
|
||||
("node.move_detach_links",
|
||||
|
@ -4652,7 +4659,9 @@ def _template_paint_radial_control(paint, rotation=False, secondary_rotation=Fal
|
|||
return items
|
||||
|
||||
|
||||
def _template_view3d_select(*, type, value, legacy):
|
||||
def _template_view3d_select(*, type, value, legacy, exclude_mod=None):
|
||||
# NOTE: `exclude_mod` is needed since we don't want this tool to exclude Control-RMB actions when this is used
|
||||
# as a tool key-map with RMB-select and `use_fallback_tool_rmb` is enabled. See T92467.
|
||||
return [(
|
||||
"view3d.select",
|
||||
{"type": type, "value": value, **{m: True for m in mods}},
|
||||
|
@ -4666,7 +4675,7 @@ def _template_view3d_select(*, type, value, legacy):
|
|||
(("center", "enumerate"), ("ctrl", "alt")),
|
||||
(("toggle", "enumerate"), ("shift", "alt")),
|
||||
(("toggle", "center", "enumerate"), ("shift", "ctrl", "alt")),
|
||||
)]
|
||||
) if exclude_mod is None or exclude_mod not in mods]
|
||||
|
||||
|
||||
def _template_view3d_gpencil_select(*, type, value, legacy, use_select_mouse=True):
|
||||
|
@ -5460,6 +5469,7 @@ def km_sculpt_curves(params):
|
|||
|
||||
items.extend([
|
||||
("sculpt_curves.brush_stroke", {"type": 'LEFTMOUSE', "value": 'PRESS'}, None),
|
||||
*_template_paint_radial_control("curves_sculpt"),
|
||||
])
|
||||
|
||||
return keymap
|
||||
|
@ -6489,7 +6499,7 @@ def km_3d_view_tool_select(params, *, fallback):
|
|||
*([] if (fallback and (params.select_mouse == 'RIGHTMOUSE')) else _template_items_tool_select(
|
||||
params, "view3d.select", "view3d.cursor3d", extend="toggle")),
|
||||
*([] if (not params.use_fallback_tool_rmb) else _template_view3d_select(
|
||||
type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy)),
|
||||
type=params.select_mouse, value=params.select_mouse_value, legacy=params.legacy, exclude_mod="ctrl")),
|
||||
]},
|
||||
)
|
||||
|
||||
|
|
|
@ -3408,9 +3408,6 @@ def km_sculpt(params):
|
|||
("sculpt.set_detail_size", {"type": 'D', "value": 'PRESS', "shift": True, "alt": True}, None),
|
||||
# Remesh
|
||||
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
|
||||
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
# Remesh
|
||||
("object.voxel_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True}, None),
|
||||
("object.voxel_size_edit", {"type": 'R', "value": 'PRESS', "shift": True}, None),
|
||||
("object.quadriflow_remesh", {"type": 'R', "value": 'PRESS', "ctrl": True, "alt": True}, None),
|
||||
# Brush properties
|
||||
|
|
|
@ -963,21 +963,19 @@ class WM_OT_url_open(Operator):
|
|||
return {'FINISHED'}
|
||||
|
||||
|
||||
# NOTE: needed for Python 3.10 since there are name-space issues with annotations.
|
||||
# This can be moved into the class as a static-method once Python 3.9x is dropped.
|
||||
def _wm_url_open_preset_type_items(_self, _context):
|
||||
return [item for (item, _) in WM_OT_url_open_preset.preset_items]
|
||||
|
||||
|
||||
class WM_OT_url_open_preset(Operator):
|
||||
"""Open a preset website in the web browser"""
|
||||
bl_idname = "wm.url_open_preset"
|
||||
bl_label = "Open Preset Website"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
@staticmethod
|
||||
def _wm_url_open_preset_type_items(_self, _context):
|
||||
return [item for (item, _) in WM_OT_url_open_preset.preset_items]
|
||||
|
||||
type: EnumProperty(
|
||||
name="Site",
|
||||
items=_wm_url_open_preset_type_items,
|
||||
items=WM_OT_url_open_preset._wm_url_open_preset_type_items,
|
||||
)
|
||||
|
||||
id: StringProperty(
|
||||
|
@ -1283,12 +1281,6 @@ rna_vector_subtype_items = (
|
|||
)
|
||||
|
||||
|
||||
# NOTE: needed for Python 3.10 since there are name-space issues with annotations.
|
||||
# This can be moved into the class as a static-method once Python 3.9x is dropped.
|
||||
def _wm_properties_edit_subtype_items(_self, _context):
|
||||
return WM_OT_properties_edit.subtype_items
|
||||
|
||||
|
||||
class WM_OT_properties_edit(Operator):
|
||||
"""Change a custom property's type, or adjust how it is displayed in the interface"""
|
||||
bl_idname = "wm.properties_edit"
|
||||
|
@ -1395,7 +1387,7 @@ class WM_OT_properties_edit(Operator):
|
|||
)
|
||||
subtype: EnumProperty(
|
||||
name="Subtype",
|
||||
items=_wm_properties_edit_subtype_items,
|
||||
items=WM_OT_properties_edit.subtype_items,
|
||||
)
|
||||
|
||||
# String properties.
|
||||
|
|
|
@ -79,6 +79,8 @@ class UnifiedPaintPanel:
|
|||
return tool_settings.gpencil_weight_paint
|
||||
elif mode == 'VERTEX_GPENCIL':
|
||||
return tool_settings.gpencil_vertex_paint
|
||||
elif mode == 'SCULPT_CURVES':
|
||||
return tool_settings.curves_sculpt
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
|
@ -468,6 +468,38 @@ class _draw_tool_settings_context_mode:
|
|||
|
||||
return True
|
||||
|
||||
@staticmethod
|
||||
def SCULPT_CURVES(context, layout, tool):
|
||||
if (tool is None) or (not tool.has_datablock):
|
||||
return False
|
||||
|
||||
paint = context.tool_settings.curves_sculpt
|
||||
layout.template_ID_preview(paint, "brush", rows=3, cols=8, hide_buttons=True)
|
||||
|
||||
brush = paint.brush
|
||||
if brush is None:
|
||||
return False
|
||||
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
layout,
|
||||
context,
|
||||
brush,
|
||||
"size",
|
||||
unified_name="use_unified_size",
|
||||
text="Radius",
|
||||
slider=True,
|
||||
header=True
|
||||
)
|
||||
|
||||
UnifiedPaintPanel.prop_unified(
|
||||
layout,
|
||||
context,
|
||||
brush,
|
||||
"strength",
|
||||
unified_name="use_unified_strength",
|
||||
header=True
|
||||
)
|
||||
|
||||
|
||||
class VIEW3D_HT_header(Header):
|
||||
bl_space_type = 'VIEW_3D'
|
||||
|
|
|
@ -25,7 +25,7 @@ extern "C" {
|
|||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 3
|
||||
#define BLENDER_FILE_SUBVERSION 4
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and show a warning if the file
|
||||
|
|
|
@ -208,11 +208,13 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
|
|||
* \param gpd: Grease pencil data-block
|
||||
* \param gps: Stroke to sample
|
||||
* \param dist: Distance of one segment
|
||||
* \param sharp_threshold: Threshold for preserving sharp corners
|
||||
*/
|
||||
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd,
|
||||
struct bGPDstroke *gps,
|
||||
float dist,
|
||||
bool select);
|
||||
const float dist,
|
||||
const bool select,
|
||||
const float sharp_threshold);
|
||||
/**
|
||||
* Apply smooth position to stroke point.
|
||||
* \param gps: Stroke to smooth
|
||||
|
|
|
@ -763,7 +763,7 @@ static bool fcurves_path_rename_fix(ID *owner_id,
|
|||
if (fcu->rna_path != old_path) {
|
||||
bActionGroup *agrp = fcu->grp;
|
||||
is_changed = true;
|
||||
if ((agrp != NULL) && STREQ(oldName, agrp->name)) {
|
||||
if (oldName != NULL && (agrp != NULL) && STREQ(oldName, agrp->name)) {
|
||||
BLI_strncpy(agrp->name, newName, sizeof(agrp->name));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -529,8 +529,8 @@ static void contarget_get_mesh_mat(Object *ob, const char *substring, float mat[
|
|||
float vec[3] = {0.0f, 0.0f, 0.0f};
|
||||
float normal[3] = {0.0f, 0.0f, 0.0f};
|
||||
float weightsum = 0.0f;
|
||||
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
|
||||
if (me_eval) {
|
||||
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(me_eval);
|
||||
const MDeformVert *dvert = CustomData_get_layer(&me_eval->vdata, CD_MDEFORMVERT);
|
||||
int numVerts = me_eval->totvert;
|
||||
|
||||
|
|
|
@ -358,16 +358,16 @@ static Curves *curves_evaluate_modifiers(struct Depsgraph *depsgraph,
|
|||
curves = BKE_curves_copy_for_eval(curves, true);
|
||||
}
|
||||
|
||||
/* Ensure we are not overwriting referenced data. */
|
||||
CustomData_duplicate_referenced_layer_named(&curves->geometry.point_data,
|
||||
CD_PROP_FLOAT3,
|
||||
ATTR_POSITION,
|
||||
curves->geometry.point_size);
|
||||
update_custom_data_pointers(*curves);
|
||||
|
||||
/* Created deformed coordinates array on demand. */
|
||||
mti->deformVerts(
|
||||
md, &mectx, nullptr, curves->geometry.position, curves->geometry.point_size);
|
||||
blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
|
||||
curves->geometry);
|
||||
MutableSpan<float3> positions = geometry.positions();
|
||||
|
||||
mti->deformVerts(md,
|
||||
&mectx,
|
||||
nullptr,
|
||||
reinterpret_cast<float(*)[3]>(positions.data()),
|
||||
curves->geometry.point_size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -142,8 +142,8 @@ MutableSpan<int8_t> CurvesGeometry::curve_types()
|
|||
|
||||
MutableSpan<float3> CurvesGeometry::positions()
|
||||
{
|
||||
CustomData_duplicate_referenced_layer(&this->point_data, CD_PROP_FLOAT3, this->point_size);
|
||||
this->update_customdata_pointers();
|
||||
this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
|
||||
&this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_size);
|
||||
return {(float3 *)this->position, this->point_size};
|
||||
}
|
||||
Span<float3> CurvesGeometry::positions() const
|
||||
|
|
|
@ -1379,7 +1379,7 @@ bool BKE_object_data_transfer_ex(struct Depsgraph *depsgraph,
|
|||
BLI_assert((ob_src != ob_dst) && (ob_src->type == OB_MESH) && (ob_dst->type == OB_MESH));
|
||||
|
||||
if (me_dst) {
|
||||
dirty_nors_dst = (me_dst->runtime.cd_dirty_vert & CD_NORMAL) != 0;
|
||||
dirty_nors_dst = BKE_mesh_vertex_normals_are_dirty(me_dst);
|
||||
/* Never create needed custom layers on passed destination mesh
|
||||
* (assumed to *not* be ob_dst->data, aka modifier case). */
|
||||
use_create = false;
|
||||
|
|
|
@ -443,7 +443,7 @@ static void gpencil_convert_spline(Main *bmain,
|
|||
}
|
||||
|
||||
if (sample > 0.0f) {
|
||||
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
|
||||
BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
|
||||
}
|
||||
|
||||
/* Recalc fill geometry. */
|
||||
|
|
|
@ -202,8 +202,8 @@ static int stroke_march_next_point(const bGPDstroke *gps,
|
|||
int next_point_index = index_next_pt;
|
||||
bGPDspoint *pt = nullptr;
|
||||
|
||||
if (!(next_point_index < gps->totpoints)) {
|
||||
return -1;
|
||||
if (next_point_index == gps->totpoints) {
|
||||
next_point_index = 0;
|
||||
}
|
||||
|
||||
copy_v3_v3(step_start, current);
|
||||
|
@ -211,15 +211,33 @@ static int stroke_march_next_point(const bGPDstroke *gps,
|
|||
copy_v3_v3(point, &pt->x);
|
||||
remaining_till_next = len_v3v3(point, step_start);
|
||||
|
||||
while (remaining_till_next < remaining_march) {
|
||||
while (remaining_till_next < remaining_march && next_point_index) {
|
||||
remaining_march -= remaining_till_next;
|
||||
pt = &gps->points[next_point_index];
|
||||
if (pt->flag & GP_SPOINT_TEMP_TAG) {
|
||||
pt = &gps->points[next_point_index];
|
||||
copy_v3_v3(result, &pt->x);
|
||||
*pressure = gps->points[next_point_index].pressure;
|
||||
*strength = gps->points[next_point_index].strength;
|
||||
memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
|
||||
|
||||
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
|
||||
*index_to = next_point_index;
|
||||
*ratio_result = 1.0f;
|
||||
next_point_index++;
|
||||
return next_point_index == 0 ? gps->totpoints : next_point_index;
|
||||
}
|
||||
next_point_index++;
|
||||
copy_v3_v3(point, &pt->x);
|
||||
copy_v3_v3(step_start, point);
|
||||
next_point_index++;
|
||||
if (!(next_point_index < gps->totpoints)) {
|
||||
next_point_index = gps->totpoints - 1;
|
||||
break;
|
||||
if (gps->flag & GP_STROKE_CYCLIC) {
|
||||
next_point_index = 0;
|
||||
}
|
||||
else {
|
||||
next_point_index = gps->totpoints - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pt = &gps->points[next_point_index];
|
||||
copy_v3_v3(point, &pt->x);
|
||||
|
@ -232,35 +250,37 @@ static int stroke_march_next_point(const bGPDstroke *gps,
|
|||
*strength = gps->points[next_point_index].strength;
|
||||
memcpy(vert_color, gps->points[next_point_index].vert_color, sizeof(float[4]));
|
||||
|
||||
*index_from = next_point_index - 1;
|
||||
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
|
||||
*index_to = next_point_index;
|
||||
*ratio_result = 1.0f;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
*index_from = next_point_index == 0 ? (gps->totpoints - 1) : (next_point_index - 1);
|
||||
*index_to = next_point_index;
|
||||
|
||||
float ratio = remaining_march / remaining_till_next;
|
||||
interp_v3_v3v3(result, step_start, point, ratio);
|
||||
*ratio_result = ratio;
|
||||
|
||||
*pressure = interpf(
|
||||
gps->points[next_point_index].pressure, gps->points[next_point_index - 1].pressure, ratio);
|
||||
gps->points[next_point_index].pressure, gps->points[*index_from].pressure, ratio);
|
||||
*strength = interpf(
|
||||
gps->points[next_point_index].strength, gps->points[next_point_index - 1].strength, ratio);
|
||||
gps->points[next_point_index].strength, gps->points[*index_from].strength, ratio);
|
||||
interp_v4_v4v4(vert_color,
|
||||
gps->points[next_point_index - 1].vert_color,
|
||||
gps->points[*index_from].vert_color,
|
||||
gps->points[next_point_index].vert_color,
|
||||
ratio);
|
||||
|
||||
*index_from = next_point_index - 1;
|
||||
*index_to = next_point_index;
|
||||
*ratio_result = ratio;
|
||||
|
||||
return next_point_index;
|
||||
return next_point_index == 0 ? gps->totpoints : next_point_index;
|
||||
}
|
||||
|
||||
static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
|
||||
const int index_next_pt,
|
||||
const float *current,
|
||||
const float dist,
|
||||
const float sharp_threshold,
|
||||
float *result)
|
||||
{
|
||||
float remaining_till_next = 0.0f;
|
||||
|
@ -270,8 +290,8 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
|
|||
int next_point_index = index_next_pt;
|
||||
bGPDspoint *pt = nullptr;
|
||||
|
||||
if (!(next_point_index < gps->totpoints)) {
|
||||
return -1;
|
||||
if (next_point_index == gps->totpoints) {
|
||||
next_point_index = 0;
|
||||
}
|
||||
|
||||
copy_v3_v3(step_start, current);
|
||||
|
@ -279,15 +299,29 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
|
|||
copy_v3_v3(point, &pt->x);
|
||||
remaining_till_next = len_v3v3(point, step_start);
|
||||
|
||||
while (remaining_till_next < remaining_march) {
|
||||
while (remaining_till_next < remaining_march && next_point_index) {
|
||||
remaining_march -= remaining_till_next;
|
||||
pt = &gps->points[next_point_index];
|
||||
if (next_point_index < gps->totpoints - 1 &&
|
||||
angle_v3v3v3(&gps->points[next_point_index - 1].x,
|
||||
&gps->points[next_point_index].x,
|
||||
&gps->points[next_point_index + 1].x) < sharp_threshold) {
|
||||
copy_v3_v3(result, &pt->x);
|
||||
pt->flag |= GP_SPOINT_TEMP_TAG;
|
||||
next_point_index++;
|
||||
return next_point_index == 0 ? gps->totpoints : next_point_index;
|
||||
}
|
||||
next_point_index++;
|
||||
copy_v3_v3(point, &pt->x);
|
||||
copy_v3_v3(step_start, point);
|
||||
next_point_index++;
|
||||
if (!(next_point_index < gps->totpoints)) {
|
||||
next_point_index = gps->totpoints - 1;
|
||||
break;
|
||||
if (gps->flag & GP_STROKE_CYCLIC) {
|
||||
next_point_index = 0;
|
||||
}
|
||||
else {
|
||||
next_point_index = gps->totpoints - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pt = &gps->points[next_point_index];
|
||||
copy_v3_v3(point, &pt->x);
|
||||
|
@ -296,15 +330,16 @@ static int stroke_march_next_point_no_interp(const bGPDstroke *gps,
|
|||
if (remaining_till_next < remaining_march) {
|
||||
pt = &gps->points[next_point_index];
|
||||
copy_v3_v3(result, &pt->x);
|
||||
/* Stroke marching only terminates here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
float ratio = remaining_march / remaining_till_next;
|
||||
interp_v3_v3v3(result, step_start, point, ratio);
|
||||
return next_point_index;
|
||||
return next_point_index == 0 ? gps->totpoints : next_point_index;
|
||||
}
|
||||
|
||||
static int stroke_march_count(const bGPDstroke *gps, const float dist)
|
||||
static int stroke_march_count(const bGPDstroke *gps, const float dist, const float sharp_threshold)
|
||||
{
|
||||
int point_count = 0;
|
||||
float point[3];
|
||||
|
@ -315,8 +350,13 @@ static int stroke_march_count(const bGPDstroke *gps, const float dist)
|
|||
copy_v3_v3(point, &pt->x);
|
||||
point_count++;
|
||||
|
||||
/* Sharp points will be tagged by the stroke_march_next_point_no_interp() call below. */
|
||||
for (int i = 0; i < gps->totpoints; i++) {
|
||||
gps->points[i].flag &= (~GP_SPOINT_TEMP_TAG);
|
||||
}
|
||||
|
||||
while ((next_point_index = stroke_march_next_point_no_interp(
|
||||
gps, next_point_index, point, dist, point)) > -1) {
|
||||
gps, next_point_index, point, dist, sharp_threshold, point)) > -1) {
|
||||
point_count++;
|
||||
if (next_point_index == 0) {
|
||||
break; /* last point finished */
|
||||
|
@ -394,7 +434,11 @@ static void stroke_interpolate_deform_weights(
|
|||
}
|
||||
}
|
||||
|
||||
bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, const bool select)
|
||||
bool BKE_gpencil_stroke_sample(bGPdata *gpd,
|
||||
bGPDstroke *gps,
|
||||
const float dist,
|
||||
const bool select,
|
||||
const float sharp_threshold)
|
||||
{
|
||||
bGPDspoint *pt = gps->points;
|
||||
bGPDspoint *pt1 = nullptr;
|
||||
|
@ -406,7 +450,7 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
|
|||
return false;
|
||||
}
|
||||
/* TODO: Implement feature point preservation. */
|
||||
int count = stroke_march_count(gps, dist);
|
||||
int count = stroke_march_count(gps, dist, sharp_threshold);
|
||||
|
||||
bGPDspoint *new_pt = (bGPDspoint *)MEM_callocN(sizeof(bGPDspoint) * count,
|
||||
"gp_stroke_points_sampled");
|
||||
|
@ -491,6 +535,8 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist,
|
|||
|
||||
gps->totpoints = i;
|
||||
|
||||
gps->flag &= (~GP_STROKE_CYCLIC);
|
||||
|
||||
/* Calc geometry data. */
|
||||
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
||||
|
||||
|
|
|
@ -67,9 +67,11 @@ Object **BKE_view_layer_array_selected_objects_params(
|
|||
}
|
||||
FOREACH_SELECTED_OBJECT_END;
|
||||
|
||||
object_array = MEM_reallocN(object_array, sizeof(*object_array) * BLI_array_len(object_array));
|
||||
/* We always need a valid allocation (prevent crash on free). */
|
||||
if (object_array == NULL) {
|
||||
if (object_array != NULL) {
|
||||
BLI_array_trim(object_array);
|
||||
}
|
||||
else {
|
||||
/* We always need a valid allocation (prevent crash on free). */
|
||||
object_array = MEM_mallocN(0, __func__);
|
||||
}
|
||||
*r_len = BLI_array_len(object_array);
|
||||
|
@ -121,9 +123,11 @@ Base **BKE_view_layer_array_from_bases_in_mode_params(ViewLayer *view_layer,
|
|||
}
|
||||
FOREACH_BASE_IN_MODE_END;
|
||||
|
||||
base_array = MEM_reallocN(base_array, sizeof(*base_array) * BLI_array_len(base_array));
|
||||
/* We always need a valid allocation (prevent crash on free). */
|
||||
if (base_array == NULL) {
|
||||
if (base_array != NULL) {
|
||||
BLI_array_trim(base_array);
|
||||
}
|
||||
else {
|
||||
base_array = MEM_mallocN(0, __func__);
|
||||
}
|
||||
*r_len = BLI_array_len(base_array);
|
||||
|
|
|
@ -146,6 +146,17 @@ void _bli_array_grow_func(void **arr_p,
|
|||
*/
|
||||
#define BLI_array_fake_user(arr) ((void)_##arr##_len, (void)_##arr##_static)
|
||||
|
||||
/**
|
||||
* Trim excess items from the array (when they exist).
|
||||
*/
|
||||
#define BLI_array_trim(arr) \
|
||||
{ \
|
||||
if (_bli_array_totalsize_dynamic(arr) != _##arr##_len) { \
|
||||
arr = MEM_reallocN(arr, sizeof(*arr) * _##arr##_len); \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -2557,9 +2557,9 @@ template<typename T> void detect_holes(CDT_state<T> *cdt_state)
|
|||
continue; /* Don't count hits on edges between faces in same region. */
|
||||
}
|
||||
auto isect = isect_seg_seg(ray_end.exact,
|
||||
mid.exact,
|
||||
e->symedges[0].vert->co.exact,
|
||||
e->symedges[1].vert->co.exact);
|
||||
mid.exact,
|
||||
e->symedges[0].vert->co.exact,
|
||||
e->symedges[1].vert->co.exact);
|
||||
switch (isect.kind) {
|
||||
case isect_result<vec2<T>>::LINE_LINE_CROSS: {
|
||||
hits++;
|
||||
|
|
|
@ -320,15 +320,22 @@ static void oldnewmap_increase_size(OldNewMap *onm)
|
|||
|
||||
/* Public OldNewMap API */
|
||||
|
||||
static OldNewMap *oldnewmap_new(void)
|
||||
static void oldnewmap_init_data(OldNewMap *onm, const int capacity_exp)
|
||||
{
|
||||
OldNewMap *onm = MEM_callocN(sizeof(*onm), "OldNewMap");
|
||||
memset(onm, 0x0, sizeof(*onm));
|
||||
|
||||
onm->capacity_exp = DEFAULT_SIZE_EXP;
|
||||
onm->capacity_exp = capacity_exp;
|
||||
onm->entries = MEM_malloc_arrayN(
|
||||
ENTRIES_CAPACITY(onm), sizeof(*onm->entries), "OldNewMap.entries");
|
||||
onm->map = MEM_malloc_arrayN(MAP_CAPACITY(onm), sizeof(*onm->map), "OldNewMap.map");
|
||||
oldnewmap_clear_map(onm);
|
||||
}
|
||||
|
||||
static OldNewMap *oldnewmap_new(void)
|
||||
{
|
||||
OldNewMap *onm = MEM_mallocN(sizeof(*onm), "OldNewMap");
|
||||
|
||||
oldnewmap_init_data(onm, DEFAULT_SIZE_EXP);
|
||||
|
||||
return onm;
|
||||
}
|
||||
|
@ -395,9 +402,10 @@ static void oldnewmap_clear(OldNewMap *onm)
|
|||
}
|
||||
}
|
||||
|
||||
onm->capacity_exp = DEFAULT_SIZE_EXP;
|
||||
oldnewmap_clear_map(onm);
|
||||
onm->nentries = 0;
|
||||
MEM_freeN(onm->entries);
|
||||
MEM_freeN(onm->map);
|
||||
|
||||
oldnewmap_init_data(onm, DEFAULT_SIZE_EXP);
|
||||
}
|
||||
|
||||
static void oldnewmap_free(OldNewMap *onm)
|
||||
|
|
|
@ -2544,6 +2544,21 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 301, 7) ||
|
||||
(bmain->versionfile == 302 && !MAIN_VERSION_ATLEAST(bmain, 302, 4))) {
|
||||
/* Duplicate value for two flags that mistakenly had the same numeric value. */
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) {
|
||||
if (md->type == eModifierType_WeightVGProximity) {
|
||||
WeightVGProximityModifierData *wpmd = (WeightVGProximityModifierData *)md;
|
||||
if (wpmd->proximity_flags & MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK) {
|
||||
wpmd->proximity_flags |= MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_ATLEAST(bmain, 302, 2)) {
|
||||
LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
|
||||
if (scene->ed != NULL) {
|
||||
|
|
|
@ -9,6 +9,7 @@ set(INC
|
|||
../depsgraph
|
||||
../makesdna
|
||||
../../../intern/atomic
|
||||
../../../intern/clog
|
||||
../../../intern/eigen
|
||||
../../../intern/guardedalloc
|
||||
../../../extern/rangetree
|
||||
|
|
|
@ -54,6 +54,19 @@
|
|||
*
|
||||
* This has the effect from the users POV of leaving the mesh un-touched,
|
||||
* and only editing the active shape key-block.
|
||||
*
|
||||
* \subsection other_notes Other Notes
|
||||
*
|
||||
* Other details noted here which might not be so obvious:
|
||||
*
|
||||
* - The #CD_SHAPEKEY layer is only used in edit-mode,
|
||||
* and the #Mesh.key is only used in object-mode.
|
||||
* Although the #CD_SHAPEKEY custom-data layer is converted into #Key data-blocks for each
|
||||
* undo-step while in edit-mode.
|
||||
* - The #CD_SHAPE_KEYINDEX layer is used to check if vertices existed when entering edit-mode.
|
||||
* Values of the indices are only used for shape-keys when the #CD_SHAPEKEY layer can't be found,
|
||||
* allowing coordinates from the #Key to be used to prevent data-loss.
|
||||
* These indices are also used to maintain correct indices for hook modifiers and vertex parents.
|
||||
*/
|
||||
|
||||
#include "DNA_key_types.h"
|
||||
|
@ -84,6 +97,10 @@
|
|||
#include "bmesh.h"
|
||||
#include "intern/bmesh_private.h" /* For element checking. */
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
static CLG_LogRef LOG = {"bmesh.mesh.convert"};
|
||||
|
||||
using blender::Array;
|
||||
using blender::IndexRange;
|
||||
using blender::Span;
|
||||
|
@ -553,8 +570,89 @@ static BMVert **bm_to_mesh_vertex_map(BMesh *bm, int ototvert)
|
|||
return vertMap;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Edit-Mesh to Shape Key Conversion
|
||||
*
|
||||
* There are some details relating to using data from shape keys that need to be
|
||||
* considered carefully for shape key synchronization logic.
|
||||
*
|
||||
* Key Block Usage
|
||||
* ***************
|
||||
*
|
||||
* Key blocks (data in #Mesh.key must be used carefully).
|
||||
*
|
||||
* They can be used to query which key blocks are relative to the basis
|
||||
* since it's not possible to add/remove/reorder key blocks while in edit-mode.
|
||||
*
|
||||
* Key Block Coordinates
|
||||
* =====================
|
||||
*
|
||||
* Key blocks locations must *not* be used. This was done from v2.67 to 3.0,
|
||||
* causing bugs T35170 & T44415.
|
||||
*
|
||||
* Shape key synchronizing could work under the assumption that the key-block is
|
||||
* fixed-in-place when entering edit-mode allowing them to be used as a reference when exiting.
|
||||
* It often does work but isn't reliable since for e.g. rendering may flush changes
|
||||
* from the edit-mesh to the key-block (there are a handful of other situations where
|
||||
* changes may be flushed, see #ED_editors_flush_edits and related functions).
|
||||
* When using undo, it's not known if the data in key-block is from the past or future,
|
||||
* so just don't use this data as it causes pain and suffering for users and developers alike.
|
||||
*
|
||||
* Instead, use the shape-key values stored in #CD_SHAPEKEY since they are reliably
|
||||
* based on the original locations, unless explicitly manipulated.
|
||||
* It's important to write the final shape-key values back to the #CD_SHAPEKEY so applying
|
||||
* the difference between the original-basis and the new coordinates isn't done multiple times.
|
||||
* Therefore #ED_editors_flush_edits and other flushing calls will update both the #Mesh.key
|
||||
* and the edit-mode #CD_SHAPEKEY custom-data layers.
|
||||
*
|
||||
* WARNING: There is an exception to the rule of ignoring coordinates in the destination:
|
||||
* that is when shape-key data in `bm` can't be found (which is itself an error/exception).
|
||||
* In this case our own rule is violated as the alternative is loosing the shape-data entirely.
|
||||
*
|
||||
* Flushing Coordinates Back to the #BMesh
|
||||
* ---------------------------------------
|
||||
*
|
||||
* The edit-mesh may be flushed back to the #Mesh and #Key used to generate it.
|
||||
* When this is done, the new values are written back to the #BMesh's #CD_SHAPEKEY as well.
|
||||
* This is necessary when editing basis-shapes so the difference in shape keys
|
||||
* is not applied multiple times. If it were important to avoid it could be skipped while
|
||||
* exiting edit-mode (as the entire #BMesh is freed in that case), however it's just copying
|
||||
* back a `float[3]` so the work to check if it's necessary isn't worth the overhead.
|
||||
*
|
||||
* In general updating the #BMesh's #CD_SHAPEKEY makes shake-key logic easier to reason about
|
||||
* since it means flushing data back to the mesh has the same behavior as exiting and entering
|
||||
* edit-mode (a more common operation). Meaning there is one less corner-case to have to consider.
|
||||
*
|
||||
* Exceptional Cases
|
||||
* *****************
|
||||
*
|
||||
* There are some situations that should not happen in typical usage but are
|
||||
* still handled in this code, since failure to handle them could loose user-data.
|
||||
* These could be investigated further since if they never happen in practice,
|
||||
* we might consider removing them. However, the possibility of an mesh directly
|
||||
* being modified by Python or some other low level logic that changes key-blocks
|
||||
* means there is a potential this to happen so keeping code to these cases remain supported.
|
||||
*
|
||||
* - Custom Data & Mesh Key Block Synchronization.
|
||||
* Key blocks in `me->key->block` should always have an associated
|
||||
* #CD_SHAPEKEY layer in `bm->vdata`.
|
||||
* If they don't there are two fall-backs for setting the location,
|
||||
* - Use the value from the original shape key
|
||||
* WARNING: this is technically incorrect! (see note on "Key Block Usage").
|
||||
* - Use the current vertex location,
|
||||
* Also not correct but it's better then having it zeroed for e.g.
|
||||
*
|
||||
* - Missing key-index layer.
|
||||
* In this case the basis key wont apply it's deltas to other keys and in the case
|
||||
* a shape-key layer is missing, its coordinates will be initialized from the edit-mesh
|
||||
* vertex locations instead of attempting to remap the shape-keys coordinates.
|
||||
*
|
||||
* \note These cases are considered abnormal and shouldn't occur in typical usage.
|
||||
* A warning is logged in this case to help troubleshooting bugs with shape-keys.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns custom-data shapekey index from a keyblock or -1
|
||||
* Returns custom-data shape-key index from a key-block or -1
|
||||
* \note could split this out into a more generic function.
|
||||
*/
|
||||
static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
|
||||
|
@ -573,6 +671,196 @@ static int bm_to_mesh_shape_layer_index_from_kb(BMesh *bm, KeyBlock *currkey)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update `key` with shape key data stored in `bm`.
|
||||
*
|
||||
* \param bm: The source BMesh.
|
||||
* \param key: The destination key.
|
||||
* \param mvert: The destination vertex array (in some situations it's coordinates are updated).
|
||||
*/
|
||||
static void bm_to_mesh_shape(BMesh *bm, Key *key, MVert *mvert)
|
||||
{
|
||||
KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&key->block, bm->shapenr - 1));
|
||||
|
||||
/* It's unlikely this ever remains false, check for correctness. */
|
||||
bool actkey_has_layer = false;
|
||||
|
||||
/* Go through and find any shape-key custom-data layers
|
||||
* that might not have corresponding KeyBlocks, and add them if necessary. */
|
||||
for (int i = 0; i < bm->vdata.totlayer; i++) {
|
||||
if (bm->vdata.layers[i].type != CD_SHAPEKEY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KeyBlock *currkey;
|
||||
for (currkey = (KeyBlock *)key->block.first; currkey; currkey = currkey->next) {
|
||||
if (currkey->uid == bm->vdata.layers[i].uid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (currkey) {
|
||||
if (currkey == actkey) {
|
||||
actkey_has_layer = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
currkey = BKE_keyblock_add(key, bm->vdata.layers[i].name);
|
||||
currkey->uid = bm->vdata.layers[i].uid;
|
||||
}
|
||||
}
|
||||
|
||||
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
|
||||
BMIter iter;
|
||||
BMVert *eve;
|
||||
float(*ofs)[3] = nullptr;
|
||||
|
||||
/* Editing the basis key updates others. */
|
||||
if ((key->type == KEY_RELATIVE) &&
|
||||
/* The shape-key coordinates used from entering edit-mode are used. */
|
||||
(actkey_has_layer == true) &&
|
||||
/* Original key-indices are only used to check the vertex existed when entering edit-mode. */
|
||||
(cd_shape_keyindex_offset != -1) &&
|
||||
/* Offsets are only needed if the current shape is a basis for others. */
|
||||
BKE_keyblock_is_basis(key, bm->shapenr - 1)) {
|
||||
|
||||
BLI_assert(actkey != nullptr); /* Assured by `actkey_has_layer` check. */
|
||||
const int actkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, actkey);
|
||||
|
||||
/* Since `actkey_has_layer == true`, this must never fail. */
|
||||
BLI_assert(actkey_uuid != -1);
|
||||
|
||||
const int cd_shape_offset = CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, actkey_uuid);
|
||||
|
||||
ofs = static_cast<float(*)[3]>(MEM_mallocN(sizeof(float[3]) * bm->totvert, __func__));
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
/* Check the vertex existed when entering edit-mode (otherwise don't apply an offset). */
|
||||
if (keyi != ORIGINDEX_NONE) {
|
||||
float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
|
||||
/* Could use 'eve->co' or the destination #MVert.co, they're the same at this point. */
|
||||
sub_v3_v3v3(ofs[i], eve->co, co_orig);
|
||||
}
|
||||
else {
|
||||
/* If there are new vertices in the mesh, we can't propagate the offset
|
||||
* because it will only work for the existing vertices and not the new
|
||||
* ones, creating a mess when doing e.g. subdivide + translate. */
|
||||
MEM_freeN(ofs);
|
||||
ofs = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (KeyBlock *, currkey, &key->block) {
|
||||
int keyi;
|
||||
float(*currkey_data)[3];
|
||||
|
||||
const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
|
||||
const int cd_shape_offset = (currkey_uuid == -1) ?
|
||||
-1 :
|
||||
CustomData_get_n_offset(&bm->vdata, CD_SHAPEKEY, currkey_uuid);
|
||||
|
||||
/* Common case, the layer data is available, use it where possible. */
|
||||
if (cd_shape_offset != -1) {
|
||||
const bool apply_offset = (ofs != nullptr) && (currkey != actkey) &&
|
||||
(bm->shapenr - 1 == currkey->relative);
|
||||
|
||||
if (currkey->data && (currkey->totelem == bm->totvert)) {
|
||||
/* Use memory in-place. */
|
||||
}
|
||||
else {
|
||||
currkey->data = MEM_reallocN(currkey->data, key->elemsize * bm->totvert);
|
||||
currkey->totelem = bm->totvert;
|
||||
}
|
||||
currkey_data = (float(*)[3])currkey->data;
|
||||
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
float *co_orig = (float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset);
|
||||
|
||||
if (currkey == actkey) {
|
||||
copy_v3_v3(currkey_data[i], eve->co);
|
||||
|
||||
if (actkey != key->refkey) {
|
||||
/* Without this, the real mesh coordinates (uneditable) as soon as you create
|
||||
* the Basis shape, see: T30771 for details. */
|
||||
if (cd_shape_keyindex_offset != -1) {
|
||||
keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
if (keyi != ORIGINDEX_NONE) {
|
||||
copy_v3_v3(mvert[i].co, co_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
copy_v3_v3(currkey_data[i], co_orig);
|
||||
}
|
||||
|
||||
/* Propagate edited basis offsets to other shapes. */
|
||||
if (apply_offset) {
|
||||
add_v3_v3(currkey_data[i], ofs[i]);
|
||||
}
|
||||
|
||||
/* Apply back new coordinates shape-keys that have offset into #BMesh.
|
||||
* Otherwise, in case we call again #BM_mesh_bm_to_me on same #BMesh,
|
||||
* we'll apply diff from previous call to #BM_mesh_bm_to_me,
|
||||
* to shape-key values from original creation of the #BMesh. See T50524. */
|
||||
copy_v3_v3(co_orig, currkey_data[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* No original layer data, use fallback information. */
|
||||
if (currkey->data && (cd_shape_keyindex_offset != -1)) {
|
||||
CLOG_WARN(&LOG,
|
||||
"Found shape-key but no CD_SHAPEKEY layers to read from, "
|
||||
"using existing shake-key data where possible");
|
||||
}
|
||||
else {
|
||||
CLOG_WARN(&LOG,
|
||||
"Found shape-key but no CD_SHAPEKEY layers to read from, "
|
||||
"using basis shape-key data");
|
||||
}
|
||||
|
||||
currkey_data = static_cast<float(*)[3]>(
|
||||
MEM_mallocN(key->elemsize * bm->totvert, "currkey->data"));
|
||||
|
||||
int i;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
|
||||
if ((currkey->data != nullptr) && (cd_shape_keyindex_offset != -1) &&
|
||||
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
|
||||
(keyi < currkey->totelem)) {
|
||||
/* Reconstruct keys via vertices original key indices.
|
||||
* WARNING(@campbellbarton): `currkey->data` is known to be unreliable as the edit-mesh
|
||||
* coordinates may be flushed back to the shape-key when exporting or rendering.
|
||||
* This is a last resort! If this branch is running as part of regular usage
|
||||
* it can be considered a bug. */
|
||||
const float(*oldkey)[3] = static_cast<const float(*)[3]>(currkey->data);
|
||||
copy_v3_v3(currkey_data[i], oldkey[keyi]);
|
||||
}
|
||||
else {
|
||||
/* Fail! fill in with dummy value. */
|
||||
copy_v3_v3(currkey_data[i], eve->co);
|
||||
}
|
||||
}
|
||||
|
||||
currkey->totelem = bm->totvert;
|
||||
if (currkey->data) {
|
||||
MEM_freeN(currkey->data);
|
||||
}
|
||||
currkey->data = currkey_data;
|
||||
}
|
||||
}
|
||||
|
||||
if (ofs) {
|
||||
MEM_freeN(ofs);
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
BLI_INLINE void bmesh_quick_edgedraw_flag(MEdge *med, BMEdge *e)
|
||||
{
|
||||
/* This is a cheap way to set the edge draw, its not precise and will
|
||||
|
@ -604,23 +892,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
const int cd_edge_crease_offset = CustomData_get_offset(&bm->edata, CD_CREASE);
|
||||
const int cd_shape_keyindex_offset = CustomData_get_offset(&bm->vdata, CD_SHAPE_KEYINDEX);
|
||||
|
||||
MVert *oldverts = nullptr;
|
||||
const int ototvert = me->totvert;
|
||||
|
||||
if (me->key && (cd_shape_keyindex_offset != -1)) {
|
||||
/* Keep the old verts in case we are working on* a key, which is done at the end. */
|
||||
|
||||
/* Use the array in-place instead of duplicating the array. */
|
||||
#if 0
|
||||
oldverts = MEM_dupallocN(me->mvert);
|
||||
#else
|
||||
oldverts = me->mvert;
|
||||
me->mvert = nullptr;
|
||||
CustomData_update_typemap(&me->vdata);
|
||||
CustomData_set_layer(&me->vdata, CD_MVERT, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Free custom data. */
|
||||
CustomData_free(&me->vdata, me->totvert);
|
||||
CustomData_free(&me->edata, me->totedge);
|
||||
|
@ -846,152 +1119,8 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
}
|
||||
}
|
||||
|
||||
/* See comment below, this logic is in twice. */
|
||||
|
||||
if (me->key) {
|
||||
KeyBlock *currkey;
|
||||
KeyBlock *actkey = static_cast<KeyBlock *>(BLI_findlink(&me->key->block, bm->shapenr - 1));
|
||||
|
||||
float(*ofs)[3] = nullptr;
|
||||
|
||||
/* Go through and find any shape-key custom-data layers
|
||||
* that might not have corresponding KeyBlocks, and add them if necessary. */
|
||||
for (i = 0; i < bm->vdata.totlayer; i++) {
|
||||
if (bm->vdata.layers[i].type != CD_SHAPEKEY) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (currkey = (KeyBlock *)me->key->block.first; currkey; currkey = currkey->next) {
|
||||
if (currkey->uid == bm->vdata.layers[i].uid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!currkey) {
|
||||
currkey = BKE_keyblock_add(me->key, bm->vdata.layers[i].name);
|
||||
currkey->uid = bm->vdata.layers[i].uid;
|
||||
}
|
||||
}
|
||||
|
||||
/* Editing the base key should update others. */
|
||||
if (/* Only need offsets for relative shape keys. */
|
||||
(me->key->type == KEY_RELATIVE) &&
|
||||
|
||||
/* Unlikely, but the active key may not be valid if the
|
||||
* BMesh and the mesh are out of sync. */
|
||||
(actkey != nullptr) &&
|
||||
|
||||
/* Not used here, but 'oldverts' is used later for applying 'ofs'. */
|
||||
(oldverts != nullptr) &&
|
||||
|
||||
/* Needed for referencing oldverts. */
|
||||
(cd_shape_keyindex_offset != -1)) {
|
||||
|
||||
const bool act_is_basis = BKE_keyblock_is_basis(me->key, bm->shapenr - 1);
|
||||
|
||||
/* Active key is a base. */
|
||||
if (act_is_basis) {
|
||||
const float(*fp)[3] = static_cast<const float(*)[3]>(actkey->data);
|
||||
|
||||
ofs = static_cast<float(*)[3]>(
|
||||
MEM_callocN(sizeof(float[3]) * bm->totvert, "currkey->data"));
|
||||
mvert = me->mvert;
|
||||
BM_ITER_MESH_INDEX (eve, &iter, bm, BM_VERTS_OF_MESH, i) {
|
||||
const int keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
|
||||
/* Could use 'eve->co' or 'mvert->co', they're the same at this point. */
|
||||
if (keyi != ORIGINDEX_NONE && keyi < actkey->totelem) {
|
||||
sub_v3_v3v3(ofs[i], mvert->co, fp[keyi]);
|
||||
}
|
||||
else {
|
||||
/* If there are new vertices in the mesh, we can't propagate the offset
|
||||
* because it will only work for the existing vertices and not the new
|
||||
* ones, creating a mess when doing e.g. subdivide + translate. */
|
||||
MEM_freeN(ofs);
|
||||
ofs = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
mvert++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LISTBASE_FOREACH (KeyBlock *, currkey, &me->key->block) {
|
||||
int keyi;
|
||||
const float(*ofs_pt)[3] = ofs;
|
||||
float *newkey, (*oldkey)[3], *fp;
|
||||
|
||||
const int currkey_uuid = bm_to_mesh_shape_layer_index_from_kb(bm, currkey);
|
||||
const int cd_shape_offset = (currkey_uuid == -1) ? -1 :
|
||||
CustomData_get_n_offset(&bm->vdata,
|
||||
CD_SHAPEKEY,
|
||||
currkey_uuid);
|
||||
const bool apply_offset = (cd_shape_offset != -1) && (ofs != nullptr) &&
|
||||
(currkey != actkey) && (bm->shapenr - 1 == currkey->relative);
|
||||
|
||||
fp = newkey = static_cast<float *>(
|
||||
MEM_callocN(me->key->elemsize * bm->totvert, "currkey->data"));
|
||||
oldkey = static_cast<float(*)[3]>(currkey->data);
|
||||
|
||||
mvert = me->mvert;
|
||||
BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
|
||||
|
||||
if (currkey == actkey) {
|
||||
copy_v3_v3(fp, eve->co);
|
||||
|
||||
if (actkey != me->key->refkey) { /* Important see bug T30771. */
|
||||
if (cd_shape_keyindex_offset != -1) {
|
||||
if (oldverts) {
|
||||
keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset);
|
||||
if (keyi != ORIGINDEX_NONE && keyi < currkey->totelem) { /* Valid old vertex. */
|
||||
copy_v3_v3(mvert->co, oldverts[keyi].co);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (cd_shape_offset != -1) {
|
||||
/* In most cases this runs. */
|
||||
copy_v3_v3(fp, (const float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset));
|
||||
}
|
||||
else if ((oldkey != nullptr) && (cd_shape_keyindex_offset != -1) &&
|
||||
((keyi = BM_ELEM_CD_GET_INT(eve, cd_shape_keyindex_offset)) != ORIGINDEX_NONE) &&
|
||||
(keyi < currkey->totelem)) {
|
||||
/* Old method of reconstructing keys via vertices original key indices,
|
||||
* currently used if the new method above fails
|
||||
* (which is theoretically possible in certain cases of undo). */
|
||||
copy_v3_v3(fp, oldkey[keyi]);
|
||||
}
|
||||
else {
|
||||
/* Fail! fill in with dummy value. */
|
||||
copy_v3_v3(fp, mvert->co);
|
||||
}
|
||||
|
||||
/* Propagate edited basis offsets to other shapes. */
|
||||
if (apply_offset) {
|
||||
add_v3_v3(fp, *ofs_pt++);
|
||||
/* Apply back new coordinates shape-keys that have offset into BMesh.
|
||||
* Otherwise, in case we call again #BM_mesh_bm_to_me on same BMesh,
|
||||
* we'll apply diff from previous call to #BM_mesh_bm_to_me,
|
||||
* to shape-key values from *original creation of the BMesh*. See T50524. */
|
||||
copy_v3_v3((float *)BM_ELEM_CD_GET_VOID_P(eve, cd_shape_offset), fp);
|
||||
}
|
||||
|
||||
fp += 3;
|
||||
mvert++;
|
||||
}
|
||||
|
||||
currkey->totelem = bm->totvert;
|
||||
if (currkey->data) {
|
||||
MEM_freeN(currkey->data);
|
||||
}
|
||||
currkey->data = newkey;
|
||||
}
|
||||
|
||||
if (ofs) {
|
||||
MEM_freeN(ofs);
|
||||
}
|
||||
bm_to_mesh_shape(bm, me->key, me->mvert);
|
||||
}
|
||||
|
||||
/* Run this even when shape keys aren't used since it may be used for hooks or vertex parents. */
|
||||
|
@ -1005,10 +1134,6 @@ void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *me, const struct BMeshToMesh
|
|||
}
|
||||
}
|
||||
|
||||
if (oldverts != nullptr) {
|
||||
MEM_freeN(oldverts);
|
||||
}
|
||||
|
||||
/* Topology could be changed, ensure #CD_MDISPS are ok. */
|
||||
multires_topology_changed(me);
|
||||
|
||||
|
|
|
@ -2234,7 +2234,9 @@ int BM_mesh_calc_face_groups(BMesh *bm,
|
|||
MEM_freeN(stack);
|
||||
|
||||
/* reduce alloc to required size */
|
||||
group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
|
||||
if (group_index_len != group_curr) {
|
||||
group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
|
||||
}
|
||||
*r_group_index = group_index;
|
||||
|
||||
return group_curr;
|
||||
|
@ -2354,7 +2356,9 @@ int BM_mesh_calc_edge_groups(BMesh *bm,
|
|||
MEM_freeN(stack);
|
||||
|
||||
/* reduce alloc to required size */
|
||||
group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
|
||||
if (group_index_len != group_curr) {
|
||||
group_index = MEM_reallocN(group_index, sizeof(*group_index) * group_curr);
|
||||
}
|
||||
*r_group_index = group_index;
|
||||
|
||||
return group_curr;
|
||||
|
|
|
@ -29,8 +29,10 @@ void GaussianBokehBlurOperation::init_data()
|
|||
const float width = this->get_width();
|
||||
const float height = this->get_height();
|
||||
|
||||
if (!sizeavailable_) {
|
||||
update_size();
|
||||
if (execution_model_ == eExecutionModel::FullFrame) {
|
||||
if (!sizeavailable_) {
|
||||
update_size();
|
||||
}
|
||||
}
|
||||
|
||||
radxf_ = size_ * (float)data_.sizex;
|
||||
|
@ -96,6 +98,22 @@ void GaussianBokehBlurOperation::update_gauss()
|
|||
|
||||
void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data)
|
||||
{
|
||||
float result[4];
|
||||
input_size_->read_sampled(result, 0, 0, PixelSampler::Nearest);
|
||||
size_ = result[0];
|
||||
|
||||
const float width = this->get_width();
|
||||
const float height = this->get_height();
|
||||
|
||||
radxf_ = size_ * (float)data_.sizex;
|
||||
CLAMP(radxf_, 0.0f, width / 2.0f);
|
||||
|
||||
radyf_ = size_ * (float)data_.sizey;
|
||||
CLAMP(radyf_, 0.0f, height / 2.0f);
|
||||
|
||||
radx_ = ceil(radxf_);
|
||||
rady_ = ceil(radyf_);
|
||||
|
||||
float temp_color[4];
|
||||
temp_color[0] = 0;
|
||||
temp_color[1] = 0;
|
||||
|
|
|
@ -208,7 +208,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
|
|||
if (iterator.tile_data.tile_buffer == nullptr) {
|
||||
continue;
|
||||
}
|
||||
ensure_float_buffer(*iterator.tile_data.tile_buffer);
|
||||
const bool do_free_float_buffer = ensure_float_buffer(*iterator.tile_data.tile_buffer);
|
||||
const float tile_width = static_cast<float>(iterator.tile_data.tile_buffer->x);
|
||||
const float tile_height = static_cast<float>(iterator.tile_data.tile_buffer->y);
|
||||
|
||||
|
@ -314,6 +314,10 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
|
|||
0);
|
||||
imb_freerectImbuf_all(&extracted_buffer);
|
||||
}
|
||||
|
||||
if (do_free_float_buffer) {
|
||||
imb_freerectfloatImBuf(iterator.tile_data.tile_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,10 +393,7 @@ template<typename TextureMethod> class ScreenSpaceDrawingMode : public AbstractD
|
|||
{
|
||||
const int texture_width = texture_buffer.x;
|
||||
const int texture_height = texture_buffer.y;
|
||||
const bool float_buffer_created = ensure_float_buffer(tile_buffer);
|
||||
/* TODO(jbakker): Find leak when rendering VSE and don't free here. */
|
||||
const bool do_free_float_buffer = float_buffer_created &&
|
||||
instance_data.image->type == IMA_TYPE_R_RESULT;
|
||||
const bool do_free_float_buffer = ensure_float_buffer(tile_buffer);
|
||||
|
||||
/* IMB_transform works in a non-consistent space. This should be documented or fixed!.
|
||||
* Construct a variant of the info_uv_to_texture that adds the texel space
|
||||
|
|
|
@ -73,11 +73,13 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
|
|||
sh = workbench_shader_opaque_get(wpd, data);
|
||||
|
||||
wpd->prepass[opaque][infront][data].common_shgrp = grp = DRW_shgroup_create(sh, pass);
|
||||
DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
|
||||
DRW_shgroup_uniform_int_copy(grp, "materialIndex", -1);
|
||||
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
|
||||
|
||||
wpd->prepass[opaque][infront][data].vcol_shgrp = grp = DRW_shgroup_create(sh, pass);
|
||||
DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
|
||||
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. (uses vcol) */
|
||||
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
|
||||
|
@ -85,6 +87,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
|
|||
sh = workbench_shader_opaque_image_get(wpd, data, false);
|
||||
|
||||
wpd->prepass[opaque][infront][data].image_shgrp = grp = DRW_shgroup_create(sh, pass);
|
||||
DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
|
||||
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
|
||||
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
|
||||
|
@ -92,6 +95,7 @@ void workbench_opaque_cache_init(WORKBENCH_Data *vedata)
|
|||
sh = workbench_shader_opaque_image_get(wpd, data, true);
|
||||
|
||||
wpd->prepass[opaque][infront][data].image_tiled_shgrp = grp = DRW_shgroup_create(sh, pass);
|
||||
DRW_shgroup_uniform_block(grp, "world_data", wpd->world_ubo);
|
||||
DRW_shgroup_uniform_block(grp, "materials_data", wpd->material_ubo_curr);
|
||||
DRW_shgroup_uniform_int_copy(grp, "materialIndex", 0); /* Default material. */
|
||||
DRW_shgroup_uniform_bool_copy(grp, "useMatcap", use_matcap);
|
||||
|
|
|
@ -133,12 +133,12 @@ static void curves_batch_cache_fill_segments_proc_pos(Curves *curves,
|
|||
{
|
||||
/* TODO: use hair radius layer if available. */
|
||||
const int curve_size = curves->geometry.curve_size;
|
||||
Span<int> offsets{curves->geometry.curve_offsets, curves->geometry.curve_size + 1};
|
||||
|
||||
Span<float3> positions{(float3 *)curves->geometry.position, curves->geometry.point_size};
|
||||
const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
|
||||
curves->geometry);
|
||||
Span<float3> positions = geometry.positions();
|
||||
|
||||
for (const int i : IndexRange(curve_size)) {
|
||||
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
|
||||
const IndexRange curve_range = geometry.range_for_curve(i);
|
||||
|
||||
Span<float3> spline_positions = positions.slice(curve_range);
|
||||
float total_len = 0.0f;
|
||||
|
@ -215,11 +215,11 @@ static void curves_batch_cache_fill_strands_data(Curves *curves,
|
|||
GPUVertBufRaw *data_step,
|
||||
GPUVertBufRaw *seg_step)
|
||||
{
|
||||
const int curve_size = curves->geometry.curve_size;
|
||||
Span<int> offsets{curves->geometry.curve_offsets, curves->geometry.curve_size + 1};
|
||||
const blender::bke::CurvesGeometry &geometry = blender::bke::CurvesGeometry::wrap(
|
||||
curves->geometry);
|
||||
|
||||
for (const int i : IndexRange(curve_size)) {
|
||||
const IndexRange curve_range(offsets[i], offsets[i + 1] - offsets[i]);
|
||||
for (const int i : IndexRange(geometry.curves_size())) {
|
||||
const IndexRange curve_range = geometry.range_for_curve(i);
|
||||
|
||||
*(uint *)GPU_vertbuf_raw_step(data_step) = curve_range.start();
|
||||
*(ushort *)GPU_vertbuf_raw_step(seg_step) = curve_range.size() - 1;
|
||||
|
|
|
@ -566,7 +566,7 @@ static void drw_call_obinfos_init(DRWObjectInfos *ob_infos, Object *ob)
|
|||
drw_call_calc_orco(ob, ob_infos->orcotexfac);
|
||||
/* Random float value. */
|
||||
uint random = (DST.dupli_source) ?
|
||||
DST.dupli_source->random_id :
|
||||
DST.dupli_source->random_id :
|
||||
/* TODO(fclem): this is rather costly to do at runtime. Maybe we can
|
||||
* put it in ob->runtime and make depsgraph ensure it is up to date. */
|
||||
BLI_hash_int_2d(BLI_hash_string(ob->id.name + 2), 0);
|
||||
|
|
|
@ -4,74 +4,8 @@
|
|||
* \ingroup edcurves
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "ED_curves.h"
|
||||
#include "ED_object.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_toolsystem.h"
|
||||
#include "WM_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_paint.h"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.h"
|
||||
#include "RNA_define.h"
|
||||
#include "RNA_types.h"
|
||||
|
||||
static bool curves_sculptmode_toggle_poll(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (ob == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (ob->type != OB_CURVES) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
|
||||
|
||||
if (is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_mode_set) {
|
||||
ob->mode = OB_MODE_OBJECT;
|
||||
}
|
||||
else {
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
|
||||
ob->mode = OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
|
||||
WM_toolsystem_update_from_context_view3d(C);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Curve Sculpt Mode Toggle";
|
||||
ot->idname = "CURVES_OT_sculptmode_toggle";
|
||||
ot->description = "Enter/Exit sculpt mode for curves";
|
||||
|
||||
ot->exec = curves_sculptmode_toggle_exec;
|
||||
ot->poll = curves_sculptmode_toggle_poll;
|
||||
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
void ED_operatortypes_curves()
|
||||
{
|
||||
WM_operatortype_append(CURVES_OT_sculptmode_toggle);
|
||||
}
|
||||
|
|
|
@ -4356,6 +4356,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
|
|||
{
|
||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||
const float length = RNA_float_get(op->ptr, "length");
|
||||
const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold");
|
||||
|
||||
/* sanity checks */
|
||||
if (ELEM(NULL, gpd)) {
|
||||
|
@ -4365,7 +4366,7 @@ static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
|
|||
/* Go through each editable + selected stroke */
|
||||
GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
|
||||
if (gps->flag & GP_STROKE_SELECT) {
|
||||
BKE_gpencil_stroke_sample(gpd, gps, length, true);
|
||||
BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold);
|
||||
}
|
||||
}
|
||||
GP_EDITABLE_STROKES_END(gpstroke_iter);
|
||||
|
|
|
@ -314,7 +314,7 @@ void ED_gpencil_trace_data_to_strokes(Main *bmain,
|
|||
if (sample > 0.0f) {
|
||||
/* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
|
||||
* the sample function already call that. */
|
||||
BKE_gpencil_stroke_sample(gpd, gps, sample, false);
|
||||
BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
|
||||
}
|
||||
else {
|
||||
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
||||
|
|
|
@ -2903,7 +2903,7 @@ uiBut *UI_context_active_but_prop_get(const struct bContext *C,
|
|||
struct PointerRNA *r_ptr,
|
||||
struct PropertyRNA **r_prop,
|
||||
int *r_index);
|
||||
void UI_context_active_but_prop_handle(struct bContext *C);
|
||||
void UI_context_active_but_prop_handle(struct bContext *C, bool handle_undo);
|
||||
void UI_context_active_but_clear(struct bContext *C, struct wmWindow *win, struct ARegion *region);
|
||||
|
||||
struct wmOperator *UI_context_active_operator_get(const struct bContext *C);
|
||||
|
|
|
@ -8793,7 +8793,7 @@ uiBut *UI_context_active_but_prop_get(const bContext *C,
|
|||
return activebut;
|
||||
}
|
||||
|
||||
void UI_context_active_but_prop_handle(bContext *C)
|
||||
void UI_context_active_but_prop_handle(bContext *C, const bool handle_undo)
|
||||
{
|
||||
uiBut *activebut = ui_context_rna_button_active(C);
|
||||
if (activebut) {
|
||||
|
@ -8804,6 +8804,11 @@ void UI_context_active_but_prop_handle(bContext *C)
|
|||
if (block->handle_func) {
|
||||
block->handle_func(C, block->handle_func_arg, activebut->retval);
|
||||
}
|
||||
if (handle_undo) {
|
||||
/* Update the button so the undo text uses the correct value. */
|
||||
ui_but_update(activebut);
|
||||
ui_apply_but_undo(activebut);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -314,7 +314,7 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert
|
|||
RNA_property_update(C, ptr, prop);
|
||||
|
||||
/* as if we pressed the button */
|
||||
UI_context_active_but_prop_handle(C);
|
||||
UI_context_active_but_prop_handle(C, false);
|
||||
|
||||
/* Since we don't want to undo _all_ edits to settings, eg window
|
||||
* edits on the screen or on operator settings.
|
||||
|
@ -326,6 +326,19 @@ static int operator_button_property_finish(bContext *C, PointerRNA *ptr, Propert
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static int operator_button_property_finish_with_undo(bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop)
|
||||
{
|
||||
/* Perform updates required for this property. */
|
||||
RNA_property_update(C, ptr, prop);
|
||||
|
||||
/* As if we pressed the button. */
|
||||
UI_context_active_but_prop_handle(C, true);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static bool reset_default_button_poll(bContext *C)
|
||||
{
|
||||
PointerRNA ptr;
|
||||
|
@ -350,7 +363,7 @@ static int reset_default_button_exec(bContext *C, wmOperator *op)
|
|||
/* if there is a valid property that is editable... */
|
||||
if (ptr.data && prop && RNA_property_editable(&ptr, prop)) {
|
||||
if (RNA_property_reset(&ptr, prop, (all) ? -1 : index)) {
|
||||
return operator_button_property_finish(C, &ptr, prop);
|
||||
return operator_button_property_finish_with_undo(C, &ptr, prop);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +382,9 @@ static void UI_OT_reset_default_button(wmOperatorType *ot)
|
|||
ot->exec = reset_default_button_exec;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_UNDO;
|
||||
/* Don't set #OPTYPE_UNDO because #operator_button_property_finish_with_undo
|
||||
* is responsible for the undo push. */
|
||||
ot->flag = 0;
|
||||
|
||||
/* properties */
|
||||
RNA_def_boolean(ot->srna, "all", 1, "All", "Reset to default values all elements of the array");
|
||||
|
|
|
@ -632,7 +632,7 @@ static void *undomesh_from_editmesh(UndoMesh *um, BMEditMesh *em, Key *key, Undo
|
|||
return um;
|
||||
}
|
||||
|
||||
static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *key)
|
||||
static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em)
|
||||
{
|
||||
BMEditMesh *em_tmp;
|
||||
BMesh *bm;
|
||||
|
@ -688,29 +688,6 @@ static void undomesh_to_editmesh(UndoMesh *um, Object *ob, BMEditMesh *em, Key *
|
|||
|
||||
bm->spacearr_dirty = BM_SPACEARR_DIRTY_ALL;
|
||||
|
||||
/* T35170: Restore the active key on the RealMesh. Otherwise 'fake' offset propagation happens
|
||||
* if the active is a basis for any other. */
|
||||
if (key && (key->type == KEY_RELATIVE)) {
|
||||
/* Since we can't add, remove or reorder keyblocks in editmode, it's safe to assume
|
||||
* shapenr from restored bmesh and keyblock indices are in sync. */
|
||||
const int kb_act_idx = ob->shapenr - 1;
|
||||
|
||||
/* If it is, let's patch the current mesh key block to its restored value.
|
||||
* Else, the offsets won't be computed and it won't matter. */
|
||||
if (BKE_keyblock_is_basis(key, kb_act_idx)) {
|
||||
KeyBlock *kb_act = BLI_findlink(&key->block, kb_act_idx);
|
||||
|
||||
if (kb_act->totelem != um->me.totvert) {
|
||||
/* The current mesh has some extra/missing verts compared to the undo, adjust. */
|
||||
MEM_SAFE_FREE(kb_act->data);
|
||||
kb_act->data = MEM_mallocN((size_t)(key->elemsize) * bm->totvert, __func__);
|
||||
kb_act->totelem = um->me.totvert;
|
||||
}
|
||||
|
||||
BKE_keyblock_update_from_mesh(&um->me, kb_act);
|
||||
}
|
||||
}
|
||||
|
||||
ob->shapenr = um->shapenr;
|
||||
|
||||
MEM_freeN(em_tmp);
|
||||
|
@ -858,7 +835,7 @@ static void mesh_undosys_step_decode(struct bContext *C,
|
|||
continue;
|
||||
}
|
||||
BMEditMesh *em = me->edit_mesh;
|
||||
undomesh_to_editmesh(&elem->data, obedit, em, me->key);
|
||||
undomesh_to_editmesh(&elem->data, obedit, em);
|
||||
em->needs_flush_to_id = 1;
|
||||
DEG_id_tag_update(&me->id, ID_RECALC_GEOMETRY);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ extern "C" {
|
|||
#endif
|
||||
|
||||
bool CURVES_SCULPT_mode_poll(struct bContext *C);
|
||||
bool CURVES_SCULPT_mode_poll_view3d(struct bContext *C);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -6,18 +6,37 @@
|
|||
#include "BKE_paint.h"
|
||||
|
||||
#include "WM_api.h"
|
||||
#include "WM_toolsystem.h"
|
||||
|
||||
#include "ED_curves_sculpt.h"
|
||||
#include "ED_object.h"
|
||||
|
||||
#include "curves_sculpt_intern.h"
|
||||
#include "paint_intern.h"
|
||||
|
||||
bool CURVES_SCULPT_mode_poll(struct bContext *C)
|
||||
bool CURVES_SCULPT_mode_poll(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
return ob && ob->mode & OB_MODE_SCULPT_CURVES;
|
||||
}
|
||||
|
||||
bool CURVES_SCULPT_mode_poll_view3d(bContext *C)
|
||||
{
|
||||
if (!CURVES_SCULPT_mode_poll(C)) {
|
||||
return false;
|
||||
}
|
||||
if (CTX_wm_region_view3d(C) == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace blender::ed::sculpt_paint {
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* SCULPT_CURVES_OT_brush_stroke.
|
||||
*/
|
||||
|
||||
static bool stroke_get_location(bContext *C, float out[3], const float mouse[2])
|
||||
{
|
||||
out[0] = mouse[0];
|
||||
|
@ -33,9 +52,12 @@ static bool stroke_test_start(bContext *C, struct wmOperator *op, const float mo
|
|||
return true;
|
||||
}
|
||||
|
||||
static void stroke_update_step(bContext *C, PaintStroke *stroke, PointerRNA *itemptr)
|
||||
static void stroke_update_step(bContext *C,
|
||||
wmOperator *op,
|
||||
PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
UNUSED_VARS(C, stroke, itemptr);
|
||||
UNUSED_VARS(C, op, stroke, itemptr);
|
||||
}
|
||||
|
||||
static void stroke_done(const bContext *C, PaintStroke *stroke)
|
||||
|
@ -59,9 +81,14 @@ static int sculpt_curves_stroke_invoke(bContext *C, wmOperator *op, const wmEven
|
|||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
static int sculpt_curves_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void sculpt_curves_stroke_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op);
|
||||
paint_stroke_cancel(C, op, static_cast<PaintStroke *>(op->customdata));
|
||||
}
|
||||
|
||||
static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
|
||||
|
@ -71,7 +98,7 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
|
|||
ot->description = "Sculpt curves using a brush";
|
||||
|
||||
ot->invoke = sculpt_curves_stroke_invoke;
|
||||
ot->modal = paint_stroke_modal;
|
||||
ot->modal = sculpt_curves_stroke_modal;
|
||||
ot->cancel = sculpt_curves_stroke_cancel;
|
||||
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
|
@ -79,7 +106,84 @@ static void SCULPT_CURVES_OT_brush_stroke(struct wmOperatorType *ot)
|
|||
paint_stroke_operator_properties(ot);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* CURVES_OT_sculptmode_toggle.
|
||||
*/
|
||||
|
||||
static bool curves_sculptmode_toggle_poll(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
if (ob == nullptr) {
|
||||
return false;
|
||||
}
|
||||
if (ob->type != OB_CURVES) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void curves_sculptmode_enter(bContext *C)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->curves_sculpt);
|
||||
CurvesSculpt *curves_sculpt = scene->toolsettings->curves_sculpt;
|
||||
|
||||
ob->mode = OB_MODE_SCULPT_CURVES;
|
||||
|
||||
paint_cursor_start(&curves_sculpt->paint, CURVES_SCULPT_mode_poll_view3d);
|
||||
}
|
||||
|
||||
static void curves_sculptmode_exit(bContext *C)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
ob->mode = OB_MODE_OBJECT;
|
||||
}
|
||||
|
||||
static int curves_sculptmode_toggle_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = CTX_data_active_object(C);
|
||||
const bool is_mode_set = ob->mode == OB_MODE_SCULPT_CURVES;
|
||||
|
||||
if (is_mode_set) {
|
||||
if (!ED_object_mode_compat_set(C, ob, OB_MODE_SCULPT_CURVES, op->reports)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_mode_set) {
|
||||
curves_sculptmode_exit(C);
|
||||
}
|
||||
else {
|
||||
curves_sculptmode_enter(C);
|
||||
}
|
||||
|
||||
WM_toolsystem_update_from_context_view3d(C);
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE, nullptr);
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
static void CURVES_OT_sculptmode_toggle(wmOperatorType *ot)
|
||||
{
|
||||
ot->name = "Curve Sculpt Mode Toggle";
|
||||
ot->idname = "CURVES_OT_sculptmode_toggle";
|
||||
ot->description = "Enter/Exit sculpt mode for curves";
|
||||
|
||||
ot->exec = curves_sculptmode_toggle_exec;
|
||||
ot->poll = curves_sculptmode_toggle_poll;
|
||||
|
||||
ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
|
||||
}
|
||||
|
||||
} // namespace blender::ed::sculpt_paint
|
||||
|
||||
/* --------------------------------------------------------------------
|
||||
* Registration.
|
||||
*/
|
||||
|
||||
void ED_operatortypes_sculpt_curves()
|
||||
{
|
||||
using namespace blender::ed::sculpt_paint;
|
||||
WM_operatortype_append(SCULPT_CURVES_OT_brush_stroke);
|
||||
WM_operatortype_append(CURVES_OT_sculptmode_toggle);
|
||||
}
|
||||
|
|
|
@ -502,7 +502,10 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const flo
|
|||
return pop;
|
||||
}
|
||||
|
||||
static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
||||
static void paint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
PaintOperation *pop = paint_stroke_mode_data(stroke);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
|
@ -544,12 +547,15 @@ static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, Po
|
|||
ED_image_undo_restore(ustack->step_init);
|
||||
}
|
||||
|
||||
if (pop->mode == PAINT_MODE_3D_PROJECT) {
|
||||
paint_proj_stroke(
|
||||
C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
}
|
||||
else {
|
||||
paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
switch (pop->mode) {
|
||||
case PAINT_MODE_2D:
|
||||
paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
break;
|
||||
|
||||
case PAINT_MODE_3D_PROJECT:
|
||||
paint_proj_stroke(
|
||||
C, pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size);
|
||||
break;
|
||||
}
|
||||
|
||||
copy_v2_v2(pop->prevmouse, mouse);
|
||||
|
@ -562,11 +568,14 @@ static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, b
|
|||
{
|
||||
PaintOperation *pop = paint_stroke_mode_data(stroke);
|
||||
|
||||
if (pop->mode == PAINT_MODE_3D_PROJECT) {
|
||||
paint_proj_redraw(C, pop->custom_paint, final);
|
||||
}
|
||||
else {
|
||||
paint_2d_redraw(C, pop->custom_paint, final);
|
||||
switch (pop->mode) {
|
||||
case PAINT_MODE_2D:
|
||||
paint_2d_redraw(C, pop->custom_paint, final);
|
||||
break;
|
||||
|
||||
case PAINT_MODE_3D_PROJECT:
|
||||
paint_proj_redraw(C, pop->custom_paint, final);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -581,54 +590,65 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke)
|
|||
|
||||
if (brush->imagepaint_tool == PAINT_TOOL_FILL) {
|
||||
if (brush->flag & BRUSH_USE_GRADIENT) {
|
||||
if (pop->mode == PAINT_MODE_2D) {
|
||||
paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
|
||||
}
|
||||
else {
|
||||
paint_proj_stroke(C,
|
||||
pop->custom_paint,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->custom_paint, false);
|
||||
paint_proj_redraw(C, pop->custom_paint, true);
|
||||
switch (pop->mode) {
|
||||
case PAINT_MODE_2D:
|
||||
paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
|
||||
break;
|
||||
|
||||
case PAINT_MODE_3D_PROJECT:
|
||||
paint_proj_stroke(C,
|
||||
pop->custom_paint,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->custom_paint, false);
|
||||
paint_proj_redraw(C, pop->custom_paint, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (pop->mode == PAINT_MODE_2D) {
|
||||
float color[3];
|
||||
if (paint_stroke_inverted(stroke)) {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
|
||||
}
|
||||
else {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
|
||||
}
|
||||
paint_2d_bucket_fill(C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
|
||||
}
|
||||
else {
|
||||
paint_proj_stroke(C,
|
||||
pop->custom_paint,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->custom_paint, false);
|
||||
paint_proj_redraw(C, pop->custom_paint, true);
|
||||
switch (pop->mode) {
|
||||
case PAINT_MODE_2D:
|
||||
float color[3];
|
||||
if (paint_stroke_inverted(stroke)) {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_secondary_color_get(scene, brush));
|
||||
}
|
||||
else {
|
||||
srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush));
|
||||
}
|
||||
paint_2d_bucket_fill(
|
||||
C, color, brush, pop->startmouse, pop->prevmouse, pop->custom_paint);
|
||||
break;
|
||||
|
||||
case PAINT_MODE_3D_PROJECT:
|
||||
paint_proj_stroke(C,
|
||||
pop->custom_paint,
|
||||
pop->startmouse,
|
||||
pop->prevmouse,
|
||||
paint_stroke_flipped(stroke),
|
||||
1.0,
|
||||
0.0,
|
||||
BKE_brush_size_get(scene, brush));
|
||||
/* two redraws, one for GPU update, one for notification */
|
||||
paint_proj_redraw(C, pop->custom_paint, false);
|
||||
paint_proj_redraw(C, pop->custom_paint, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (pop->mode == PAINT_MODE_3D_PROJECT) {
|
||||
paint_proj_stroke_done(pop->custom_paint);
|
||||
}
|
||||
else {
|
||||
paint_2d_stroke_done(pop->custom_paint);
|
||||
|
||||
switch (pop->mode) {
|
||||
case PAINT_MODE_2D:
|
||||
paint_2d_stroke_done(pop->custom_paint);
|
||||
break;
|
||||
|
||||
case PAINT_MODE_3D_PROJECT:
|
||||
paint_proj_stroke_done(pop->custom_paint);
|
||||
break;
|
||||
}
|
||||
|
||||
if (pop->cursor) {
|
||||
|
@ -685,7 +705,7 @@ static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* add modal handler */
|
||||
|
@ -720,7 +740,17 @@ static int paint_exec(bContext *C, wmOperator *op)
|
|||
paint_stroke_done,
|
||||
0);
|
||||
/* frees op->customdata */
|
||||
return paint_stroke_exec(C, op);
|
||||
return paint_stroke_exec(C, op, op->customdata);
|
||||
}
|
||||
|
||||
static int paint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, op->customdata);
|
||||
}
|
||||
|
||||
static void paint_cancel(bContext *C, wmOperator *op)
|
||||
{
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
}
|
||||
|
||||
void PAINT_OT_image_paint(wmOperatorType *ot)
|
||||
|
@ -732,10 +762,10 @@ void PAINT_OT_image_paint(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
ot->invoke = paint_invoke;
|
||||
ot->modal = paint_stroke_modal;
|
||||
ot->modal = paint_modal;
|
||||
ot->exec = paint_exec;
|
||||
ot->poll = image_paint_poll;
|
||||
ot->cancel = paint_stroke_cancel;
|
||||
ot->cancel = paint_cancel;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_BLOCKING;
|
||||
|
|
|
@ -49,6 +49,7 @@ typedef struct CoNo {
|
|||
typedef bool (*StrokeGetLocation)(struct bContext *C, float location[3], const float mouse[2]);
|
||||
typedef bool (*StrokeTestStart)(struct bContext *C, struct wmOperator *op, const float mouse[2]);
|
||||
typedef void (*StrokeUpdateStep)(struct bContext *C,
|
||||
struct wmOperator *op,
|
||||
struct PaintStroke *stroke,
|
||||
struct PointerRNA *itemptr);
|
||||
typedef void (*StrokeRedraw)(const struct bContext *C, struct PaintStroke *stroke, bool final);
|
||||
|
@ -62,7 +63,7 @@ struct PaintStroke *paint_stroke_new(struct bContext *C,
|
|||
StrokeRedraw redraw,
|
||||
StrokeDone done,
|
||||
int event_type);
|
||||
void paint_stroke_free(struct bContext *C, struct wmOperator *op);
|
||||
void paint_stroke_free(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
|
||||
|
||||
/**
|
||||
* Returns zero if the stroke dots should not be spaced, non-zero otherwise.
|
||||
|
@ -84,9 +85,12 @@ bool paint_supports_jitter(enum ePaintMode mode);
|
|||
* Called in paint_ops.c, on each regeneration of key-maps.
|
||||
*/
|
||||
struct wmKeyMap *paint_stroke_modal_keymap(struct wmKeyConfig *keyconf);
|
||||
int paint_stroke_modal(struct bContext *C, struct wmOperator *op, const struct wmEvent *event);
|
||||
int paint_stroke_exec(struct bContext *C, struct wmOperator *op);
|
||||
void paint_stroke_cancel(struct bContext *C, struct wmOperator *op);
|
||||
int paint_stroke_modal(struct bContext *C,
|
||||
struct wmOperator *op,
|
||||
const struct wmEvent *event,
|
||||
struct PaintStroke *stroke);
|
||||
int paint_stroke_exec(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
|
||||
void paint_stroke_cancel(struct bContext *C, struct wmOperator *op, struct PaintStroke *stroke);
|
||||
bool paint_stroke_flipped(struct PaintStroke *stroke);
|
||||
bool paint_stroke_inverted(struct PaintStroke *stroke);
|
||||
struct ViewContext *paint_stroke_view_context(struct PaintStroke *stroke);
|
||||
|
|
|
@ -505,16 +505,13 @@ static bool paint_stroke_use_jitter(ePaintMode mode, Brush *brush, bool invert)
|
|||
}
|
||||
|
||||
/* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
|
||||
static void paint_brush_stroke_add_step(bContext *C,
|
||||
wmOperator *op,
|
||||
const float mouse_in[2],
|
||||
float pressure)
|
||||
static void paint_brush_stroke_add_step(
|
||||
bContext *C, wmOperator *op, PaintStroke *stroke, const float mouse_in[2], float pressure)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
|
||||
Brush *brush = BKE_paint_brush(paint);
|
||||
PaintStroke *stroke = op->customdata;
|
||||
UnifiedPaintSettings *ups = stroke->ups;
|
||||
float mouse_out[2];
|
||||
PointerRNA itemptr;
|
||||
|
@ -614,7 +611,7 @@ static void paint_brush_stroke_add_step(bContext *C,
|
|||
RNA_float_set(&itemptr, "x_tilt", stroke->x_tilt);
|
||||
RNA_float_set(&itemptr, "y_tilt", stroke->y_tilt);
|
||||
|
||||
stroke->update_step(C, stroke, &itemptr);
|
||||
stroke->update_step(C, op, stroke, &itemptr);
|
||||
|
||||
/* don't record this for now, it takes up a lot of memory when doing long
|
||||
* strokes with small brush size, and operators have register disabled */
|
||||
|
@ -785,12 +782,12 @@ static float paint_space_stroke_spacing_variable(bContext *C,
|
|||
* towards the final mouse location. */
|
||||
static int paint_space_stroke(bContext *C,
|
||||
wmOperator *op,
|
||||
PaintStroke *stroke,
|
||||
const float final_mouse[2],
|
||||
float final_pressure)
|
||||
{
|
||||
const Scene *scene = CTX_data_scene(C);
|
||||
ARegion *region = CTX_wm_region(C);
|
||||
PaintStroke *stroke = op->customdata;
|
||||
UnifiedPaintSettings *ups = stroke->ups;
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
|
||||
|
@ -852,7 +849,7 @@ static int paint_space_stroke(bContext *C,
|
|||
spacing / no_pressure_spacing);
|
||||
|
||||
stroke->stroke_distance += spacing / stroke->zoom_2d;
|
||||
paint_brush_stroke_add_step(C, op, mouse, pressure);
|
||||
paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
|
||||
|
||||
length -= spacing;
|
||||
pressure = stroke->last_pressure;
|
||||
|
@ -929,7 +926,7 @@ PaintStroke *paint_stroke_new(bContext *C,
|
|||
return stroke;
|
||||
}
|
||||
|
||||
void paint_stroke_free(bContext *C, wmOperator *op)
|
||||
void paint_stroke_free(bContext *C, wmOperator *UNUSED(op), PaintStroke *stroke)
|
||||
{
|
||||
RegionView3D *rv3d = CTX_wm_region_view3d(C);
|
||||
if (rv3d) {
|
||||
|
@ -938,7 +935,6 @@ void paint_stroke_free(bContext *C, wmOperator *op)
|
|||
|
||||
BKE_paint_set_overlay_override(0);
|
||||
|
||||
PaintStroke *stroke = op->customdata;
|
||||
if (stroke == NULL) {
|
||||
return;
|
||||
}
|
||||
|
@ -961,12 +957,11 @@ void paint_stroke_free(bContext *C, wmOperator *op)
|
|||
|
||||
BLI_freelistN(&stroke->line);
|
||||
|
||||
MEM_SAFE_FREE(op->customdata);
|
||||
MEM_SAFE_FREE(stroke);
|
||||
}
|
||||
|
||||
static void stroke_done(bContext *C, wmOperator *op)
|
||||
static void stroke_done(bContext *C, wmOperator *op, PaintStroke *stroke)
|
||||
{
|
||||
PaintStroke *stroke = op->customdata;
|
||||
UnifiedPaintSettings *ups = stroke->ups;
|
||||
|
||||
/* reset rotation here to avoid doing so in cursor display */
|
||||
|
@ -988,7 +983,7 @@ static void stroke_done(bContext *C, wmOperator *op)
|
|||
}
|
||||
}
|
||||
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, stroke);
|
||||
}
|
||||
|
||||
bool paint_space_stroke_enabled(Brush *br, ePaintMode mode)
|
||||
|
@ -1230,7 +1225,7 @@ static void paint_line_strokes_spacing(bContext *C,
|
|||
ups->overlap_factor = paint_stroke_integrate_overlap(stroke->brush, 1.0);
|
||||
|
||||
stroke->stroke_distance += spacing / stroke->zoom_2d;
|
||||
paint_brush_stroke_add_step(C, op, mouse, 1.0);
|
||||
paint_brush_stroke_add_step(C, op, stroke, mouse, 1.0);
|
||||
|
||||
length -= spacing;
|
||||
spacing_final = spacing;
|
||||
|
@ -1252,8 +1247,8 @@ static void paint_stroke_line_end(bContext *C,
|
|||
if (stroke->stroke_started && (br->flag & BRUSH_LINE)) {
|
||||
stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0);
|
||||
|
||||
paint_brush_stroke_add_step(C, op, stroke->last_mouse_position, 1.0);
|
||||
paint_space_stroke(C, op, mouse, 1.0);
|
||||
paint_brush_stroke_add_step(C, op, stroke, stroke->last_mouse_position, 1.0);
|
||||
paint_space_stroke(C, op, stroke, mouse, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1331,7 +1326,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
|
|||
stroke->stroke_started = stroke->test_start(C, op, stroke->last_mouse_position);
|
||||
|
||||
if (stroke->stroke_started) {
|
||||
paint_brush_stroke_add_step(C, op, data + 2 * j, 1.0);
|
||||
paint_brush_stroke_add_step(C, op, stroke, data + 2 * j, 1.0);
|
||||
paint_line_strokes_spacing(
|
||||
C, op, stroke, spacing, &length_residue, data + 2 * j, data + 2 * (j + 1));
|
||||
}
|
||||
|
@ -1343,7 +1338,7 @@ static bool paint_stroke_curve_end(bContext *C, wmOperator *op, PaintStroke *str
|
|||
}
|
||||
}
|
||||
|
||||
stroke_done(C, op);
|
||||
stroke_done(C, op, stroke);
|
||||
|
||||
#ifdef DEBUG_TIME
|
||||
TIMEIT_END_AVERAGED(whole_stroke);
|
||||
|
@ -1384,11 +1379,10 @@ static void paint_stroke_line_constrain(PaintStroke *stroke, float mouse[2])
|
|||
}
|
||||
}
|
||||
|
||||
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event, PaintStroke *stroke)
|
||||
{
|
||||
Paint *p = BKE_paint_get_active_from_context(C);
|
||||
ePaintMode mode = BKE_paintmode_get_active_from_context(C);
|
||||
PaintStroke *stroke = op->customdata;
|
||||
Brush *br = stroke->brush = BKE_paint_brush(p);
|
||||
PaintSample sample_average;
|
||||
float mouse[2];
|
||||
|
@ -1482,7 +1476,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
op->type->cancel(C, op);
|
||||
}
|
||||
else {
|
||||
paint_stroke_cancel(C, op);
|
||||
paint_stroke_cancel(C, op, stroke);
|
||||
}
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
@ -1492,13 +1486,13 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
copy_v2_fl2(mouse, event->mval[0], event->mval[1]);
|
||||
paint_stroke_line_constrain(stroke, mouse);
|
||||
paint_stroke_line_end(C, op, stroke, mouse);
|
||||
stroke_done(C, op);
|
||||
stroke_done(C, op, stroke);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
}
|
||||
else if (ELEM(event->type, EVT_RETKEY, EVT_SPACEKEY)) {
|
||||
paint_stroke_line_end(C, op, stroke, sample_average.mouse);
|
||||
stroke_done(C, op);
|
||||
stroke_done(C, op, stroke);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
else if (br->flag & BRUSH_LINE) {
|
||||
|
@ -1530,7 +1524,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) {
|
||||
if (stroke->stroke_started) {
|
||||
if (paint_space_stroke_enabled(br, mode)) {
|
||||
if (paint_space_stroke(C, op, mouse, pressure)) {
|
||||
if (paint_space_stroke(C, op, stroke, mouse, pressure)) {
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
|
@ -1538,7 +1532,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
float dmouse[2];
|
||||
sub_v2_v2v2(dmouse, mouse, stroke->last_mouse_position);
|
||||
stroke->stroke_distance += len_v2(dmouse);
|
||||
paint_brush_stroke_add_step(C, op, mouse, pressure);
|
||||
paint_brush_stroke_add_step(C, op, stroke, mouse, pressure);
|
||||
redraw = true;
|
||||
}
|
||||
}
|
||||
|
@ -1549,7 +1543,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
* instead of waiting till we have moved the space distance */
|
||||
if (first_dab && paint_space_stroke_enabled(br, mode) && !(br->flag & BRUSH_SMOOTH_STROKE)) {
|
||||
stroke->ups->overlap_factor = paint_stroke_integrate_overlap(br, 1.0);
|
||||
paint_brush_stroke_add_step(C, op, sample_average.mouse, sample_average.pressure);
|
||||
paint_brush_stroke_add_step(C, op, stroke, sample_average.mouse, sample_average.pressure);
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
|
@ -1572,10 +1566,8 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
return OPERATOR_RUNNING_MODAL;
|
||||
}
|
||||
|
||||
int paint_stroke_exec(bContext *C, wmOperator *op)
|
||||
int paint_stroke_exec(bContext *C, wmOperator *op, PaintStroke *stroke)
|
||||
{
|
||||
PaintStroke *stroke = op->customdata;
|
||||
|
||||
/* only when executed for the first time */
|
||||
if (stroke->stroke_started == 0) {
|
||||
PropertyRNA *strokeprop;
|
||||
|
@ -1592,21 +1584,21 @@ int paint_stroke_exec(bContext *C, wmOperator *op)
|
|||
|
||||
if (stroke->stroke_started) {
|
||||
RNA_BEGIN (op->ptr, itemptr, "stroke") {
|
||||
stroke->update_step(C, stroke, &itemptr);
|
||||
stroke->update_step(C, op, stroke, &itemptr);
|
||||
}
|
||||
RNA_END;
|
||||
}
|
||||
|
||||
bool ok = (stroke->stroke_started != 0);
|
||||
|
||||
stroke_done(C, op);
|
||||
stroke_done(C, op, stroke);
|
||||
|
||||
return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
void paint_stroke_cancel(bContext *C, wmOperator *op)
|
||||
void paint_stroke_cancel(bContext *C, wmOperator *op, PaintStroke *stroke)
|
||||
{
|
||||
stroke_done(C, op);
|
||||
stroke_done(C, op, stroke);
|
||||
}
|
||||
|
||||
ViewContext *paint_stroke_view_context(PaintStroke *stroke)
|
||||
|
|
|
@ -2370,7 +2370,10 @@ static void wpaint_do_symmetrical_brush_actions(
|
|||
cache->is_last_valid = true;
|
||||
}
|
||||
|
||||
static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
||||
static void wpaint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
@ -2551,7 +2554,7 @@ static int wpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* add modal handler */
|
||||
|
@ -2575,7 +2578,7 @@ static int wpaint_exec(bContext *C, wmOperator *op)
|
|||
0);
|
||||
|
||||
/* frees op->customdata */
|
||||
paint_stroke_exec(C, op);
|
||||
paint_stroke_exec(C, op, op->customdata);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -2588,7 +2591,12 @@ static void wpaint_cancel(bContext *C, wmOperator *op)
|
|||
ob->sculpt->cache = NULL;
|
||||
}
|
||||
|
||||
paint_stroke_cancel(C, op);
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
}
|
||||
|
||||
static int wpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, op->customdata);
|
||||
}
|
||||
|
||||
void PAINT_OT_weight_paint(wmOperatorType *ot)
|
||||
|
@ -2600,7 +2608,7 @@ void PAINT_OT_weight_paint(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
ot->invoke = wpaint_invoke;
|
||||
ot->modal = paint_stroke_modal;
|
||||
ot->modal = wpaint_modal;
|
||||
ot->exec = wpaint_exec;
|
||||
ot->poll = weight_paint_poll;
|
||||
ot->cancel = wpaint_cancel;
|
||||
|
@ -3395,7 +3403,10 @@ static void vpaint_do_symmetrical_brush_actions(
|
|||
cache->is_last_valid = true;
|
||||
}
|
||||
|
||||
static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr)
|
||||
static void vpaint_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *stroke,
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ToolSettings *ts = CTX_data_tool_settings(C);
|
||||
|
@ -3497,7 +3508,7 @@ static int vpaint_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|||
event->type);
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
|
@ -3522,7 +3533,7 @@ static int vpaint_exec(bContext *C, wmOperator *op)
|
|||
0);
|
||||
|
||||
/* frees op->customdata */
|
||||
paint_stroke_exec(C, op);
|
||||
paint_stroke_exec(C, op, op->customdata);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -3535,7 +3546,12 @@ static void vpaint_cancel(bContext *C, wmOperator *op)
|
|||
ob->sculpt->cache = NULL;
|
||||
}
|
||||
|
||||
paint_stroke_cancel(C, op);
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
}
|
||||
|
||||
static int vpaint_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, op->customdata);
|
||||
}
|
||||
|
||||
void PAINT_OT_vertex_paint(wmOperatorType *ot)
|
||||
|
@ -3547,7 +3563,7 @@ void PAINT_OT_vertex_paint(wmOperatorType *ot)
|
|||
|
||||
/* api callbacks */
|
||||
ot->invoke = vpaint_invoke;
|
||||
ot->modal = paint_stroke_modal;
|
||||
ot->modal = vpaint_modal;
|
||||
ot->exec = vpaint_exec;
|
||||
ot->poll = vertex_paint_poll;
|
||||
ot->cancel = vpaint_cancel;
|
||||
|
|
|
@ -5186,6 +5186,7 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f
|
|||
}
|
||||
|
||||
static void sculpt_stroke_update_step(bContext *C,
|
||||
wmOperator *UNUSED(op),
|
||||
struct PaintStroke *UNUSED(stroke),
|
||||
PointerRNA *itemptr)
|
||||
{
|
||||
|
@ -5333,12 +5334,12 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent
|
|||
ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click");
|
||||
|
||||
if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) {
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
return OPERATOR_PASS_THROUGH;
|
||||
}
|
||||
|
||||
if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) {
|
||||
paint_stroke_free(C, op);
|
||||
paint_stroke_free(C, op, op->customdata);
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
/* Add modal handler. */
|
||||
|
@ -5364,7 +5365,7 @@ static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op)
|
|||
0);
|
||||
|
||||
/* Frees op->customdata. */
|
||||
paint_stroke_exec(C, op);
|
||||
paint_stroke_exec(C, op, op->customdata);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
@ -5382,7 +5383,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
|
|||
paint_mesh_restore_co(sd, ob);
|
||||
}
|
||||
|
||||
paint_stroke_cancel(C, op);
|
||||
paint_stroke_cancel(C, op, op->customdata);
|
||||
|
||||
if (ss->cache) {
|
||||
SCULPT_cache_free(ss->cache);
|
||||
|
@ -5392,6 +5393,11 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
|
|||
sculpt_brush_exit_tex(sd);
|
||||
}
|
||||
|
||||
static int sculpt_brush_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
return paint_stroke_modal(C, op, event, op->customdata);
|
||||
}
|
||||
|
||||
void SCULPT_OT_brush_stroke(wmOperatorType *ot)
|
||||
{
|
||||
/* Identifiers. */
|
||||
|
@ -5401,7 +5407,7 @@ void SCULPT_OT_brush_stroke(wmOperatorType *ot)
|
|||
|
||||
/* API callbacks. */
|
||||
ot->invoke = sculpt_brush_stroke_invoke;
|
||||
ot->modal = paint_stroke_modal;
|
||||
ot->modal = sculpt_brush_stroke_modal;
|
||||
ot->exec = sculpt_brush_stroke_exec;
|
||||
ot->poll = SCULPT_poll;
|
||||
ot->cancel = sculpt_brush_stroke_cancel;
|
||||
|
|
|
@ -104,7 +104,9 @@ static void BKE_gpencil_instance_modifier_instance_tfm(Object *ob,
|
|||
float obinv[4][4];
|
||||
|
||||
unit_m4(mat_offset);
|
||||
add_v3_v3(mat_offset[3], mmd->offset);
|
||||
if (mmd->flag & GP_ARRAY_USE_OFFSET) {
|
||||
add_v3_v3(mat_offset[3], mmd->offset);
|
||||
}
|
||||
invert_m4_m4(obinv, ob->obmat);
|
||||
|
||||
mul_m4_series(r_offset, mat_offset, obinv, mmd->object->obmat);
|
||||
|
|
|
@ -393,7 +393,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE);
|
||||
uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE);
|
||||
uiItemR(col, ptr, "use_back_face_culling", 0, NULL, ICON_NONE);
|
||||
uiItemR(col, ptr, "use_back_face_culling", 0, IFACE_("Force Backface Culling"), ICON_NONE);
|
||||
}
|
||||
|
||||
static void style_panel_draw(const bContext *UNUSED(C), Panel *panel)
|
||||
|
|
|
@ -88,7 +88,7 @@ static void deformStroke(GpencilModifierData *md,
|
|||
break;
|
||||
}
|
||||
case GP_SIMPLIFY_SAMPLE: {
|
||||
BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false);
|
||||
BKE_gpencil_stroke_sample(gpd, gps, mmd->length, false, mmd->sharp_threshold);
|
||||
break;
|
||||
}
|
||||
case GP_SIMPLIFY_MERGE: {
|
||||
|
@ -143,6 +143,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel)
|
|||
}
|
||||
else if (mode == GP_SIMPLIFY_SAMPLE) {
|
||||
uiItemR(layout, ptr, "length", 0, NULL, ICON_NONE);
|
||||
uiItemR(layout, ptr, "sharp_threshold", 0, NULL, ICON_NONE);
|
||||
}
|
||||
else if (mode == GP_SIMPLIFY_MERGE) {
|
||||
uiItemR(layout, ptr, "distance", 0, NULL, ICON_NONE);
|
||||
|
|
|
@ -343,6 +343,7 @@ typedef enum eLineartTriangleFlags {
|
|||
LRT_CULL_GENERATED = (1 << 2),
|
||||
LRT_TRIANGLE_INTERSECTION_ONLY = (1 << 3),
|
||||
LRT_TRIANGLE_NO_INTERSECTION = (1 << 4),
|
||||
LRT_TRIANGLE_MAT_BACK_FACE_CULLING = (1 << 5),
|
||||
} eLineartTriangleFlags;
|
||||
|
||||
/**
|
||||
|
|
|
@ -1529,8 +1529,9 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
|
|||
double *view_vector = vv;
|
||||
double dot_1 = 0, dot_2 = 0;
|
||||
double result;
|
||||
bool material_back_face = ((tri1->flags | tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING);
|
||||
|
||||
if (rb->use_contour || rb->use_back_face_culling) {
|
||||
if (rb->use_contour || rb->use_back_face_culling || material_back_face) {
|
||||
|
||||
if (rb->cam_is_persp) {
|
||||
sub_v3_v3v3_db(view_vector, rb->camera_pos, l->gloc);
|
||||
|
@ -1555,14 +1556,19 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
|
|||
tri2->flags |= LRT_CULL_DISCARD;
|
||||
}
|
||||
}
|
||||
if (material_back_face) {
|
||||
if (tri1->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_1 < 0) {
|
||||
tri1->flags |= LRT_CULL_DISCARD;
|
||||
}
|
||||
if (tri2->flags & LRT_TRIANGLE_MAT_BACK_FACE_CULLING && dot_2 < 0) {
|
||||
tri2->flags |= LRT_CULL_DISCARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
view_vector = rb->view_vector;
|
||||
}
|
||||
|
||||
dot_1 = dot_v3v3_db(view_vector, tri1->gn);
|
||||
dot_2 = dot_v3v3_db(view_vector, tri2->gn);
|
||||
|
||||
if ((result = dot_1 * dot_2) <= 0 && (fabs(dot_1) + fabs(dot_2))) {
|
||||
edge_flag_result |= LRT_EDGE_FLAG_CONTOUR;
|
||||
}
|
||||
|
@ -1573,6 +1579,16 @@ static uint16_t lineart_identify_feature_line(LineartRenderBuffer *rb,
|
|||
return edge_flag_result;
|
||||
}
|
||||
|
||||
/* Do not show lines other than contour on back face (because contour has one adjacent face that
|
||||
* isn't a back face).
|
||||
* TODO(Yiming): Do we need separate option for this? */
|
||||
if (rb->use_back_face_culling ||
|
||||
((tri1->flags & tri2->flags) & LRT_TRIANGLE_MAT_BACK_FACE_CULLING)) {
|
||||
if (dot_1 < 0 && dot_2 < 0) {
|
||||
return edge_flag_result;
|
||||
}
|
||||
}
|
||||
|
||||
if (rb->use_crease) {
|
||||
if (rb->sharp_as_crease && !BM_elem_flag_test(e, BM_ELEM_SMOOTH)) {
|
||||
edge_flag_result |= LRT_EDGE_FLAG_CREASE;
|
||||
|
@ -1859,6 +1875,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu
|
|||
mat->lineart.material_mask_bits :
|
||||
0);
|
||||
tri->mat_occlusion |= (mat ? mat->lineart.mat_occlusion : 1);
|
||||
tri->flags |= (mat && (mat->blend_flag & MA_BL_CULL_BACKFACE)) ?
|
||||
LRT_TRIANGLE_MAT_BACK_FACE_CULLING :
|
||||
0;
|
||||
|
||||
tri->intersection_mask = obi->override_intersection_mask;
|
||||
|
||||
|
|
|
@ -196,7 +196,7 @@ void GpencilExporterPDF::export_gpencil_layers()
|
|||
|
||||
/* Sample stroke. */
|
||||
if (params_.stroke_sample > 0.0f) {
|
||||
BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false);
|
||||
BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0);
|
||||
}
|
||||
|
||||
export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false);
|
||||
|
|
|
@ -221,7 +221,7 @@ void GpencilExporterSVG::export_gpencil_layers()
|
|||
|
||||
/* Sample stroke. */
|
||||
if (params_.stroke_sample > 0.0f) {
|
||||
BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false);
|
||||
BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0);
|
||||
}
|
||||
|
||||
export_stroke_to_path(gpl, gps_perimeter, node_gpl, false);
|
||||
|
|
|
@ -649,9 +649,10 @@ typedef struct SimplifyGpencilModifierData {
|
|||
int layer_pass;
|
||||
/** Sample length */
|
||||
float length;
|
||||
/** Sample sharp threshold */
|
||||
float sharp_threshold;
|
||||
/** Merge distance */
|
||||
float distance;
|
||||
char _pad[4];
|
||||
} SimplifyGpencilModifierData;
|
||||
|
||||
typedef enum eSimplifyGpencil_Flag {
|
||||
|
|
|
@ -1604,6 +1604,10 @@ enum {
|
|||
MOD_WVG_MIX_DIF = 6,
|
||||
/** Average of both weights. */
|
||||
MOD_WVG_MIX_AVG = 7,
|
||||
/** Minimum of both weights. */
|
||||
MOD_WVG_MIX_MIN = 8,
|
||||
/** Maximum of both weights. */
|
||||
MOD_WVG_MIX_MAX = 9,
|
||||
};
|
||||
|
||||
/** #WeightVGMixModifierData.mix_set (what vertices to affect). */
|
||||
|
@ -1694,7 +1698,7 @@ enum {
|
|||
MOD_WVG_PROXIMITY_GEOM_FACES = (1 << 2),
|
||||
MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK = (1 << 3),
|
||||
MOD_WVG_PROXIMITY_INVERT_FALLOFF = (1 << 4),
|
||||
MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 3),
|
||||
MOD_WVG_PROXIMITY_WEIGHTS_NORMALIZE = (1 << 5),
|
||||
};
|
||||
|
||||
/* Defines common to all WeightVG modifiers. */
|
||||
|
|
|
@ -4391,24 +4391,21 @@ void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropert
|
|||
|
||||
if (tot == 0) {
|
||||
*items = MEM_callocN(sizeof(EnumPropertyItem[8]), __func__);
|
||||
/* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */
|
||||
#ifdef DEBUG
|
||||
memset(*items, 0xff, sizeof(EnumPropertyItem[8]));
|
||||
#endif
|
||||
}
|
||||
else if (tot >= 8 && (tot & (tot - 1)) == 0) {
|
||||
/* power of two > 8 */
|
||||
*items = MEM_recallocN_id(*items, sizeof(EnumPropertyItem) * tot * 2, __func__);
|
||||
#ifdef DEBUG
|
||||
memset((*items) + tot, 0xff, sizeof(EnumPropertyItem) * tot);
|
||||
#endif
|
||||
}
|
||||
|
||||
(*items)[tot] = *item;
|
||||
*totitem = tot + 1;
|
||||
|
||||
/* Ensure we get crashes on missing calls to 'RNA_enum_item_end', see T74227. */
|
||||
#ifdef DEBUG
|
||||
static const EnumPropertyItem item_error = {
|
||||
-1, POINTER_FROM_INT(-1), -1, POINTER_FROM_INT(-1), POINTER_FROM_INT(-1)};
|
||||
if (item != &item_error) {
|
||||
RNA_enum_item_add(items, totitem, &item_error);
|
||||
*totitem -= 1;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void RNA_enum_item_add_separator(EnumPropertyItem **items, int *totitem)
|
||||
|
|
|
@ -1223,6 +1223,14 @@ static void rna_def_modifier_gpencilsimplify(BlenderRNA *brna)
|
|||
RNA_def_property_ui_text(prop, "Length", "Length of each segment");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "sharp_threshold", PROP_FLOAT, PROP_ANGLE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "sharp_threshold");
|
||||
RNA_def_property_range(prop, 0, M_PI);
|
||||
RNA_def_property_ui_range(prop, 0, M_PI, 1.0, 1);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Sharp Threshold", "Preserve corners that have sharper angle than this threshold");
|
||||
RNA_def_property_update(prop, 0, "rna_GpencilModifier_update");
|
||||
|
||||
/* Merge */
|
||||
prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE);
|
||||
RNA_def_property_float_sdna(prop, NULL, "distance");
|
||||
|
|
|
@ -4949,6 +4949,7 @@ static void rna_def_modifier_uvwarp(BlenderRNA *brna)
|
|||
static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna),
|
||||
StructRNA *srna,
|
||||
const char *mask_flags,
|
||||
const int invert_vgroup_mask_flag,
|
||||
const char *mask_vgroup_setter,
|
||||
const char *mask_uvlayer_setter)
|
||||
{
|
||||
|
@ -4994,7 +4995,7 @@ static void rna_def_modifier_weightvg_mask(BlenderRNA *UNUSED(brna),
|
|||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
prop = RNA_def_property(srna, "invert_mask_vertex_group", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, mask_flags, MOD_WVG_EDIT_INVERT_VGROUP_MASK);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, mask_flags, invert_vgroup_mask_flag);
|
||||
RNA_def_property_ui_text(prop, "Invert", "Invert vertex group mask influence");
|
||||
RNA_def_property_update(prop, 0, "rna_Modifier_update");
|
||||
|
||||
|
@ -5151,6 +5152,7 @@ static void rna_def_modifier_weightvgedit(BlenderRNA *brna)
|
|||
rna_def_modifier_weightvg_mask(brna,
|
||||
srna,
|
||||
"edit_flags",
|
||||
MOD_WVG_EDIT_INVERT_VGROUP_MASK,
|
||||
"rna_WeightVGEditModifier_mask_defgrp_name_set",
|
||||
"rna_WeightVGEditModifier_mask_tex_uvlayer_name_set");
|
||||
}
|
||||
|
@ -5169,6 +5171,8 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna)
|
|||
"Difference",
|
||||
"Difference between VGroup A's and VGroup B's weights"},
|
||||
{MOD_WVG_MIX_AVG, "AVG", 0, "Average", "Average value of VGroup A's and VGroup B's weights"},
|
||||
{MOD_WVG_MIX_MIN, "MIN", 0, "Minimum", "Minimum of VGroup A's and VGroup B's weights"},
|
||||
{MOD_WVG_MIX_MAX, "MAX", 0, "Maximum", "Maximum of VGroup A's and VGroup B's weights"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
|
@ -5266,6 +5270,7 @@ static void rna_def_modifier_weightvgmix(BlenderRNA *brna)
|
|||
rna_def_modifier_weightvg_mask(brna,
|
||||
srna,
|
||||
"flag",
|
||||
MOD_WVG_MIX_INVERT_VGROUP_MASK,
|
||||
"rna_WeightVGMixModifier_mask_defgrp_name_set",
|
||||
"rna_WeightVGMixModifier_mask_tex_uvlayer_name_set");
|
||||
}
|
||||
|
@ -5396,6 +5401,7 @@ static void rna_def_modifier_weightvgproximity(BlenderRNA *brna)
|
|||
rna_def_modifier_weightvg_mask(brna,
|
||||
srna,
|
||||
"proximity_flags",
|
||||
MOD_WVG_PROXIMITY_INVERT_VGROUP_MASK,
|
||||
"rna_WeightVGProximityModifier_mask_defgrp_name_set",
|
||||
"rna_WeightVGProximityModifier_mask_tex_uvlayer_name_set");
|
||||
}
|
||||
|
|
|
@ -2898,6 +2898,10 @@ static void rna_def_tool_settings(BlenderRNA *brna)
|
|||
RNA_def_property_struct_type(prop, "Sculpt");
|
||||
RNA_def_property_ui_text(prop, "Sculpt", "");
|
||||
|
||||
prop = RNA_def_property(srna, "curves_sculpt", PROP_POINTER, PROP_NONE);
|
||||
RNA_def_property_struct_type(prop, "CurvesSculpt");
|
||||
RNA_def_property_ui_text(prop, "Curves Sculpt", "");
|
||||
|
||||
prop = RNA_def_property(srna, "use_auto_normalize", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
|
||||
RNA_def_property_boolean_sdna(prop, NULL, "auto_normalize", 1);
|
||||
|
|
|
@ -423,6 +423,11 @@ static char *rna_UvSculpt_path(PointerRNA *UNUSED(ptr))
|
|||
return BLI_strdup("tool_settings.uv_sculpt");
|
||||
}
|
||||
|
||||
static char *rna_CurvesSculpt_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.curves_sculpt");
|
||||
}
|
||||
|
||||
static char *rna_GpPaint_path(PointerRNA *UNUSED(ptr))
|
||||
{
|
||||
return BLI_strdup("tool_settings.gpencil_paint");
|
||||
|
@ -1498,6 +1503,15 @@ static void rna_def_gpencil_sculpt(BlenderRNA *brna)
|
|||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
}
|
||||
|
||||
static void rna_def_curves_sculpt(BlenderRNA *brna)
|
||||
{
|
||||
StructRNA *srna;
|
||||
|
||||
srna = RNA_def_struct(brna, "CurvesSculpt", "Paint");
|
||||
RNA_def_struct_path_func(srna, "rna_CurvesSculpt_path");
|
||||
RNA_def_struct_ui_text(srna, "Curves Sculpt Paint", "");
|
||||
}
|
||||
|
||||
void RNA_def_sculpt_paint(BlenderRNA *brna)
|
||||
{
|
||||
/* *** Non-Animated *** */
|
||||
|
@ -1516,6 +1530,7 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
|
|||
rna_def_particle_edit(brna);
|
||||
rna_def_gpencil_guides(brna);
|
||||
rna_def_gpencil_sculpt(brna);
|
||||
rna_def_curves_sculpt(brna);
|
||||
RNA_define_animate_sdna(true);
|
||||
}
|
||||
|
||||
|
|
|
@ -454,6 +454,10 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket)
|
|||
ui_data->base.rna_subtype = PROP_COLOR;
|
||||
ui_data->default_array = (double *)MEM_mallocN(sizeof(double[4]), __func__);
|
||||
ui_data->default_array_len = 4;
|
||||
ui_data->min = 0.0;
|
||||
ui_data->max = FLT_MAX;
|
||||
ui_data->soft_min = 0.0;
|
||||
ui_data->soft_max = 1.0;
|
||||
for (const int i : IndexRange(4)) {
|
||||
ui_data->default_array[i] = double(value->value[i]);
|
||||
}
|
||||
|
|
|
@ -1405,21 +1405,26 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
for (uint j = 0; g->valid; j++, g++) {
|
||||
if (!g->is_singularity) {
|
||||
float *nor = g->no;
|
||||
/* During vertex position calculation, the algorithm decides if it wants to disable the
|
||||
* boundary fix to maintain correct thickness. If the used algorithm does not produce a
|
||||
* free move direction (move_nor), it can use approximate_free_direction to decide on
|
||||
* a movement direction based on the connected edges. */
|
||||
float move_nor[3] = {0, 0, 0};
|
||||
bool disable_boundary_fix = (smd->nonmanifold_boundary_mode ==
|
||||
MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_NONE ||
|
||||
(g->is_orig_closed || g->split));
|
||||
bool approximate_free_direction = false;
|
||||
/* Constraints Method. */
|
||||
if (smd->nonmanifold_offset_mode == MOD_SOLIDIFY_NONMANIFOLD_OFFSET_MODE_CONSTRAINTS) {
|
||||
NewEdgeRef *first_edge = NULL;
|
||||
NewEdgeRef **edge_ptr = g->edges;
|
||||
/* Contains normal and offset [nx, ny, nz, ofs]. */
|
||||
float(*normals_queue)[4] = MEM_malloc_arrayN(
|
||||
g->edges_len + 1, sizeof(*normals_queue), "normals_queue in solidify");
|
||||
float(*planes_queue)[4] = MEM_malloc_arrayN(
|
||||
g->edges_len + 1, sizeof(*planes_queue), "planes_queue in solidify");
|
||||
uint queue_index = 0;
|
||||
|
||||
float face_nors[3][3];
|
||||
float nor_ofs[3];
|
||||
float fallback_nor[3];
|
||||
float fallback_ofs = 0.0f;
|
||||
|
||||
const bool cycle = (g->is_orig_closed && !g->split) || g->is_even_split;
|
||||
for (uint k = 0; k < g->edges_len; k++, edge_ptr++) {
|
||||
|
@ -1436,17 +1441,17 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
}
|
||||
|
||||
if (!null_faces[face->index]) {
|
||||
/* And normal to the queue. */
|
||||
mul_v3_v3fl(normals_queue[queue_index],
|
||||
/* And plane to the queue. */
|
||||
mul_v3_v3fl(planes_queue[queue_index],
|
||||
poly_nors[face->index],
|
||||
face->reversed ? -1 : 1);
|
||||
normals_queue[queue_index++][3] = ofs;
|
||||
planes_queue[queue_index++][3] = ofs;
|
||||
}
|
||||
else {
|
||||
/* Just use this approximate normal of the null face if there is no other
|
||||
* normal to use. */
|
||||
mul_v3_v3fl(face_nors[0], poly_nors[face->index], face->reversed ? -1 : 1);
|
||||
nor_ofs[0] = ofs;
|
||||
mul_v3_v3fl(fallback_nor, poly_nors[face->index], face->reversed ? -1 : 1);
|
||||
fallback_ofs = ofs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1455,131 +1460,170 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
}
|
||||
}
|
||||
}
|
||||
uint face_nors_len = 0;
|
||||
const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
|
||||
while (queue_index > 0) {
|
||||
if (face_nors_len == 0) {
|
||||
if (queue_index <= 2) {
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
copy_v3_v3(face_nors[k], normals_queue[k]);
|
||||
nor_ofs[k] = normals_queue[k][3];
|
||||
}
|
||||
face_nors_len = queue_index;
|
||||
queue_index = 0;
|
||||
}
|
||||
else {
|
||||
/* Find most different two normals. */
|
||||
float min_p = 2;
|
||||
uint min_n0 = 0;
|
||||
uint min_n1 = 0;
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
for (uint m = k + 1; m < queue_index; m++) {
|
||||
float p = dot_v3v3(normals_queue[k], normals_queue[m]);
|
||||
if (p <= min_p + FLT_EPSILON) {
|
||||
min_p = p;
|
||||
min_n0 = m;
|
||||
min_n1 = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
copy_v3_v3(face_nors[0], normals_queue[min_n0]);
|
||||
copy_v3_v3(face_nors[1], normals_queue[min_n1]);
|
||||
nor_ofs[0] = normals_queue[min_n0][3];
|
||||
nor_ofs[1] = normals_queue[min_n1][3];
|
||||
face_nors_len = 2;
|
||||
queue_index--;
|
||||
memmove(normals_queue + min_n0,
|
||||
normals_queue + min_n0 + 1,
|
||||
(queue_index - min_n0) * sizeof(*normals_queue));
|
||||
queue_index--;
|
||||
memmove(normals_queue + min_n1,
|
||||
normals_queue + min_n1 + 1,
|
||||
(queue_index - min_n1) * sizeof(*normals_queue));
|
||||
min_p = 1;
|
||||
min_n1 = 0;
|
||||
float max_p = -1;
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
max_p = -1;
|
||||
for (uint m = 0; m < face_nors_len; m++) {
|
||||
float p = dot_v3v3(face_nors[m], normals_queue[k]);
|
||||
if (p > max_p + FLT_EPSILON) {
|
||||
max_p = p;
|
||||
}
|
||||
}
|
||||
if (max_p <= min_p + FLT_EPSILON) {
|
||||
min_p = max_p;
|
||||
min_n1 = k;
|
||||
}
|
||||
}
|
||||
if (min_p < 0.8) {
|
||||
copy_v3_v3(face_nors[2], normals_queue[min_n1]);
|
||||
nor_ofs[2] = normals_queue[min_n1][3];
|
||||
face_nors_len++;
|
||||
queue_index--;
|
||||
memmove(normals_queue + min_n1,
|
||||
normals_queue + min_n1 + 1,
|
||||
(queue_index - min_n1) * sizeof(*normals_queue));
|
||||
if (queue_index > 2) {
|
||||
/* Find the two most different normals. */
|
||||
float min_p = 2.0f;
|
||||
uint min_n0 = 0;
|
||||
uint min_n1 = 0;
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
for (uint m = k + 1; m < queue_index; m++) {
|
||||
float p = dot_v3v3(planes_queue[k], planes_queue[m]);
|
||||
if (p <= min_p + FLT_EPSILON) {
|
||||
min_p = p;
|
||||
min_n0 = m;
|
||||
min_n1 = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Put the two found normals, first in the array queue. */
|
||||
if (min_n1 != 0) {
|
||||
swap_v4_v4(planes_queue[min_n0], planes_queue[0]);
|
||||
swap_v4_v4(planes_queue[min_n1], planes_queue[1]);
|
||||
}
|
||||
else {
|
||||
uint best = 0;
|
||||
uint best_group = 0;
|
||||
float best_p = -1.0f;
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
for (uint m = 0; m < face_nors_len; m++) {
|
||||
float p = dot_v3v3(face_nors[m], normals_queue[k]);
|
||||
if (p > best_p + FLT_EPSILON) {
|
||||
best_p = p;
|
||||
best = m;
|
||||
best_group = k;
|
||||
swap_v4_v4(planes_queue[min_n0], planes_queue[1]);
|
||||
}
|
||||
/* Find the third most important/different normal. */
|
||||
min_p = 1;
|
||||
min_n1 = 0;
|
||||
float max_p = -1;
|
||||
for (uint k = 2; k < queue_index; k++) {
|
||||
max_p = -1;
|
||||
for (uint m = 0; m < 2; m++) {
|
||||
float p = dot_v3v3(planes_queue[m], planes_queue[k]);
|
||||
if (p > max_p + FLT_EPSILON) {
|
||||
max_p = p;
|
||||
}
|
||||
}
|
||||
if (max_p <= min_p + FLT_EPSILON) {
|
||||
min_p = max_p;
|
||||
min_n1 = k;
|
||||
}
|
||||
}
|
||||
swap_v4_v4(planes_queue[min_n1], planes_queue[2]);
|
||||
}
|
||||
/* Remove/average duplicate normals in planes_queue. */
|
||||
while (queue_index > 0) {
|
||||
uint best_n0 = 0;
|
||||
uint best_n1 = 0;
|
||||
float best_p = -1.0f;
|
||||
float best_ofs_diff = 0.0f;
|
||||
for (uint k = 0; k < queue_index; k++) {
|
||||
for (uint m = k + 1; m < queue_index; m++) {
|
||||
float p = dot_v3v3(planes_queue[m], planes_queue[k]);
|
||||
float ofs_diff = fabsf(planes_queue[m][3] - planes_queue[k][3]);
|
||||
if (p > best_p + FLT_EPSILON || (p >= best_p && ofs_diff < best_ofs_diff)) {
|
||||
best_p = p;
|
||||
best_ofs_diff = ofs_diff;
|
||||
best_n0 = m;
|
||||
best_n1 = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (best_p < 0.999f) {
|
||||
break;
|
||||
}
|
||||
add_v3_v3(planes_queue[best_n0], planes_queue[best_n1]);
|
||||
normalize_v3(planes_queue[best_n0]);
|
||||
planes_queue[best_n0][3] = (planes_queue[best_n0][3] + planes_queue[best_n1][3]) *
|
||||
0.5f;
|
||||
queue_index--;
|
||||
memmove(planes_queue + best_n1,
|
||||
planes_queue + best_n1 + 1,
|
||||
(queue_index - best_n1) * sizeof(*planes_queue));
|
||||
}
|
||||
const uint size = queue_index;
|
||||
/* If there is more than 2 planes at this vertex, the boundary fix should be disabled
|
||||
* to stay at the correct thickness for all the faces. This is not very good in
|
||||
* practice though, since that will almost always disable the boundary fix. Instead
|
||||
* introduce a threshold which decides whether the boundary fix can be used without
|
||||
* major thickness changes. If the following constant is 1.0, it would always
|
||||
* prioritize correct thickness. At 0.7 the thickness is allowed to change a bit if
|
||||
* necessary for the fix (~10%). Note this only applies if a boundary fix is used. */
|
||||
const float boundary_fix_threshold = 0.7f;
|
||||
if (size > 3) {
|
||||
/* Use the most general least squares method to find the best position. */
|
||||
float mat[3][3];
|
||||
zero_m3(mat);
|
||||
for (int k = 0; k < 3; k++) {
|
||||
for (int m = 0; m < size; m++) {
|
||||
madd_v3_v3fl(mat[k], planes_queue[m], planes_queue[m][k]);
|
||||
}
|
||||
}
|
||||
/* NOTE: this matrix invert fails if there is less than 3 different normals. */
|
||||
invert_m3(mat);
|
||||
zero_v3(nor);
|
||||
for (int k = 0; k < size; k++) {
|
||||
madd_v3_v3fl(nor, planes_queue[k], planes_queue[k][3]);
|
||||
}
|
||||
mul_v3_m3v3(nor, mat, nor);
|
||||
|
||||
if (!disable_boundary_fix) {
|
||||
/* Figure out if the approximate boundary fix can get use here. */
|
||||
float greatest_angle_cos = 1.0f;
|
||||
for (uint k = 0; k < 2; k++) {
|
||||
for (uint m = 2; m < size; m++) {
|
||||
float p = dot_v3v3(planes_queue[m], planes_queue[k]);
|
||||
if (p < greatest_angle_cos) {
|
||||
greatest_angle_cos = p;
|
||||
}
|
||||
}
|
||||
}
|
||||
add_v3_v3(face_nors[best], normals_queue[best_group]);
|
||||
normalize_v3(face_nors[best]);
|
||||
nor_ofs[best] = (nor_ofs[best] + normals_queue[best_group][3]) * 0.5f;
|
||||
queue_index--;
|
||||
memmove(normals_queue + best_group,
|
||||
normals_queue + best_group + 1,
|
||||
(queue_index - best_group) * sizeof(*normals_queue));
|
||||
if (greatest_angle_cos > boundary_fix_threshold) {
|
||||
approximate_free_direction = true;
|
||||
}
|
||||
else {
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
MEM_freeN(normals_queue);
|
||||
|
||||
/* When up to 3 constraint normals are found. */
|
||||
if (ELEM(face_nors_len, 2, 3)) {
|
||||
const float q = dot_v3v3(face_nors[0], face_nors[1]);
|
||||
else if (size > 1) {
|
||||
/* When up to 3 constraint normals are found, there is a simple solution. */
|
||||
const float stop_explosion = 0.999f - fabsf(smd->offset_fac) * 0.05f;
|
||||
const float q = dot_v3v3(planes_queue[0], planes_queue[1]);
|
||||
float d = 1.0f - q * q;
|
||||
cross_v3_v3v3(move_nor, face_nors[0], face_nors[1]);
|
||||
cross_v3_v3v3(move_nor, planes_queue[0], planes_queue[1]);
|
||||
normalize_v3(move_nor);
|
||||
if (d > FLT_EPSILON * 10 && q < stop_explosion) {
|
||||
d = 1.0f / d;
|
||||
mul_v3_fl(face_nors[0], (nor_ofs[0] - nor_ofs[1] * q) * d);
|
||||
mul_v3_fl(face_nors[1], (nor_ofs[1] - nor_ofs[0] * q) * d);
|
||||
mul_v3_fl(planes_queue[0], (planes_queue[0][3] - planes_queue[1][3] * q) * d);
|
||||
mul_v3_fl(planes_queue[1], (planes_queue[1][3] - planes_queue[0][3] * q) * d);
|
||||
}
|
||||
else {
|
||||
d = 1.0f / (fabsf(q) + 1.0f);
|
||||
mul_v3_fl(face_nors[0], nor_ofs[0] * d);
|
||||
mul_v3_fl(face_nors[1], nor_ofs[1] * d);
|
||||
mul_v3_fl(planes_queue[0], planes_queue[0][3] * d);
|
||||
mul_v3_fl(planes_queue[1], planes_queue[1][3] * d);
|
||||
}
|
||||
add_v3_v3v3(nor, face_nors[0], face_nors[1]);
|
||||
if (face_nors_len == 3) {
|
||||
float *free_nor = move_nor;
|
||||
mul_v3_fl(face_nors[2], nor_ofs[2]);
|
||||
d = dot_v3v3(face_nors[2], free_nor);
|
||||
add_v3_v3v3(nor, planes_queue[0], planes_queue[1]);
|
||||
if (size == 3) {
|
||||
d = dot_v3v3(planes_queue[2], move_nor);
|
||||
if (LIKELY(fabsf(d) > FLT_EPSILON)) {
|
||||
sub_v3_v3v3(face_nors[0], nor, face_nors[2]); /* Override face_nor[0]. */
|
||||
mul_v3_fl(free_nor, dot_v3v3(face_nors[2], face_nors[0]) / d);
|
||||
sub_v3_v3(nor, free_nor);
|
||||
float tmp[3];
|
||||
madd_v3_v3v3fl(tmp, nor, planes_queue[2], -planes_queue[2][3]);
|
||||
mul_v3_v3fl(tmp, move_nor, dot_v3v3(planes_queue[2], tmp) / d);
|
||||
sub_v3_v3(nor, tmp);
|
||||
/* Disable boundary fix if the constraints would be majorly unsatisfied. */
|
||||
if (fabsf(d) > 1.0f - boundary_fix_threshold) {
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
approximate_free_direction = false;
|
||||
}
|
||||
else if (size == 1) {
|
||||
/* Face corner case. */
|
||||
mul_v3_v3fl(nor, planes_queue[0], planes_queue[0][3]);
|
||||
if (g->edges_len > 2) {
|
||||
disable_boundary_fix = true;
|
||||
approximate_free_direction = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert(face_nors_len < 2);
|
||||
mul_v3_v3fl(nor, face_nors[0], nor_ofs[0]);
|
||||
/* Fallback case for null faces. */
|
||||
mul_v3_v3fl(nor, fallback_nor, fallback_ofs);
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
MEM_freeN(planes_queue);
|
||||
}
|
||||
/* Fixed/Even Method. */
|
||||
else {
|
||||
|
@ -1707,26 +1751,29 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
}
|
||||
/* Set move_nor for boundary fix. */
|
||||
if (!disable_boundary_fix && g->edges_len > 2) {
|
||||
edge_ptr = g->edges + 1;
|
||||
float tmp[3];
|
||||
uint k;
|
||||
for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
|
||||
MEdge *e = orig_medge + (*edge_ptr)->old_edge;
|
||||
sub_v3_v3v3(
|
||||
tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
|
||||
add_v3_v3(move_nor, tmp);
|
||||
}
|
||||
if (k == 1) {
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
else {
|
||||
disable_boundary_fix = normalize_v3(move_nor) == 0.0f;
|
||||
}
|
||||
approximate_free_direction = true;
|
||||
}
|
||||
else {
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
}
|
||||
if (approximate_free_direction) {
|
||||
/* Set move_nor for boundary fix. */
|
||||
NewEdgeRef **edge_ptr = g->edges + 1;
|
||||
float tmp[3];
|
||||
int k;
|
||||
for (k = 1; k + 1 < g->edges_len; k++, edge_ptr++) {
|
||||
MEdge *e = orig_medge + (*edge_ptr)->old_edge;
|
||||
sub_v3_v3v3(tmp, orig_mvert_co[vm[e->v1] == i ? e->v2 : e->v1], orig_mvert_co[i]);
|
||||
add_v3_v3(move_nor, tmp);
|
||||
}
|
||||
if (k == 1) {
|
||||
disable_boundary_fix = true;
|
||||
}
|
||||
else {
|
||||
disable_boundary_fix = normalize_v3(move_nor) == 0.0f;
|
||||
}
|
||||
}
|
||||
/* Fix boundary verts. */
|
||||
if (!disable_boundary_fix) {
|
||||
/* Constraint normal, nor * constr_nor == 0 after this fix. */
|
||||
|
@ -1743,8 +1790,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
orig_mvert_co[i]);
|
||||
if (smd->nonmanifold_boundary_mode == MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_FLAT) {
|
||||
cross_v3_v3v3(constr_nor, e0, e1);
|
||||
normalize_v3(constr_nor);
|
||||
}
|
||||
else {
|
||||
BLI_assert(smd->nonmanifold_boundary_mode ==
|
||||
MOD_SOLIDIFY_NONMANIFOLD_BOUNDARY_MODE_ROUND);
|
||||
float f0[3];
|
||||
float f1[3];
|
||||
if (g->edges[0]->faces[0]->reversed) {
|
||||
|
@ -1766,9 +1816,11 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md,
|
|||
normalize_v3(n0);
|
||||
normalize_v3(n1);
|
||||
add_v3_v3v3(constr_nor, n0, n1);
|
||||
normalize_v3(constr_nor);
|
||||
}
|
||||
float d = dot_v3v3(constr_nor, move_nor);
|
||||
if (LIKELY(fabsf(d) > FLT_EPSILON)) {
|
||||
/* Only allow the thickness to increase about 10 times. */
|
||||
if (fabsf(d) > 0.1f) {
|
||||
mul_v3_fl(move_nor, dot_v3v3(constr_nor, nor) / d);
|
||||
sub_v3_v3(nor, move_nor);
|
||||
}
|
||||
|
|
|
@ -105,6 +105,12 @@ static float mix_weight(float weight, float weight2, char mix_mode)
|
|||
if (mix_mode == MOD_WVG_MIX_AVG) {
|
||||
return (weight + weight2) * 0.5f;
|
||||
}
|
||||
if (mix_mode == MOD_WVG_MIX_MIN) {
|
||||
return (weight < weight2 ? weight : weight2);
|
||||
}
|
||||
if (mix_mode == MOD_WVG_MIX_MAX) {
|
||||
return (weight > weight2 ? weight : weight2);
|
||||
}
|
||||
|
||||
return weight2;
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ static int node_shader_gpu_ambient_occlusion(GPUMaterial *mat,
|
|||
|
||||
GPU_material_flag_set(mat, GPU_MATFLAG_DIFFUSE);
|
||||
|
||||
float inverted = node->custom2 ? 1.0f : 0.0f;
|
||||
float inverted = (node->custom2 & SHD_AO_INSIDE) ? 1.0f : 0.0f;
|
||||
float f_samples = divide_ceil_u(node->custom1, 4);
|
||||
|
||||
return GPU_stack_link(mat,
|
||||
|
|
|
@ -151,7 +151,7 @@ static PyObject *pygpu_state_depth_test_set(PyObject *UNUSED(self), PyObject *va
|
|||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_depth_test_get_doc,
|
||||
".. function:: blend_depth_test_get()\n"
|
||||
".. function:: depth_test_get()\n"
|
||||
"\n"
|
||||
" Current depth_test equation.\n"
|
||||
"\n");
|
||||
|
@ -179,7 +179,7 @@ static PyObject *pygpu_state_depth_mask_set(PyObject *UNUSED(self), PyObject *va
|
|||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_state_depth_mask_get_doc,
|
||||
".. function:: depth_mask_set_get()\n"
|
||||
".. function:: depth_mask_get()\n"
|
||||
"\n"
|
||||
" Writing status in the depth component.\n");
|
||||
static PyObject *pygpu_state_depth_mask_get(PyObject *UNUSED(self))
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#if PY_VERSION_HEX < 0x03090000
|
||||
# error "Python 3.9 or greater is required, you'll need to update your Python."
|
||||
#if PY_VERSION_HEX < 0x030a0000
|
||||
# error "Python 3.10 or greater is required, you'll need to update your Python."
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
|
@ -76,11 +76,7 @@ Py_hash_t mathutils_array_hash(const float *array, size_t array_len)
|
|||
x = 0x345678UL;
|
||||
i = 0;
|
||||
while (--len >= 0) {
|
||||
#if PY_VERSION_HEX >= 0x30a0000 /* Version: 3.10. */
|
||||
y = _Py_HashDouble(NULL, (double)(array[i++]));
|
||||
#else
|
||||
y = _Py_HashDouble((double)(array[i++]));
|
||||
#endif
|
||||
if (y == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
|
|
@ -1166,10 +1166,8 @@ static PyObject *C_BVHTree_FromObject(PyObject *UNUSED(cls), PyObject *args, PyO
|
|||
tree = BLI_bvhtree_new((int)tris_len, epsilon, PY_BVH_TREE_TYPE_DEFAULT, PY_BVH_AXIS_DEFAULT);
|
||||
if (tree) {
|
||||
orig_index = MEM_mallocN(sizeof(*orig_index) * (size_t)tris_len, __func__);
|
||||
CustomData *pdata = &mesh->pdata;
|
||||
orig_normal = CustomData_get_layer(pdata, CD_NORMAL); /* can be NULL */
|
||||
if (orig_normal) {
|
||||
orig_normal = MEM_dupallocN(orig_normal);
|
||||
if (!BKE_mesh_poly_normals_are_dirty(mesh)) {
|
||||
orig_normal = MEM_dupallocN(BKE_mesh_poly_normals_ensure(mesh));
|
||||
}
|
||||
|
||||
for (i = 0; i < tris_len; i++, lt++) {
|
||||
|
|
|
@ -468,7 +468,9 @@ static TriTessFace *mesh_calc_tri_tessface(Mesh *me, bool tangent, Mesh *me_eval
|
|||
looptri = MEM_mallocN(sizeof(*looptri) * tottri, __func__);
|
||||
triangles = MEM_callocN(sizeof(TriTessFace) * tottri, __func__);
|
||||
|
||||
const float(*precomputed_normals)[3] = CustomData_get_layer(&me->pdata, CD_NORMAL);
|
||||
const float(*precomputed_normals)[3] = BKE_mesh_poly_normals_are_dirty(me) ?
|
||||
NULL :
|
||||
BKE_mesh_poly_normals_ensure(me);
|
||||
const bool calculate_normal = precomputed_normals ? false : true;
|
||||
|
||||
if (precomputed_normals != NULL) {
|
||||
|
|
|
@ -467,7 +467,6 @@ static void do_multires_bake(MultiresBakeRender *bkr,
|
|||
MPoly *mpoly = dm->getPolyArray(dm);
|
||||
MLoop *mloop = dm->getLoopArray(dm);
|
||||
MLoopUV *mloopuv = dm->getLoopDataArray(dm, CD_MLOOPUV);
|
||||
const float *precomputed_normals = dm->getPolyDataArray(dm, CD_NORMAL);
|
||||
float *pvtangent = NULL;
|
||||
|
||||
ListBase threads;
|
||||
|
@ -482,6 +481,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
|
|||
memcpy(temp_mesh->mpoly, dm->getPolyArray(dm), temp_mesh->totpoly * sizeof(*temp_mesh->mpoly));
|
||||
memcpy(temp_mesh->mloop, dm->getLoopArray(dm), temp_mesh->totloop * sizeof(*temp_mesh->mloop));
|
||||
const float(*vert_normals)[3] = BKE_mesh_vertex_normals_ensure(temp_mesh);
|
||||
const float(*poly_normals)[3] = BKE_mesh_poly_normals_ensure(temp_mesh);
|
||||
|
||||
if (require_tangent) {
|
||||
if (CustomData_get_layer_index(&dm->loopData, CD_TANGENT) == -1) {
|
||||
|
@ -497,7 +497,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
|
|||
NULL,
|
||||
0,
|
||||
vert_normals,
|
||||
(const float(*)[3])CustomData_get_layer(&dm->polyData, CD_NORMAL),
|
||||
poly_normals,
|
||||
(const float(*)[3])dm->getLoopDataArray(dm, CD_NORMAL),
|
||||
(const float(*)[3])dm->getVertDataArray(dm, CD_ORCO), /* may be nullptr */
|
||||
/* result */
|
||||
|
@ -542,7 +542,7 @@ static void do_multires_bake(MultiresBakeRender *bkr,
|
|||
handle->data.mlooptri = mlooptri;
|
||||
handle->data.mloop = mloop;
|
||||
handle->data.pvtangent = pvtangent;
|
||||
handle->data.precomputed_normals = precomputed_normals; /* don't strictly need this */
|
||||
handle->data.precomputed_normals = (float *)poly_normals; /* don't strictly need this */
|
||||
handle->data.w = ibuf->x;
|
||||
handle->data.h = ibuf->y;
|
||||
handle->data.lores_dm = dm;
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 515e67c1932bc06f24cb50b621265c2a6e8a25a9
|
||||
Subproject commit 7fd2ed908b4f50140670caf6786e5ed245b79137
|
|
@ -16,6 +16,7 @@ This catches the following kinds of issues:
|
|||
- Unused keymaps (keymaps which are defined but not used anywhere).
|
||||
- Event values that don't make sense for the event type, e.g.
|
||||
An escape key could have the value "NORTH" instead of "PRESS".
|
||||
- Identical key-map items.
|
||||
|
||||
This works by taking the keymap data (before it's loaded into Blender),
|
||||
then comparing it with that same keymap after exporting and importing.
|
||||
|
@ -27,16 +28,40 @@ NOTE:
|
|||
"""
|
||||
|
||||
import types
|
||||
import typing
|
||||
from typing import (
|
||||
Any,
|
||||
Dict,
|
||||
Generator,
|
||||
List,
|
||||
Optional,
|
||||
Sequence,
|
||||
Tuple,
|
||||
)
|
||||
|
||||
KeyConfigData = List[Tuple[str, Tuple[Any], Dict[str, Any]]]
|
||||
|
||||
import os
|
||||
import contextlib
|
||||
|
||||
import bpy
|
||||
import bpy # type: ignore
|
||||
|
||||
# Useful for diffing the output to see what changed in context.
|
||||
# this writes keymaps into the current directory with `.orig.py` & `.rewrite.py` extensions.
|
||||
WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory.
|
||||
WRITE_OUTPUT_DIR = "/tmp/test" # "/tmp", defaults to the systems temp directory.
|
||||
|
||||
# For each preset, test all of these options.
|
||||
# The key is the preset name, containing a sequence of (attribute, value) pairs to test.
|
||||
#
|
||||
# NOTE(@campbellbarton): only add these for preferences which impact multiple keys as exposing all preferences
|
||||
# this way would create too many combinations making the tests take too long to complete.
|
||||
PRESET_PREFS = {
|
||||
"Blender": (
|
||||
(("select_mouse", 'LEFT'), ("use_alt_tool", False)),
|
||||
(("select_mouse", 'LEFT'), ("use_alt_tool", True)),
|
||||
(("select_mouse", 'RIGHT'), ("rmb_action", 'TWEAK')),
|
||||
(("select_mouse", 'RIGHT'), ("rmb_action", 'FALLBACK_TOOL')),
|
||||
),
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# Generic Utilities
|
||||
|
@ -45,7 +70,7 @@ WRITE_OUTPUT_DIR = None # "/tmp", defaults to the systems temp directory.
|
|||
def temp_fn_argument_extractor(
|
||||
mod: types.ModuleType,
|
||||
mod_attr: str,
|
||||
) -> typing.Iterator[typing.List[typing.Tuple[list, dict]]]:
|
||||
) -> Generator[List[Tuple[Tuple[Tuple[Any], ...], Dict[str, Dict[str, Any]]]], None, None]:
|
||||
"""
|
||||
Temporarily intercept a function, so it's arguments can be extracted.
|
||||
The context manager gives us a list where each item is a tuple of
|
||||
|
@ -54,7 +79,7 @@ def temp_fn_argument_extractor(
|
|||
args_collected = []
|
||||
real_fn = getattr(mod, mod_attr)
|
||||
|
||||
def wrap_fn(*args, **kw):
|
||||
def wrap_fn(*args: Tuple[Any], **kw: Dict[str, Any]) -> Any:
|
||||
args_collected.append((args, kw))
|
||||
return real_fn(*args, **kw)
|
||||
setattr(mod, mod_attr, wrap_fn)
|
||||
|
@ -66,10 +91,10 @@ def temp_fn_argument_extractor(
|
|||
|
||||
def round_float_32(f: float) -> float:
|
||||
from struct import pack, unpack
|
||||
return unpack("f", pack("f", f))[0]
|
||||
return unpack("f", pack("f", f))[0] # type: ignore
|
||||
|
||||
|
||||
def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.Optional[str]:
|
||||
def report_humanly_readable_difference(a: Any, b: Any) -> Optional[str]:
|
||||
"""
|
||||
Compare strings, return None whrn they match,
|
||||
otherwise a humanly readable difference message.
|
||||
|
@ -86,7 +111,7 @@ def report_humanly_readable_difference(a: typing.Any, b: typing.Any) -> typing.O
|
|||
# -----------------------------------------------------------------------------
|
||||
# Keymap Utilities.
|
||||
|
||||
def keyconfig_preset_scan() -> typing.List[str]:
|
||||
def keyconfig_preset_scan() -> List[str]:
|
||||
"""
|
||||
Return all bundled presets (keymaps), not user presets.
|
||||
"""
|
||||
|
@ -104,7 +129,7 @@ def keyconfig_preset_scan() -> typing.List[str]:
|
|||
]
|
||||
|
||||
|
||||
def keymap_item_property_clean(value: typing.Any) -> typing.Any:
|
||||
def keymap_item_property_clean(value: Any) -> Any:
|
||||
"""
|
||||
Recursive property sanitize.
|
||||
|
||||
|
@ -118,12 +143,13 @@ def keymap_item_property_clean(value: typing.Any) -> typing.Any:
|
|||
return sorted(
|
||||
# Convert to `dict` to de-duplicate.
|
||||
dict([(k, keymap_item_property_clean(v)) for k, v in value]).items(),
|
||||
key=lambda item: item[0],
|
||||
# Ignore type checking, these are strings which we know can be sorted.
|
||||
key=lambda item: item[0], # type: ignore
|
||||
)
|
||||
return value
|
||||
|
||||
|
||||
def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None:
|
||||
def keymap_data_clean(keyconfig_data: KeyConfigData, *, relaxed: bool) -> None:
|
||||
"""
|
||||
Order & sanitize keymap data so the result
|
||||
from the hand written Python script is comparable with data exported & imported.
|
||||
|
@ -153,22 +179,82 @@ def keymap_data_clean(keyconfig_data: typing.List, *, relaxed: bool) -> None:
|
|||
items[i] = item_op, item_event, None
|
||||
|
||||
|
||||
def keyconfig_activate_and_extract_data(filepath: str, *, relaxed: bool) -> typing.List:
|
||||
def keyconfig_config_as_filename_component(values: Sequence[Tuple[str, Any]]) -> str:
|
||||
"""
|
||||
Takes a configuration, eg:
|
||||
|
||||
[("select_mouse", 'LEFT'), ("rmb_action", 'TWEAK')]
|
||||
|
||||
And returns a filename compatible path:
|
||||
"""
|
||||
from urllib.parse import quote
|
||||
if not values:
|
||||
return ""
|
||||
|
||||
return "(" + quote(
|
||||
".".join([
|
||||
"-".join((str(key), str(val)))
|
||||
for key, val in values
|
||||
]),
|
||||
# Needed so forward slashes aren't included in the resulting name.
|
||||
safe="",
|
||||
) + ")"
|
||||
|
||||
|
||||
def keyconfig_activate_and_extract_data(
|
||||
filepath: str,
|
||||
*,
|
||||
relaxed: bool,
|
||||
config: Sequence[Tuple[str, Any]],
|
||||
) -> KeyConfigData:
|
||||
"""
|
||||
Activate the key-map by filepath,
|
||||
return the key-config data (cleaned for comparison).
|
||||
"""
|
||||
import bl_keymap_utils.io
|
||||
import bl_keymap_utils.io # type: ignore
|
||||
|
||||
if config:
|
||||
bpy.ops.preferences.keyconfig_activate(filepath=filepath)
|
||||
km_prefs = bpy.context.window_manager.keyconfigs.active.preferences
|
||||
for attr, value in config:
|
||||
setattr(km_prefs, attr, value)
|
||||
|
||||
with temp_fn_argument_extractor(bl_keymap_utils.io, "keyconfig_init_from_data") as args_collected:
|
||||
bpy.ops.preferences.keyconfig_activate(filepath=filepath)
|
||||
|
||||
# If called multiple times, something strange is happening.
|
||||
assert(len(args_collected) == 1)
|
||||
args, _kw = args_collected[0]
|
||||
keyconfig_data = args[1]
|
||||
# Ignore the type check as `temp_fn_argument_extractor` is a generic function
|
||||
# which doesn't contain type information of the function being wrapped.
|
||||
keyconfig_data: KeyConfigData = args[1] # type: ignore
|
||||
keymap_data_clean(keyconfig_data, relaxed=relaxed)
|
||||
return keyconfig_data
|
||||
|
||||
|
||||
def keyconfig_report_duplicates(keyconfig_data: KeyConfigData) -> str:
|
||||
"""
|
||||
Return true if any of the key-maps have duplicate items.
|
||||
|
||||
Duplicate items are reported so they can be resolved.
|
||||
"""
|
||||
error_text = []
|
||||
for km_idname, km_args, km_items_data in keyconfig_data:
|
||||
items = tuple(km_items_data["items"])
|
||||
unique: Dict[str, List[int]] = {}
|
||||
for i, (item_op, item_event, item_prop) in enumerate(items):
|
||||
# Ensure stable order as `repr` will use order of definition.
|
||||
item_event = {key: item_event[key] for key in sorted(item_event.keys())}
|
||||
if item_prop is not None:
|
||||
item_prop = {key: item_prop[key] for key in sorted(item_prop.keys())}
|
||||
item_repr = repr((item_op, item_event, item_prop))
|
||||
unique.setdefault(item_repr, []).append(i)
|
||||
for key, value in unique.items():
|
||||
if len(value) > 1:
|
||||
error_text.append("\"%s\" %r indices %r for item %r" % (km_idname, km_args, value, key))
|
||||
return "\n".join(error_text)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
import os
|
||||
import sys
|
||||
|
@ -186,37 +272,61 @@ def main() -> None:
|
|||
for filepath in presets:
|
||||
name_only = os.path.splitext(os.path.basename(filepath))[0]
|
||||
|
||||
print("KeyMap Validate:", name_only, end=" ... ")
|
||||
for config in PRESET_PREFS.get(name_only, ((),)):
|
||||
name_only_with_config = name_only + keyconfig_config_as_filename_component(config)
|
||||
print("KeyMap Validate:", name_only_with_config, end=" ... ")
|
||||
data_orig = keyconfig_activate_and_extract_data(
|
||||
filepath,
|
||||
relaxed=relaxed,
|
||||
config=config,
|
||||
)
|
||||
|
||||
data_orig = keyconfig_activate_and_extract_data(filepath, relaxed=relaxed)
|
||||
with tempfile.TemporaryDirectory() as dir_temp:
|
||||
filepath_temp = os.path.join(
|
||||
dir_temp,
|
||||
name_only_with_config + ".test" + ".py",
|
||||
)
|
||||
|
||||
with tempfile.TemporaryDirectory() as dir_temp:
|
||||
filepath_temp = os.path.join(dir_temp, name_only + ".test.py")
|
||||
bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True)
|
||||
data_reimport = keyconfig_activate_and_extract_data(filepath_temp, relaxed=relaxed)
|
||||
bpy.ops.preferences.keyconfig_export(filepath=filepath_temp, all=True)
|
||||
data_reimport = keyconfig_activate_and_extract_data(
|
||||
filepath_temp,
|
||||
relaxed=relaxed,
|
||||
# No configuration supported when loading exported key-maps.
|
||||
config=(),
|
||||
)
|
||||
|
||||
# Comparing a pretty printed string tends to give more useful
|
||||
# text output compared to the data-structure. Both will work.
|
||||
if (cmp_message := report_humanly_readable_difference(
|
||||
pprint.pformat(data_orig, indent=0, width=120),
|
||||
pprint.pformat(data_reimport, indent=0, width=120),
|
||||
)):
|
||||
print("FAILED!")
|
||||
sys.stdout.write((
|
||||
"Keymap %s has inconsistency on re-importing:\n"
|
||||
" %r"
|
||||
) % (filepath, cmp_message))
|
||||
has_error = True
|
||||
else:
|
||||
print("OK!")
|
||||
# Comparing a pretty printed string tends to give more useful
|
||||
# text output compared to the data-structure. Both will work.
|
||||
if (cmp_message := report_humanly_readable_difference(
|
||||
pprint.pformat(data_orig, indent=0, width=120),
|
||||
pprint.pformat(data_reimport, indent=0, width=120),
|
||||
)):
|
||||
error_text_consistency = "Keymap %s has inconsistency on re-importing." % cmp_message
|
||||
else:
|
||||
error_text_consistency = ""
|
||||
|
||||
if WRITE_OUTPUT_DIR:
|
||||
name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only)
|
||||
print("Writing data to:", name_only_temp + ".*.py")
|
||||
with open(name_only_temp + ".orig.py", 'w') as fh:
|
||||
fh.write(pprint.pformat(data_orig, indent=0, width=120))
|
||||
with open(name_only_temp + ".rewrite.py", 'w') as fh:
|
||||
fh.write(pprint.pformat(data_reimport, indent=0, width=120))
|
||||
# Perform an additional sanity check:
|
||||
# That there are no identical key-map items.
|
||||
error_text_duplicates = keyconfig_report_duplicates(data_orig)
|
||||
|
||||
if error_text_consistency or error_text_duplicates:
|
||||
print("FAILED!")
|
||||
print("%r has errors!" % filepath)
|
||||
if error_text_consistency:
|
||||
print(error_text_consistency)
|
||||
if error_text_duplicates:
|
||||
print(error_text_duplicates)
|
||||
else:
|
||||
print("OK!")
|
||||
|
||||
if WRITE_OUTPUT_DIR:
|
||||
os.makedirs(WRITE_OUTPUT_DIR, exist_ok=True)
|
||||
name_only_temp = os.path.join(WRITE_OUTPUT_DIR, name_only_with_config)
|
||||
print("Writing data to:", name_only_temp + ".*.py")
|
||||
with open(name_only_temp + ".orig.py", 'w') as fh:
|
||||
fh.write(pprint.pformat(data_orig, indent=0, width=120))
|
||||
with open(name_only_temp + ".rewrite.py", 'w') as fh:
|
||||
fh.write(pprint.pformat(data_reimport, indent=0, width=120))
|
||||
if has_error:
|
||||
sys.exit(1)
|
||||
|
||||
|
|
Loading…
Reference in New Issue