Advance WebGPU volume and bounded workflows
This commit is contained in:
@@ -1543,7 +1543,7 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
|
||||
type == "moveObjectToCollection" || type == "renameId" || type == "joinObjects" ||
|
||||
type == "separateMeshFaces" || type == "applyObjectTransform" || type == "setObjectOrigin" ||
|
||||
type == "setCurveControlPoints" || type == "setCurveHandle" || type == "setCurveTopology" || type == "setCurveSplines" || type == "setSurfaceTopology" || type == "setFontBody" ||
|
||||
type == "setFontProperties" || type == "setFontAdvanced" || type == "setFontLinks" || type == "deleteNonMeshData" ||
|
||||
type == "setFontProperties" || type == "setFontAdvanced" || type == "setFontLinks" || type == "setVolumeProperties" || type == "deleteNonMeshData" ||
|
||||
type == "setMetaballElements" || type == "createGreasePencilLayer" ||
|
||||
type == "removeGreasePencilLayer" || type == "moveGreasePencilLayer" ||
|
||||
type == "insertGreasePencilFrame" || type == "removeGreasePencilFrame" ||
|
||||
@@ -1934,6 +1934,20 @@ EMSCRIPTEN_KEEPALIVE int web_engine_apply_command(const int handle,
|
||||
applied = main_error.empty() && web_engine_blend_main_set_font_links(
|
||||
engine->authoritative_main, command.value("dataId", "").c_str(), font_ids, main_error);
|
||||
}
|
||||
else if (type == "setVolumeProperties") {
|
||||
WebVolumePropertiesEdit properties;
|
||||
properties.source_path = command.value("sourcePath", "");
|
||||
properties.display_density = command.value("displayDensity", 1.0f);
|
||||
properties.interpolation = command.value("interpolation", "LINEAR");
|
||||
properties.step_size = command.value("stepSize", 0.0f);
|
||||
properties.velocity_grid = command.value("velocityGrid", "");
|
||||
properties.velocity_scale = command.value("velocityScale", 1.0f);
|
||||
applied = web_engine_blend_main_set_volume_properties(
|
||||
engine->authoritative_main,
|
||||
command.value("dataId", "").c_str(),
|
||||
properties,
|
||||
main_error);
|
||||
}
|
||||
else if (type == "setMetaballElements") {
|
||||
std::vector<WebMetaballElementEdit> elements;
|
||||
const json source_elements = command.value("elements", json::array());
|
||||
|
||||
@@ -735,6 +735,28 @@ std::vector<float> node_socket_default_values(const ParsedBlend &blend, const El
|
||||
return read_float_array(*blend.sdna, *default_value, "value", 4);
|
||||
}
|
||||
|
||||
std::optional<ElementRef> node_socket_by_identifier(const ParsedBlend &blend,
|
||||
const ElementRef &node,
|
||||
const std::string &identifier)
|
||||
{
|
||||
for (const ElementRef &socket : linked_list_elements(blend, node, "inputs")) {
|
||||
std::string socket_identifier = read_string(*blend.sdna, socket, "identifier");
|
||||
if (socket_identifier.empty()) socket_identifier = read_string(*blend.sdna, socket, "name");
|
||||
if (socket_identifier == identifier) return socket;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ElementRef> node_socket_default_element(const ParsedBlend &blend,
|
||||
const ElementRef &node,
|
||||
const std::string &identifier)
|
||||
{
|
||||
const std::optional<ElementRef> socket = node_socket_by_identifier(blend, node, identifier);
|
||||
if (!socket) return std::nullopt;
|
||||
const std::optional<uint64_t> pointer = read_pointer(*blend.sdna, *socket, "default_value");
|
||||
return pointer && *pointer != 0 ? element_for_pointer(blend, *pointer) : std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<ElementRef> raw_array_element(const ParsedBlend &blend,
|
||||
uint64_t pointer,
|
||||
const std::string &type_name,
|
||||
@@ -813,6 +835,36 @@ std::optional<json> compositor_graph_from_scene(const ParsedBlend &blend,
|
||||
node_ir["properties"] = {{"color", {values[0], values[1], values[2], values[3]}}};
|
||||
}
|
||||
}
|
||||
else if (blender_type == "CompositorNodeExposure") {
|
||||
const std::optional<ElementRef> exposure = node_socket_default_element(
|
||||
blend, node, "Exposure");
|
||||
const std::optional<float> value = exposure ? read_float(*blend.sdna, *exposure, "value") :
|
||||
std::nullopt;
|
||||
if (value && std::isfinite(*value) && *value >= -20.0f && *value <= 20.0f) {
|
||||
node_ir["type"] = "EXPOSURE";
|
||||
node_ir.erase("blenderType");
|
||||
node_ir["properties"] = {{"exposure", *value}};
|
||||
}
|
||||
}
|
||||
else if (blender_type == "CompositorNodeInvert") {
|
||||
const std::optional<ElementRef> factor = node_socket_default_element(blend, node, "Fac");
|
||||
const std::optional<ElementRef> invert_color = node_socket_default_element(
|
||||
blend, node, "Invert Color");
|
||||
const std::optional<ElementRef> invert_alpha = node_socket_default_element(
|
||||
blend, node, "Invert Alpha");
|
||||
const std::optional<float> factor_value = factor ? read_float(*blend.sdna, *factor, "value") :
|
||||
std::nullopt;
|
||||
const std::optional<int64_t> color_value = invert_color ?
|
||||
read_integer(*blend.sdna, *invert_color, "value") : std::nullopt;
|
||||
const std::optional<int64_t> alpha_value = invert_alpha ?
|
||||
read_integer(*blend.sdna, *invert_alpha, "value") : std::nullopt;
|
||||
if (factor_value && std::abs(*factor_value - 1.0f) <= 1e-6f && color_value == 1 &&
|
||||
alpha_value == 0)
|
||||
{
|
||||
node_ir["type"] = "INVERT";
|
||||
node_ir.erase("blenderType");
|
||||
}
|
||||
}
|
||||
nodes.push_back(std::move(node_ir));
|
||||
}
|
||||
if (output_node_id.empty()) output_node_id = fallback_output_node_id;
|
||||
@@ -835,6 +887,18 @@ std::optional<json> compositor_graph_from_scene(const ParsedBlend &blend,
|
||||
std::string to_socket_name = read_string(*blend.sdna, *to_socket, "identifier");
|
||||
if (from_socket_name.empty()) from_socket_name = read_string(*blend.sdna, *from_socket, "name");
|
||||
if (to_socket_name.empty()) to_socket_name = read_string(*blend.sdna, *to_socket, "name");
|
||||
const std::optional<ElementRef> to_node = element_for_pointer(blend, *to_node_pointer);
|
||||
const std::string to_blender_type = to_node ? read_string(*blend.sdna, *to_node, "idname") :
|
||||
std::string();
|
||||
if (to_blender_type == "CompositorNodeInvert" && to_socket_name == "Color") {
|
||||
to_socket_name = "Image";
|
||||
}
|
||||
else if ((to_blender_type == "NodeGroupOutput" ||
|
||||
to_blender_type == "CompositorNodeComposite") &&
|
||||
read_string(*blend.sdna, *to_socket, "name") == "Image")
|
||||
{
|
||||
to_socket_name = "Image";
|
||||
}
|
||||
if (from_socket_name.empty() || to_socket_name.empty() || from_socket_name.size() > 256 ||
|
||||
to_socket_name.size() > 256)
|
||||
{
|
||||
@@ -2414,6 +2478,22 @@ json non_mesh_data_from_record(const ParsedBlend &blend,
|
||||
else if (record.type_name == "Volume") {
|
||||
const std::string path = read_string(*blend.sdna, element, "filepath");
|
||||
data["resourceKind"] = "OPENVDB";
|
||||
json properties = {
|
||||
{"displayDensity", 1.0f},
|
||||
{"interpolation", "LINEAR"},
|
||||
{"stepSize", 0.0f},
|
||||
{"velocityGrid", read_string(*blend.sdna, element, "velocity_grid")},
|
||||
{"velocityScale", read_float(*blend.sdna, element, "velocity_scale").value_or(1.0f)},
|
||||
};
|
||||
if (const std::optional<ElementRef> display = embedded_element(*blend.sdna, element, "display")) {
|
||||
properties["displayDensity"] = read_float(*blend.sdna, *display, "density").value_or(1.0f);
|
||||
const int64_t interpolation = read_integer(*blend.sdna, *display, "interpolation_method").value_or(0);
|
||||
properties["interpolation"] = interpolation == 2 ? "NEAREST" : "LINEAR";
|
||||
}
|
||||
if (const std::optional<ElementRef> render = embedded_element(*blend.sdna, element, "render")) {
|
||||
properties["stepSize"] = read_float(*blend.sdna, *render, "step_size").value_or(0.0f);
|
||||
}
|
||||
data["volumeProperties"] = std::move(properties);
|
||||
const std::optional<uint64_t> packed_file = read_pointer(*blend.sdna, element, "packedfile");
|
||||
const std::vector<uint8_t> packed_bytes = packed_file ?
|
||||
packed_file_bytes(blend, *packed_file) :
|
||||
@@ -3079,7 +3159,9 @@ json scene_ir_from_blend(const ParsedBlend &blend,
|
||||
mesh_bind_matrices[data_id->second] = matrix;
|
||||
json groups = json::array();
|
||||
int index = 0;
|
||||
for (const ElementRef &group : linked_list_elements(blend, object, "defbase")) {
|
||||
std::vector<ElementRef> source_groups = linked_list_elements(blend, *data, "vertex_group_names");
|
||||
if (source_groups.empty()) source_groups = linked_list_elements(blend, object, "defbase");
|
||||
for (const ElementRef &group : source_groups) {
|
||||
groups.push_back({{"name", read_string(*blend.sdna, group, "name")}, {"index", index++}});
|
||||
}
|
||||
if (!groups.empty()) vertex_groups_by_mesh_id[data_id->second] = std::move(groups);
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_deform.h"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_volume.hh"
|
||||
#include "BKE_fcurve.hh"
|
||||
#include "BKE_image.hh"
|
||||
|
||||
@@ -69,6 +70,7 @@
|
||||
#include "DNA_world_types.h"
|
||||
#include "DNA_userdef_enums.h"
|
||||
#include "DNA_vfont_types.h"
|
||||
#include "DNA_volume_types.h"
|
||||
|
||||
#include "BLI_listbase_iterator.hh"
|
||||
#include "BLI_listbase.h"
|
||||
@@ -187,6 +189,18 @@ MetaBall *find_metaball(Main *main, const char *data_id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Volume *find_volume(Main *main, const char *data_id)
|
||||
{
|
||||
if (main == nullptr || data_id == nullptr || strncmp(data_id, "volume:", 7) != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
const std::string name(data_id + 7);
|
||||
for (Volume &volume : main->volumes) {
|
||||
if (id_name(volume.id) == name) return &volume;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Light *find_light(Main *main, const char *data_id)
|
||||
{
|
||||
if (main == nullptr || data_id == nullptr || strncmp(data_id, "light:", 6) != 0) return nullptr;
|
||||
@@ -3471,6 +3485,54 @@ bool web_engine_blend_main_set_font_links(WebBlendMainState *state,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool web_engine_blend_main_set_volume_properties(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const WebVolumePropertiesEdit &properties,
|
||||
std::string &error)
|
||||
{
|
||||
Volume *volume = find_volume(state != nullptr ? state->main : nullptr, data_id);
|
||||
if (volume == nullptr ||
|
||||
!ensure_single_user_data(state != nullptr ? state->main : nullptr, &volume->id, error))
|
||||
{
|
||||
if (error.empty()) error = "NON_MESH_DATA_UNSUPPORTED: Volume data-block was not found";
|
||||
return false;
|
||||
}
|
||||
const std::string &path = properties.source_path;
|
||||
const bool project_relative = path.size() >= 7 && path.rfind("//", 0) == 0 &&
|
||||
path.compare(path.size() - 4, 4, ".vdb") == 0 &&
|
||||
path.find('\\') == std::string::npos &&
|
||||
path.find("/../") == std::string::npos &&
|
||||
path.compare(path.size() - 3, 3, "/..") != 0 &&
|
||||
path.size() < sizeof(volume->filepath);
|
||||
if (!project_relative || !std::isfinite(properties.display_density) ||
|
||||
properties.display_density < 0.0f || properties.display_density > 1000000.0f ||
|
||||
(properties.interpolation != "NEAREST" && properties.interpolation != "LINEAR") ||
|
||||
!std::isfinite(properties.step_size) || properties.step_size < 0.0f ||
|
||||
properties.step_size > 1000000.0f || properties.velocity_grid.size() >= sizeof(volume->velocity_grid) ||
|
||||
!std::isfinite(properties.velocity_scale) || properties.velocity_scale < -1000000.0f ||
|
||||
properties.velocity_scale > 1000000.0f)
|
||||
{
|
||||
error = "NON_MESH_PROPERTY_INVALID: Volume source and properties are outside the bounded project-relative range";
|
||||
return false;
|
||||
}
|
||||
BKE_volume_unload(volume);
|
||||
BLI_strncpy(volume->filepath, path.c_str(), sizeof(volume->filepath));
|
||||
volume->display.density = properties.display_density;
|
||||
volume->display.interpolation_method = properties.interpolation == "NEAREST" ?
|
||||
VOLUME_DISPLAY_INTERP_CLOSEST :
|
||||
VOLUME_DISPLAY_INTERP_LINEAR;
|
||||
volume->render.step_size = properties.step_size;
|
||||
BLI_strncpy(volume->velocity_grid,
|
||||
properties.velocity_grid.c_str(),
|
||||
sizeof(volume->velocity_grid));
|
||||
volume->velocity_scale = properties.velocity_scale;
|
||||
volume->id.recalc |= ID_RECALC_GEOMETRY;
|
||||
for (Object &object : state->main->objects) {
|
||||
if (object.data == &volume->id) object.id.recalc |= ID_RECALC_GEOMETRY;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool web_engine_blend_main_set_metaball_elements(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebMetaballElementEdit> &elements,
|
||||
|
||||
@@ -67,6 +67,15 @@ struct WebFontTextBoxEdit {
|
||||
float height = 0.0f;
|
||||
};
|
||||
|
||||
struct WebVolumePropertiesEdit {
|
||||
std::string source_path;
|
||||
float display_density = 1.0f;
|
||||
std::string interpolation;
|
||||
float step_size = 0.0f;
|
||||
std::string velocity_grid;
|
||||
float velocity_scale = 1.0f;
|
||||
};
|
||||
|
||||
struct WebGreasePencilPointEdit {
|
||||
std::array<float, 3> position = {0.0f, 0.0f, 0.0f};
|
||||
float radius = 0.01f;
|
||||
@@ -341,6 +350,10 @@ bool web_engine_blend_main_set_font_links(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::array<std::string, 4> &font_ids,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_volume_properties(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const WebVolumePropertiesEdit &properties,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_metaball_elements(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebMetaballElementEdit> &elements,
|
||||
|
||||
Reference in New Issue
Block a user