Merge branch 'master' into xr-dev

This commit is contained in:
Peter Kim 2022-02-22 17:47:20 +09:00
commit b33aaf5a2c
73 changed files with 1530 additions and 875 deletions

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")),
]},
)

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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'

View File

@ -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

View 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

View File

@ -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));
}
}

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;

View File

@ -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. */

View File

@ -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);

View File

@ -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);

View File

@ -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)
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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++;

View File

@ -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)

View File

@ -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) {

View File

@ -9,6 +9,7 @@ set(INC
../depsgraph
../makesdna
../../../intern/atomic
../../../intern/clog
../../../intern/eigen
../../../intern/guardedalloc
../../../extern/rangetree

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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);
}
}
}

View File

@ -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");

View File

@ -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);
}

View File

@ -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
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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);

View File

@ -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;
/**

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -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. */

View File

@ -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)

View File

@ -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");

View File

@ -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");
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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]);
}

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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,

View File

@ -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))

View File

@ -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

View File

@ -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;
}

View File

@ -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++) {

View File

@ -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) {

View File

@ -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

View File

@ -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)