Add Chromium-only Blender WebEngine parity work
This commit is contained in:
142
blender-5.2.0/source/blender/web_engine/CMakeLists.txt
Normal file
142
blender-5.2.0/source/blender/web_engine/CMakeLists.txt
Normal file
@@ -0,0 +1,142 @@
|
||||
# Web-only Blender data engine target. This is intentionally separate from the
|
||||
# desktop creator/window-manager entry point and only exposes the C ABI.
|
||||
|
||||
set(SRC
|
||||
web_engine_blend_reader.cpp
|
||||
web_engine_depsgraph.cpp
|
||||
web_engine_main_state.cpp
|
||||
web_engine_api.cpp
|
||||
web_engine_decimate.cpp
|
||||
web_engine_headless_fallback.cpp
|
||||
web_gpu_fallback.cpp
|
||||
web_rna_fallback.cpp
|
||||
)
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
${CMAKE_SOURCE_DIR}/source/blender/blenkernel
|
||||
${CMAKE_SOURCE_DIR}/source/blender/blenfont
|
||||
${CMAKE_SOURCE_DIR}/source/blender/animrig
|
||||
${CMAKE_SOURCE_DIR}/source/blender/blenloader
|
||||
${CMAKE_SOURCE_DIR}/source/blender/depsgraph
|
||||
${CMAKE_SOURCE_DIR}/source/blender/gpu
|
||||
${CMAKE_SOURCE_DIR}/source/blender/imbuf
|
||||
${CMAKE_SOURCE_DIR}/source/blender/makesrna
|
||||
${CMAKE_SOURCE_DIR}/source/blender/makesrna/intern
|
||||
${CMAKE_SOURCE_DIR}/source/blender/nodes
|
||||
${CMAKE_SOURCE_DIR}/source/blender/sequencer
|
||||
${CMAKE_SOURCE_DIR}/source/blender/render
|
||||
${CMAKE_SOURCE_DIR}/source/blender/windowmanager
|
||||
${CMAKE_SOURCE_DIR}/source/blender/editors/include
|
||||
${CMAKE_SOURCE_DIR}/source/blender/editors/asset
|
||||
${CMAKE_SOURCE_DIR}/source/blender/functions
|
||||
${CMAKE_SOURCE_DIR}/source/blender/makesdna/intern
|
||||
${CMAKE_SOURCE_DIR}/intern/clog
|
||||
${CMAKE_SOURCE_DIR}/extern/json/include
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::blenloader_core
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::dependencies::zstd
|
||||
)
|
||||
|
||||
blender_add_lib(blender_web_engine_core "${SRC}" "${INC}" "" "${LIB}")
|
||||
add_library(bf::web_engine ALIAS blender_web_engine_core)
|
||||
|
||||
option(WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH
|
||||
"Enable verbose native Depsgraph diagnostics" OFF)
|
||||
mark_as_advanced(WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH)
|
||||
if(WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH)
|
||||
target_compile_definitions(blender_web_engine_core PRIVATE WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH)
|
||||
endif()
|
||||
|
||||
add_executable(web_engine
|
||||
"${CMAKE_SOURCE_DIR}/../web/engine/web_engine_smoke.cpp"
|
||||
)
|
||||
target_include_directories(web_engine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}")
|
||||
|
||||
# Link archive files instead of CMake targets here. Blender's static targets
|
||||
# intentionally expose their implementation dependencies to the desktop
|
||||
# executable; following those edges would pull the entire editor and image
|
||||
# stack into this data-only WebAssembly module.
|
||||
set(WEB_ENGINE_ARCHIVE_TARGETS
|
||||
blender_web_engine_core
|
||||
bf_bmesh
|
||||
bf_blenkernel
|
||||
bf_blenloader_core
|
||||
bf_blenloader
|
||||
bf_depsgraph
|
||||
bf_rna
|
||||
bf_blentranslation
|
||||
bf_animrig
|
||||
bf_asset_system
|
||||
bf_imbuf
|
||||
bf_imbuf_movie
|
||||
bf_functions
|
||||
bf_geometry
|
||||
bf_nodes
|
||||
bf_modifiers
|
||||
bf_blenfont
|
||||
bf_editor_datafiles
|
||||
bf_imbuf_opencolorio
|
||||
bf_imbuf_openexr
|
||||
bf_simulation
|
||||
bf_ikplugin
|
||||
bf_intern_iksolver
|
||||
bf_intern_eigen
|
||||
bf_intern_memutil
|
||||
bf_intern_ghost
|
||||
extern_xxhash
|
||||
extern_wcwidth
|
||||
bf_intern_libmv
|
||||
bf_intern_opensubdiv
|
||||
bf_blenlib
|
||||
bf_dna
|
||||
bf_intern_clog
|
||||
bf_intern_guardedalloc
|
||||
extern_rangetree
|
||||
zlibstatic
|
||||
libzstd_static
|
||||
osd_static_cpu
|
||||
)
|
||||
set(WEB_ENGINE_ARCHIVES "")
|
||||
foreach(archive_target IN LISTS WEB_ENGINE_ARCHIVE_TARGETS)
|
||||
list(APPEND WEB_ENGINE_ARCHIVES "$<TARGET_FILE:${archive_target}>")
|
||||
endforeach()
|
||||
if(WITH_GMP)
|
||||
list(APPEND WEB_ENGINE_ARCHIVES ${GMP_LIBRARIES})
|
||||
endif()
|
||||
add_dependencies(web_engine ${WEB_ENGINE_ARCHIVE_TARGETS})
|
||||
# The libraries contain a few intentional cycles. Repeating the small archive
|
||||
# group is equivalent to Blender's LINK_INTERFACE_MULTIPLICITY policy without
|
||||
# restoring the desktop-only dependency graph.
|
||||
target_link_libraries(web_engine PRIVATE
|
||||
${WEB_ENGINE_ARCHIVES}
|
||||
${WEB_ENGINE_ARCHIVES}
|
||||
${WEB_ENGINE_ARCHIVES}
|
||||
)
|
||||
target_link_options(web_engine PRIVATE
|
||||
"-Wl,--error-limit=0"
|
||||
"-sUSE_FREETYPE=1"
|
||||
"-sENVIRONMENT=web,node,worker"
|
||||
"-sALLOW_MEMORY_GROWTH=1"
|
||||
"-sINITIAL_MEMORY=67108864"
|
||||
"-sMAXIMUM_MEMORY=2147483648"
|
||||
"-sSTACK_SIZE=8388608"
|
||||
"-sEXPORTED_FUNCTIONS=['_malloc','_free','_web_engine_create','_web_engine_destroy','_web_engine_get_memory_stats','_web_engine_get_live_handles','_web_engine_get_allocated_bytes','_web_engine_open_blend','_web_engine_apply_command','_web_engine_undo','_web_engine_redo','_web_engine_get_scene_snapshot','_web_engine_get_scene_metadata','_web_engine_get_scene_geometry','_web_engine_get_scene_delta','_web_engine_get_packed_asset','_web_engine_evaluate_depsgraph','_web_engine_save_blend','_web_engine_free_buffer','_web_engine_last_error_code','_web_engine_last_error_message','_web_engine_decimate_apply']"
|
||||
"-sEXPORTED_RUNTIME_METHODS=['ccall','cwrap','UTF8ToString']"
|
||||
)
|
||||
if(WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH)
|
||||
target_link_options(web_engine PRIVATE
|
||||
"-sASSERTIONS=2"
|
||||
"-sSTACK_OVERFLOW_CHECK=2"
|
||||
"--profiling-funcs"
|
||||
)
|
||||
endif()
|
||||
set_target_properties(web_engine PROPERTIES OUTPUT_NAME "web_engine")
|
||||
2373
blender-5.2.0/source/blender/web_engine/web_engine_api.cpp
Normal file
2373
blender-5.2.0/source/blender/web_engine/web_engine_api.cpp
Normal file
File diff suppressed because it is too large
Load Diff
50
blender-5.2.0/source/blender/web_engine/web_engine_api.h
Normal file
50
blender-5.2.0/source/blender/web_engine/web_engine_api.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct WebEngineMemoryStats {
|
||||
uint32_t live_handles;
|
||||
uint32_t allocated_bytes;
|
||||
} WebEngineMemoryStats;
|
||||
|
||||
typedef enum WebEngineResultCode {
|
||||
WEB_ENGINE_OK = 0,
|
||||
WEB_ENGINE_INVALID_HANDLE = -1,
|
||||
WEB_ENGINE_INVALID_ARGUMENT = -2,
|
||||
WEB_ENGINE_NOT_IMPLEMENTED = -3,
|
||||
WEB_ENGINE_BLEND_READ_FAILED = -4,
|
||||
WEB_ENGINE_BLEND_WRITE_FAILED = -5,
|
||||
WEB_ENGINE_HISTORY_EMPTY = -6,
|
||||
} WebEngineResultCode;
|
||||
|
||||
int web_engine_create(void);
|
||||
void web_engine_destroy(int handle);
|
||||
WebEngineMemoryStats web_engine_get_memory_stats(void);
|
||||
uint32_t web_engine_get_live_handles(void);
|
||||
uint32_t web_engine_get_allocated_bytes(void);
|
||||
int web_engine_open_blend(int handle, const uint8_t *data, uint32_t length);
|
||||
int web_engine_apply_command(int handle, const uint8_t *data, uint32_t length);
|
||||
int web_engine_undo(int handle);
|
||||
int web_engine_redo(int handle);
|
||||
int web_engine_get_scene_snapshot(int handle, const uint8_t **data, uint32_t *length);
|
||||
int web_engine_get_scene_metadata(int handle, const uint8_t **data, uint32_t *length);
|
||||
int web_engine_get_scene_geometry(int handle, const uint8_t **data, uint32_t *length);
|
||||
int web_engine_get_scene_delta(int handle, const uint8_t **data, uint32_t *length);
|
||||
int web_engine_get_packed_asset(int handle,
|
||||
const uint8_t *asset_id,
|
||||
uint32_t asset_id_length,
|
||||
const uint8_t **data,
|
||||
uint32_t *length);
|
||||
int web_engine_evaluate_depsgraph(int handle, const uint8_t **data, uint32_t *length);
|
||||
int web_engine_save_blend(int handle, const uint8_t **data, uint32_t *length);
|
||||
void web_engine_free_buffer(const uint8_t *data);
|
||||
int web_engine_last_error_code(void);
|
||||
const char *web_engine_last_error_message(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
3156
blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp
Normal file
3156
blender-5.2.0/source/blender/web_engine/web_engine_blend_reader.cpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct WebBlendReadResult {
|
||||
bool ok = false;
|
||||
std::string error;
|
||||
std::string scene_ir;
|
||||
std::unordered_map<std::string, std::vector<uint8_t>> packed_assets;
|
||||
uint32_t source_bytes = 0;
|
||||
};
|
||||
|
||||
WebBlendReadResult web_engine_read_blend_scene_ir(const uint8_t *data,
|
||||
uint32_t length,
|
||||
uint64_t revision);
|
||||
338
blender-5.2.0/source/blender/web_engine/web_engine_decimate.cpp
Normal file
338
blender-5.2.0/source/blender/web_engine/web_engine_decimate.cpp
Normal file
@@ -0,0 +1,338 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "web_engine_decimate.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "BLI_mempool.h"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "tools/bmesh_decimate.hh"
|
||||
|
||||
#ifdef __EMSCRIPTEN__
|
||||
# include <emscripten/emscripten.h>
|
||||
#else
|
||||
# define EMSCRIPTEN_KEEPALIVE
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
using blender::BMesh;
|
||||
using blender::BMFace;
|
||||
using blender::BMLoop;
|
||||
using blender::BMVert;
|
||||
|
||||
struct OutputMesh {
|
||||
std::vector<float> positions;
|
||||
std::vector<uint32_t> indices;
|
||||
std::vector<float> uvs;
|
||||
std::vector<float> colors;
|
||||
std::vector<uint32_t> material_indices;
|
||||
uint32_t face_count = 0;
|
||||
};
|
||||
|
||||
blender::BMO_Delimit delimit_flags(const uint32_t value)
|
||||
{
|
||||
blender::BMO_Delimit flags = blender::BMO_Delimit(0);
|
||||
if (value & (1u << 0)) flags |= blender::BMO_DELIM_NORMAL;
|
||||
if (value & (1u << 1)) flags |= blender::BMO_DELIM_MATERIAL;
|
||||
if (value & (1u << 2)) flags |= blender::BMO_DELIM_SEAM;
|
||||
if (value & (1u << 3)) flags |= blender::BMO_DELIM_SHARP;
|
||||
if (value & (1u << 4)) flags |= blender::BMO_DELIM_UV;
|
||||
return flags;
|
||||
}
|
||||
|
||||
int build_and_decimate(const float *positions,
|
||||
const uint32_t vertex_count,
|
||||
const uint32_t *indices,
|
||||
const uint32_t triangle_count,
|
||||
const uint32_t mode,
|
||||
const float ratio,
|
||||
const uint32_t iterations,
|
||||
const float angle_limit,
|
||||
const uint32_t delimit,
|
||||
const uint32_t use_dissolve_boundaries,
|
||||
const uint32_t triangulate,
|
||||
const int32_t symmetry_axis,
|
||||
const float symmetry_epsilon,
|
||||
const float *vertex_weights,
|
||||
const float vertex_group_factor,
|
||||
const uint32_t vertex_group_invert,
|
||||
const float *input_uvs,
|
||||
const float *input_colors,
|
||||
const uint32_t *input_material_indices,
|
||||
OutputMesh &output)
|
||||
{
|
||||
if (positions == nullptr || indices == nullptr || vertex_count == 0 || triangle_count == 0) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
if (mode > WEB_ENGINE_DECIMATE_DISSOLVE_PLANAR) return WEB_ENGINE_DECIMATE_NOT_IMPLEMENTED;
|
||||
if (mode == WEB_ENGINE_DECIMATE_COLLAPSE && (ratio <= 0.0f || ratio > 1.0f)) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
if (mode == WEB_ENGINE_DECIMATE_UNSUBDIV && iterations == 0) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
if (mode == WEB_ENGINE_DECIMATE_DISSOLVE_PLANAR && (angle_limit < 0.0f || angle_limit > 3.14159265358979323846f)) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
if (symmetry_axis < -1 || symmetry_axis > 2 || symmetry_epsilon <= 0.0f || vertex_group_factor < 0.0f ||
|
||||
vertex_group_factor > 1.0f) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
blender::BMeshCreateParams create_params{};
|
||||
BMesh *bm = blender::BM_mesh_create(&blender::bm_mesh_allocsize_default, &create_params);
|
||||
if (bm == nullptr) return WEB_ENGINE_DECIMATE_FAILED;
|
||||
/* UV/color transfer is performed by the command layer. Adding transient
|
||||
* loop CustomData to a hand-built BMesh is not safe for all Web allocator
|
||||
* layouts; Blender's native topology result remains authoritative. */
|
||||
(void)input_uvs;
|
||||
(void)input_colors;
|
||||
std::vector<BMVert *> input_verts(vertex_count, nullptr);
|
||||
for (uint32_t index = 0; index < vertex_count; index++) {
|
||||
input_verts[index] = blender::BM_vert_create(bm, positions + index * 3, nullptr, blender::BM_CREATE_NOP);
|
||||
if (input_verts[index] == nullptr) {
|
||||
blender::BM_mesh_free(bm);
|
||||
return WEB_ENGINE_DECIMATE_FAILED;
|
||||
}
|
||||
}
|
||||
for (uint32_t triangle = 0; triangle < triangle_count; triangle++) {
|
||||
const uint32_t a = indices[triangle * 3];
|
||||
const uint32_t b = indices[triangle * 3 + 1];
|
||||
const uint32_t c = indices[triangle * 3 + 2];
|
||||
if (a >= vertex_count || b >= vertex_count || c >= vertex_count || a == b || b == c || a == c) {
|
||||
blender::BM_mesh_free(bm);
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
BMVert *face_verts[3] = {input_verts[a], input_verts[b], input_verts[c]};
|
||||
BMFace *face = blender::BM_face_create_verts(
|
||||
bm, face_verts, 3, nullptr, blender::BM_CREATE_NO_DOUBLE, true);
|
||||
if (face == nullptr) {
|
||||
blender::BM_mesh_free(bm);
|
||||
return WEB_ENGINE_DECIMATE_FAILED;
|
||||
}
|
||||
if (input_material_indices != nullptr) {
|
||||
face->mat_nr = static_cast<short>(std::min(input_material_indices[triangle], uint32_t(32767)));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<float> weights;
|
||||
if (vertex_weights != nullptr) {
|
||||
weights.assign(vertex_weights, vertex_weights + vertex_count);
|
||||
if (vertex_group_invert != 0) {
|
||||
for (float &weight : weights) weight = 1.0f - weight;
|
||||
}
|
||||
}
|
||||
try {
|
||||
switch (mode) {
|
||||
case WEB_ENGINE_DECIMATE_COLLAPSE: {
|
||||
/* A hand-built BMesh can be safely collapsed one native step at a
|
||||
* time. Blender's desktop conversion keeps a longer-lived heap, but
|
||||
* repeatedly mutating this small Web mesh in one call can leave heap
|
||||
* nodes pointing at removed triangles. Each step gets a fresh native
|
||||
* heap and recalculates the Blender quadric choice from the current
|
||||
* mesh state without exposing that lifetime issue to WASM. */
|
||||
const int target_faces = static_cast<int>(bm->totface * ratio);
|
||||
std::unordered_map<BMVert *, float> weight_by_vertex;
|
||||
if (!weights.empty()) {
|
||||
blender::BM_mesh_elem_index_ensure(bm, blender::BM_VERT);
|
||||
blender::BMIter weight_iter;
|
||||
BMVert *weight_vertex;
|
||||
int weight_index = 0;
|
||||
BM_ITER_MESH_INDEX (weight_vertex, &weight_iter, bm, blender::BM_VERTS_OF_MESH, weight_index) {
|
||||
weight_by_vertex[weight_vertex] = weights[weight_index];
|
||||
}
|
||||
}
|
||||
uint32_t step_count = 0;
|
||||
while (bm->totface > target_faces && step_count++ < triangle_count * 4 + 16) {
|
||||
const int faces_before = bm->totface;
|
||||
blender::BM_mesh_elem_index_ensure(bm, blender::BM_VERT | blender::BM_EDGE);
|
||||
std::vector<float> step_weights;
|
||||
if (!weights.empty()) {
|
||||
step_weights.assign(static_cast<size_t>(bm->totvert), 0.0f);
|
||||
blender::BMIter weight_iter;
|
||||
BMVert *weight_vertex;
|
||||
int weight_index = 0;
|
||||
BM_ITER_MESH_INDEX (weight_vertex,
|
||||
&weight_iter,
|
||||
bm,
|
||||
blender::BM_VERTS_OF_MESH,
|
||||
weight_index) {
|
||||
const auto found = weight_by_vertex.find(weight_vertex);
|
||||
if (found != weight_by_vertex.end()) step_weights[weight_index] = found->second;
|
||||
}
|
||||
}
|
||||
const float step_factor = static_cast<float>(faces_before - 1) /
|
||||
static_cast<float>(std::max(faces_before, 1));
|
||||
blender::BM_mesh_decimate_collapse(bm,
|
||||
step_factor,
|
||||
step_weights.empty() ? nullptr : step_weights.data(),
|
||||
vertex_group_factor,
|
||||
triangulate != 0,
|
||||
symmetry_axis,
|
||||
symmetry_epsilon);
|
||||
if (bm->totface >= faces_before) break;
|
||||
if (!step_weights.empty()) {
|
||||
weight_by_vertex.clear();
|
||||
blender::BM_mesh_elem_index_ensure(bm, blender::BM_VERT);
|
||||
blender::BMIter weight_iter;
|
||||
BMVert *weight_vertex;
|
||||
int weight_index = 0;
|
||||
BM_ITER_MESH_INDEX (weight_vertex,
|
||||
&weight_iter,
|
||||
bm,
|
||||
blender::BM_VERTS_OF_MESH,
|
||||
weight_index) {
|
||||
weight_by_vertex[weight_vertex] = step_weights[weight_index];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WEB_ENGINE_DECIMATE_UNSUBDIV:
|
||||
blender::BM_mesh_decimate_unsubdivide(bm, static_cast<int>(iterations));
|
||||
break;
|
||||
case WEB_ENGINE_DECIMATE_DISSOLVE_PLANAR:
|
||||
blender::BM_mesh_decimate_dissolve(bm,
|
||||
angle_limit,
|
||||
use_dissolve_boundaries != 0,
|
||||
delimit_flags(delimit));
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (...) {
|
||||
blender::BM_mesh_free(bm);
|
||||
return WEB_ENGINE_DECIMATE_FAILED;
|
||||
}
|
||||
|
||||
blender::BM_mesh_elem_index_ensure(bm, blender::BM_VERT | blender::BM_FACE);
|
||||
output.positions.reserve(static_cast<size_t>(bm->totvert) * 3);
|
||||
blender::BMIter iter;
|
||||
BMVert *vertex;
|
||||
int vertex_index = 0;
|
||||
BM_ITER_MESH_INDEX (vertex, &iter, bm, blender::BM_VERTS_OF_MESH, vertex_index) {
|
||||
blender::BM_elem_index_set(vertex, vertex_index);
|
||||
output.positions.insert(output.positions.end(), vertex->co, vertex->co + 3);
|
||||
}
|
||||
BMFace *face;
|
||||
BM_ITER_MESH (face, &iter, bm, blender::BM_FACES_OF_MESH) {
|
||||
BMLoop *first = face->l_first;
|
||||
if (first == nullptr || first->next == first || first->next->next == first) continue;
|
||||
BMLoop *second = first->next;
|
||||
BMLoop *third = second->next;
|
||||
while (third != first) {
|
||||
const uint32_t a = static_cast<uint32_t>(blender::BM_elem_index_get(first->v));
|
||||
const uint32_t b = static_cast<uint32_t>(blender::BM_elem_index_get(second->v));
|
||||
const uint32_t c = static_cast<uint32_t>(blender::BM_elem_index_get(third->v));
|
||||
if (a != b && b != c && a != c) {
|
||||
output.indices.insert(output.indices.end(), {a, b, c});
|
||||
if (input_material_indices != nullptr) {
|
||||
output.material_indices.push_back(static_cast<uint32_t>(std::max(face->mat_nr, short(0))));
|
||||
}
|
||||
}
|
||||
second = third;
|
||||
third = third->next;
|
||||
}
|
||||
output.face_count++;
|
||||
}
|
||||
blender::BM_mesh_free(bm);
|
||||
return WEB_ENGINE_DECIMATE_OK;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" EMSCRIPTEN_KEEPALIVE int web_engine_decimate_apply(const float *positions,
|
||||
const uint32_t vertex_count,
|
||||
const uint32_t *indices,
|
||||
const uint32_t triangle_count,
|
||||
const uint32_t mode,
|
||||
const float ratio,
|
||||
const uint32_t iterations,
|
||||
const float angle_limit,
|
||||
const uint32_t delimit,
|
||||
const uint32_t use_dissolve_boundaries,
|
||||
const uint32_t triangulate,
|
||||
const int32_t symmetry_axis,
|
||||
const float symmetry_epsilon,
|
||||
const float *vertex_weights,
|
||||
const float vertex_group_factor,
|
||||
const uint32_t vertex_group_invert,
|
||||
const float *uvs,
|
||||
const float *colors,
|
||||
const uint32_t *material_indices,
|
||||
float *positions_out,
|
||||
const uint32_t positions_capacity,
|
||||
uint32_t *positions_count,
|
||||
uint32_t *indices_out,
|
||||
const uint32_t indices_capacity,
|
||||
uint32_t *indices_count,
|
||||
float *uvs_out,
|
||||
const uint32_t uvs_capacity,
|
||||
uint32_t *uvs_count,
|
||||
float *colors_out,
|
||||
const uint32_t colors_capacity,
|
||||
uint32_t *colors_count,
|
||||
uint32_t *material_indices_out,
|
||||
const uint32_t material_indices_capacity,
|
||||
uint32_t *material_indices_count,
|
||||
uint32_t *face_count)
|
||||
{
|
||||
if (positions_count == nullptr || indices_count == nullptr || uvs_count == nullptr || colors_count == nullptr ||
|
||||
material_indices_count == nullptr || face_count == nullptr) {
|
||||
return WEB_ENGINE_DECIMATE_INVALID_ARGUMENT;
|
||||
}
|
||||
OutputMesh output;
|
||||
const int result = build_and_decimate(positions,
|
||||
vertex_count,
|
||||
indices,
|
||||
triangle_count,
|
||||
mode,
|
||||
ratio,
|
||||
iterations,
|
||||
angle_limit,
|
||||
delimit,
|
||||
use_dissolve_boundaries,
|
||||
triangulate,
|
||||
symmetry_axis,
|
||||
symmetry_epsilon,
|
||||
vertex_weights,
|
||||
vertex_group_factor,
|
||||
vertex_group_invert,
|
||||
uvs,
|
||||
colors,
|
||||
material_indices,
|
||||
output);
|
||||
if (result != WEB_ENGINE_DECIMATE_OK) return result;
|
||||
*positions_count = static_cast<uint32_t>(output.positions.size());
|
||||
*indices_count = static_cast<uint32_t>(output.indices.size());
|
||||
*uvs_count = static_cast<uint32_t>(output.uvs.size());
|
||||
*colors_count = static_cast<uint32_t>(output.colors.size());
|
||||
*material_indices_count = static_cast<uint32_t>(output.material_indices.size());
|
||||
*face_count = output.face_count;
|
||||
if (positions_capacity < output.positions.size() || indices_capacity < output.indices.size() ||
|
||||
uvs_capacity < output.uvs.size() || colors_capacity < output.colors.size() ||
|
||||
material_indices_capacity < output.material_indices.size() ||
|
||||
(output.positions.size() > 0 && positions_out == nullptr) ||
|
||||
(output.indices.size() > 0 && indices_out == nullptr) ||
|
||||
(output.uvs.size() > 0 && uvs_out == nullptr) || (output.colors.size() > 0 && colors_out == nullptr) ||
|
||||
(output.material_indices.size() > 0 && material_indices_out == nullptr)) {
|
||||
return WEB_ENGINE_DECIMATE_BUFFER_TOO_SMALL;
|
||||
}
|
||||
if (!output.positions.empty()) std::memcpy(positions_out, output.positions.data(), output.positions.size() * sizeof(float));
|
||||
if (!output.indices.empty()) std::memcpy(indices_out, output.indices.data(), output.indices.size() * sizeof(uint32_t));
|
||||
if (!output.uvs.empty()) std::memcpy(uvs_out, output.uvs.data(), output.uvs.size() * sizeof(float));
|
||||
if (!output.colors.empty()) std::memcpy(colors_out, output.colors.data(), output.colors.size() * sizeof(float));
|
||||
if (!output.material_indices.empty()) {
|
||||
std::memcpy(material_indices_out,
|
||||
output.material_indices.data(),
|
||||
output.material_indices.size() * sizeof(uint32_t));
|
||||
}
|
||||
return WEB_ENGINE_DECIMATE_OK;
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
enum WebEngineDecimateMode : uint32_t {
|
||||
WEB_ENGINE_DECIMATE_COLLAPSE = 0,
|
||||
WEB_ENGINE_DECIMATE_UNSUBDIV = 1,
|
||||
WEB_ENGINE_DECIMATE_DISSOLVE_PLANAR = 2,
|
||||
};
|
||||
|
||||
enum WebEngineDecimateResultCode : int {
|
||||
WEB_ENGINE_DECIMATE_OK = 0,
|
||||
WEB_ENGINE_DECIMATE_INVALID_ARGUMENT = -2,
|
||||
WEB_ENGINE_DECIMATE_NOT_IMPLEMENTED = -3,
|
||||
WEB_ENGINE_DECIMATE_FAILED = -4,
|
||||
WEB_ENGINE_DECIMATE_BUFFER_TOO_SMALL = -6,
|
||||
};
|
||||
|
||||
/*
|
||||
* Run Blender's BMesh decimate implementation on a triangle input. The first
|
||||
* call may pass null output buffers and zero capacities to query the required
|
||||
* float/index counts. A second call with those capacities copies the evaluated
|
||||
* topology while leaving the input arrays untouched.
|
||||
*/
|
||||
extern "C" int web_engine_decimate_apply(const float *positions,
|
||||
uint32_t vertex_count,
|
||||
const uint32_t *indices,
|
||||
uint32_t triangle_count,
|
||||
uint32_t mode,
|
||||
float ratio,
|
||||
uint32_t iterations,
|
||||
float angle_limit,
|
||||
uint32_t delimit,
|
||||
uint32_t use_dissolve_boundaries,
|
||||
uint32_t triangulate,
|
||||
int32_t symmetry_axis,
|
||||
float symmetry_epsilon,
|
||||
const float *vertex_weights,
|
||||
float vertex_group_factor,
|
||||
uint32_t vertex_group_invert,
|
||||
const float *uvs,
|
||||
const float *colors,
|
||||
const uint32_t *material_indices,
|
||||
float *positions_out,
|
||||
uint32_t positions_capacity,
|
||||
uint32_t *positions_count,
|
||||
uint32_t *indices_out,
|
||||
uint32_t indices_capacity,
|
||||
uint32_t *indices_count,
|
||||
float *uvs_out,
|
||||
uint32_t uvs_capacity,
|
||||
uint32_t *uvs_count,
|
||||
float *colors_out,
|
||||
uint32_t colors_capacity,
|
||||
uint32_t *colors_count,
|
||||
uint32_t *material_indices_out,
|
||||
uint32_t material_indices_capacity,
|
||||
uint32_t *material_indices_count,
|
||||
uint32_t *face_count);
|
||||
750
blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp
Normal file
750
blender-5.2.0/source/blender/web_engine/web_engine_depsgraph.cpp
Normal file
@@ -0,0 +1,750 @@
|
||||
#include "web_engine_depsgraph.h"
|
||||
#include "web_engine_blend_reader.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
|
||||
#ifdef WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH
|
||||
# include <cstdio>
|
||||
# define WEB_DEPSGRAPH_TRACE(stage) \
|
||||
do { \
|
||||
std::fprintf(stderr, "web-depsgraph-stage=%s\n", stage); \
|
||||
std::fflush(stderr); \
|
||||
} while (false)
|
||||
#else
|
||||
# define WEB_DEPSGRAPH_TRACE(stage) ((void)0)
|
||||
#endif
|
||||
|
||||
#include "json.hpp"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_attribute.hh"
|
||||
#include "BKE_blender.hh"
|
||||
#include "BKE_callbacks.hh"
|
||||
#include "BKE_cpp_types.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_material.hh"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_wrapper.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_scene.hh"
|
||||
#include "BKE_vfont.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_threads.h"
|
||||
|
||||
#include "BLO_readfile.hh"
|
||||
|
||||
#include "BLF_api.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "ED_datafiles.h"
|
||||
|
||||
#include "DNA_genfile.h"
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_constraint_types.h"
|
||||
#include "DNA_curve_types.h"
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vfont_types.h"
|
||||
#include "DNA_world_types.h"
|
||||
|
||||
#include "FN_init.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
namespace blender {
|
||||
void WEB_headless_node_system_init();
|
||||
void web_rna_init();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
using json = nlohmann::json;
|
||||
using namespace blender;
|
||||
|
||||
std::once_flag blender_runtime_once;
|
||||
|
||||
void initialize_blender_runtime()
|
||||
{
|
||||
std::call_once(blender_runtime_once, []() {
|
||||
CLG_init();
|
||||
BLI_threadapi_init();
|
||||
DNA_sdna_current_init();
|
||||
BKE_blender_globals_init();
|
||||
BKE_cpp_types_init();
|
||||
web_rna_init();
|
||||
fn::multi_function::register_common_functions();
|
||||
BKE_idtype_init();
|
||||
BKE_appdir_init();
|
||||
BKE_modifier_init();
|
||||
BKE_callback_global_init();
|
||||
BLI_task_scheduler_init();
|
||||
BLF_init();
|
||||
BKE_vfont_builtin_register(datatoc_bfont_pfb, datatoc_bfont_pfb_size);
|
||||
WEB_headless_node_system_init();
|
||||
DEG_register_node_types();
|
||||
G.background = true;
|
||||
G.factory_startup = true;
|
||||
});
|
||||
}
|
||||
|
||||
#ifdef WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH
|
||||
void trace_loaded_scene(Main *main, Scene *scene)
|
||||
{
|
||||
std::fprintf(stderr,
|
||||
"web-depsgraph-data scene=%p master=%p nodetree=%p world=%p collections=%p\n",
|
||||
static_cast<void *>(scene),
|
||||
scene ? static_cast<void *>(scene->master_collection) : nullptr,
|
||||
scene ? static_cast<void *>(scene->nodetree) : nullptr,
|
||||
scene && scene->world ? static_cast<void *>(scene->world->nodetree) : nullptr,
|
||||
main ? main->collections.first : nullptr);
|
||||
if (main == nullptr) return;
|
||||
int index = 0;
|
||||
for (Collection &collection : main->collections) {
|
||||
if (index++ >= 16) break;
|
||||
std::fprintf(stderr,
|
||||
"web-depsgraph-collection index=%d id=%p name=%c%c%c%c first=%p last=%p\n",
|
||||
index - 1,
|
||||
static_cast<void *>(&collection),
|
||||
collection.id.name[0],
|
||||
collection.id.name[1],
|
||||
collection.id.name[2],
|
||||
collection.id.name[3],
|
||||
collection.gobject.first,
|
||||
collection.children.first);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
class GlobalMainScope {
|
||||
public:
|
||||
explicit GlobalMainScope(Main *loaded_main) : loaded_main_(loaded_main)
|
||||
{
|
||||
if (loaded_main_ != nullptr && !loaded_main_->is_global_main) {
|
||||
previous_main_ = BKE_blender_globals_main_swap(loaded_main_);
|
||||
}
|
||||
}
|
||||
|
||||
~GlobalMainScope()
|
||||
{
|
||||
if (previous_main_ != nullptr) {
|
||||
Main *restored_loaded_main = BKE_blender_globals_main_swap(previous_main_);
|
||||
BLI_assert(restored_loaded_main == loaded_main_);
|
||||
(void)restored_loaded_main;
|
||||
}
|
||||
}
|
||||
|
||||
GlobalMainScope(const GlobalMainScope &) = delete;
|
||||
GlobalMainScope &operator=(const GlobalMainScope &) = delete;
|
||||
|
||||
private:
|
||||
Main *loaded_main_ = nullptr;
|
||||
Main *previous_main_ = nullptr;
|
||||
};
|
||||
|
||||
std::string id_name(const ID &id)
|
||||
{
|
||||
return id.name[0] != '\0' && id.name[1] != '\0' ? std::string(id.name + 2) : std::string();
|
||||
}
|
||||
|
||||
std::string scene_ir_id(const char *prefix, const ID &id)
|
||||
{
|
||||
const std::string name = id_name(id);
|
||||
return std::string(prefix) + ":" + (name.empty() ? "unnamed" : name);
|
||||
}
|
||||
|
||||
std::string object_type_name(const Object &object)
|
||||
{
|
||||
switch (object.type) {
|
||||
case OB_MESH:
|
||||
return "MESH";
|
||||
case OB_CURVES_LEGACY:
|
||||
return "CURVE";
|
||||
case OB_SURF:
|
||||
return "SURFACE";
|
||||
case OB_FONT:
|
||||
return "FONT";
|
||||
case OB_MBALL:
|
||||
return "METABALL";
|
||||
case OB_LATTICE:
|
||||
return "LATTICE";
|
||||
case OB_ARMATURE:
|
||||
return "ARMATURE";
|
||||
case OB_GREASE_PENCIL:
|
||||
return "GREASE_PENCIL";
|
||||
case OB_CURVES:
|
||||
return "CURVES";
|
||||
case OB_POINTCLOUD:
|
||||
return "POINT_CLOUD";
|
||||
case OB_VOLUME:
|
||||
return "VOLUME";
|
||||
case OB_EMPTY:
|
||||
return "EMPTY";
|
||||
default:
|
||||
return "OTHER";
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<std::string> preflight_unsafe_resource(const uint8_t *data, const uint32_t length)
|
||||
{
|
||||
/* Geometry node trees are safe to load with undefined node typeinfo. The
|
||||
* bounded Web modifier validates its supported graph before executing it. */
|
||||
(void)data;
|
||||
(void)length;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
struct ModifierTargetObjects {
|
||||
const Object *primary = nullptr;
|
||||
const Object *secondary = nullptr;
|
||||
bool required = false;
|
||||
};
|
||||
|
||||
ModifierTargetObjects modifier_target_objects(const ModifierData &modifier)
|
||||
{
|
||||
switch (modifier.type) {
|
||||
case eModifierType_Array: {
|
||||
const auto &array = reinterpret_cast<const ArrayModifierData &>(modifier);
|
||||
return {array.offset_ob, array.curve_ob, false};
|
||||
}
|
||||
case eModifierType_Mirror:
|
||||
return {reinterpret_cast<const MirrorModifierData &>(modifier).mirror_ob, nullptr, false};
|
||||
case eModifierType_Boolean:
|
||||
return {reinterpret_cast<const BooleanModifierData &>(modifier).object, nullptr, false};
|
||||
case eModifierType_Lattice:
|
||||
return {reinterpret_cast<const LatticeModifierData &>(modifier).object, nullptr, true};
|
||||
case eModifierType_Curve:
|
||||
return {reinterpret_cast<const CurveModifierData &>(modifier).object, nullptr, true};
|
||||
case eModifierType_Armature:
|
||||
return {reinterpret_cast<const ArmatureModifierData &>(modifier).object, nullptr, true};
|
||||
case eModifierType_Hook:
|
||||
return {reinterpret_cast<const HookModifierData &>(modifier).object, nullptr, true};
|
||||
case eModifierType_MeshDeform:
|
||||
return {reinterpret_cast<const MeshDeformModifierData &>(modifier).object, nullptr, true};
|
||||
case eModifierType_Shrinkwrap: {
|
||||
const auto &shrinkwrap = reinterpret_cast<const ShrinkwrapModifierData &>(modifier);
|
||||
return {shrinkwrap.target, shrinkwrap.auxTarget, true};
|
||||
}
|
||||
case eModifierType_SurfaceDeform:
|
||||
return {reinterpret_cast<const SurfaceDeformModifierData &>(modifier).target, nullptr, true};
|
||||
case eModifierType_Screw:
|
||||
return {reinterpret_cast<const ScrewModifierData &>(modifier).ob_axis, nullptr, false};
|
||||
case eModifierType_Displace:
|
||||
return {reinterpret_cast<const DisplaceModifierData &>(modifier).map_object, nullptr, false};
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool object_dependency_reaches(const Object *object,
|
||||
const Object *needle,
|
||||
std::unordered_set<const Object *> &visited)
|
||||
{
|
||||
if (object == nullptr) return false;
|
||||
if (object == needle) return true;
|
||||
if (!visited.insert(object).second) return false;
|
||||
for (const ModifierData *modifier = static_cast<const ModifierData *>(object->modifiers.first);
|
||||
modifier != nullptr;
|
||||
modifier = modifier->next)
|
||||
{
|
||||
if ((modifier->mode & eModifierMode_Realtime) == 0) continue;
|
||||
const ModifierTargetObjects targets = modifier_target_objects(*modifier);
|
||||
if (object_dependency_reaches(targets.primary, needle, visited) ||
|
||||
object_dependency_reaches(targets.secondary, needle, visited))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
json evaluated_modifier_report(const Object &object, const Object *object_eval)
|
||||
{
|
||||
json modifiers = json::array();
|
||||
size_t index = 0;
|
||||
std::string previous_uuid;
|
||||
for (const ModifierData *modifier = static_cast<const ModifierData *>(object.modifiers.first);
|
||||
modifier != nullptr;
|
||||
modifier = modifier->next, index++) {
|
||||
const ModifierTypeInfo *info = BKE_modifier_get_info(modifier->type);
|
||||
const bool supported_runtime = info != nullptr &&
|
||||
(modifier->type == eModifierType_None ||
|
||||
info->type != ModifierTypeType::None);
|
||||
const int uid = modifier->persistent_uid;
|
||||
const std::string uuid = "modifier:" + scene_ir_id("object", object.id) + ":" +
|
||||
(uid > 0 ? std::to_string(uid) : std::to_string(index));
|
||||
const bool viewport_enabled = (modifier->mode & eModifierMode_Realtime) != 0;
|
||||
const ModifierTargetObjects targets = modifier_target_objects(*modifier);
|
||||
json target_object_ids = json::array();
|
||||
auto append_target = [&target_object_ids](const Object *target) {
|
||||
if (target != nullptr) target_object_ids.push_back(scene_ir_id("object", target->id));
|
||||
};
|
||||
if (supported_runtime) {
|
||||
append_target(targets.primary);
|
||||
append_target(targets.secondary);
|
||||
}
|
||||
json entry = {{"uuid", uuid},
|
||||
{"index", index},
|
||||
{"persistentUid", uid},
|
||||
{"typeCode", int(modifier->type)},
|
||||
{"type", supported_runtime ? std::string(info->idname) :
|
||||
"UNSUPPORTED_" + std::to_string(int(modifier->type))},
|
||||
{"name", std::string(modifier->name)},
|
||||
{"showViewport", viewport_enabled},
|
||||
{"showRender", (modifier->mode & eModifierMode_Render) != 0},
|
||||
{"showEditmode", (modifier->mode & eModifierMode_Editmode) != 0},
|
||||
{"showOnCage", (modifier->mode & eModifierMode_OnCage) != 0},
|
||||
{"status", viewport_enabled ? "EVALUATED" : "DISABLED"}};
|
||||
if (!target_object_ids.empty()) entry["targetObjectIds"] = target_object_ids;
|
||||
if (!viewport_enabled) {
|
||||
entry["reason"] = "viewport-disabled";
|
||||
}
|
||||
if (viewport_enabled && !supported_runtime) {
|
||||
entry["status"] = "BLOCKED";
|
||||
entry["errorCode"] = "UNSUPPORTED_MODIFIER_TYPE";
|
||||
entry["error"] = "Blender modifier type is not registered in the WebEngine runtime";
|
||||
entry["suggestion"] = "Disable, apply, or convert this modifier before Web evaluation";
|
||||
}
|
||||
if (viewport_enabled && supported_runtime && targets.required && target_object_ids.empty()) {
|
||||
entry["status"] = "BLOCKED";
|
||||
entry["errorCode"] = "MODIFIER_TARGET_MISSING";
|
||||
entry["error"] = "Modifier requires a target object";
|
||||
entry["suggestion"] = "Assign a valid target object or disable this modifier";
|
||||
}
|
||||
if (object_eval != nullptr) {
|
||||
for (const ModifierData *evaluated = static_cast<const ModifierData *>(object_eval->modifiers.first);
|
||||
evaluated != nullptr;
|
||||
evaluated = evaluated->next) {
|
||||
if (uid > 0 && evaluated->persistent_uid == uid) {
|
||||
if (viewport_enabled && evaluated->error != nullptr && evaluated->error[0] != '\0') {
|
||||
const std::string error(evaluated->error);
|
||||
entry["status"] = "BLOCKED";
|
||||
if (error.starts_with("WEB_GEOMETRY_NODES_SIMULATION_UNAVAILABLE:")) {
|
||||
entry["errorCode"] = "GEOMETRY_NODES_SIMULATION_UNAVAILABLE";
|
||||
entry["suggestion"] =
|
||||
"Bake a deterministic cache in desktop Blender or disable the simulation zone";
|
||||
}
|
||||
else if (error.starts_with("WEB_DISPLACE_CONFIGURATION_UNAVAILABLE:")) {
|
||||
entry["errorCode"] = "MODIFIER_CONFIGURATION_UNSUPPORTED";
|
||||
entry["suggestion"] =
|
||||
"Use constant local-space displacement or disable/apply this modifier in desktop Blender";
|
||||
}
|
||||
else {
|
||||
entry["errorCode"] = "BLENDER_MODIFIER_ERROR";
|
||||
entry["suggestion"] = "Fix the Blender modifier settings or disable the modifier";
|
||||
}
|
||||
entry["error"] = error;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (viewport_enabled && supported_runtime && !target_object_ids.empty()) {
|
||||
std::unordered_set<const Object *> visited;
|
||||
if (object_dependency_reaches(targets.primary, &object, visited) ||
|
||||
object_dependency_reaches(targets.secondary, &object, visited))
|
||||
{
|
||||
entry["status"] = "BLOCKED";
|
||||
entry["errorCode"] = "MODIFIER_DEPENDENCY_CYCLE";
|
||||
entry["error"] = "Modifier target dependency reaches the owning object";
|
||||
entry["suggestion"] = "Break the target-object cycle or disable one modifier in the cycle";
|
||||
}
|
||||
}
|
||||
json dependencies = json::array();
|
||||
if (!previous_uuid.empty()) dependencies.push_back(previous_uuid);
|
||||
for (const json &target : target_object_ids) dependencies.push_back(target);
|
||||
if (!dependencies.empty()) entry["dependsOn"] = std::move(dependencies);
|
||||
modifiers.push_back(std::move(entry));
|
||||
previous_uuid = uuid;
|
||||
}
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
json evaluated_mesh_report(const Depsgraph *graph, Object *object)
|
||||
{
|
||||
Object *object_eval = DEG_get_evaluated(graph, object);
|
||||
if (object_eval == nullptr) return json();
|
||||
Mesh *mesh = BKE_object_get_evaluated_mesh(object_eval);
|
||||
if (mesh == nullptr) return json();
|
||||
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh);
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<int3> corner_tris = mesh->corner_tris();
|
||||
json report = {{"objectId", scene_ir_id("object", object->id)},
|
||||
{"meshId", scene_ir_id("mesh", mesh->id)},
|
||||
{"sourceMeshId", object->data != nullptr ?
|
||||
scene_ir_id("mesh", *static_cast<ID *>(object->data)) :
|
||||
std::string()},
|
||||
{"vertexCount", positions.size()},
|
||||
{"triangleCount", corner_tris.size()},
|
||||
{"modifierCount", BLI_listbase_count(&object->modifiers)},
|
||||
{"modifiers", evaluated_modifier_report(*object, object_eval)},
|
||||
{"worldMatrix", json::array()},
|
||||
{"positions", json::array()},
|
||||
{"indices", json::array()}};
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int column = 0; column < 4; column++) {
|
||||
/* The Web scene IR uses the same column-major flattening as Blender's Python API and glTF. */
|
||||
report["worldMatrix"].push_back(object_eval->object_to_world()[column][row]);
|
||||
}
|
||||
}
|
||||
for (const float3 &position : positions) {
|
||||
report["positions"].push_back(position.x);
|
||||
report["positions"].push_back(position.y);
|
||||
report["positions"].push_back(position.z);
|
||||
}
|
||||
for (const int3 &triangle : corner_tris) {
|
||||
for (int index = 0; index < 3; index++) {
|
||||
report["indices"].push_back(corner_verts[triangle[index]]);
|
||||
}
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
std::string non_mesh_data_id(const Object &object)
|
||||
{
|
||||
if (object.data == nullptr) return {};
|
||||
const ID &id = *static_cast<const ID *>(object.data);
|
||||
switch (object.type) {
|
||||
case OB_CURVES_LEGACY:
|
||||
case OB_SURF:
|
||||
case OB_FONT:
|
||||
return scene_ir_id("curve", id);
|
||||
case OB_MBALL:
|
||||
return scene_ir_id("metaball", id);
|
||||
case OB_CURVES:
|
||||
return scene_ir_id("curves", id);
|
||||
case OB_POINTCLOUD:
|
||||
return scene_ir_id("pointcloud", id);
|
||||
case OB_VOLUME:
|
||||
return scene_ir_id("volume", id);
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void ensure_builtin_font_links(Main *main)
|
||||
{
|
||||
if (main == nullptr) return;
|
||||
VFont *builtin = nullptr;
|
||||
for (Object &object : main->objects) {
|
||||
if (object.type != OB_FONT || object.data == nullptr) continue;
|
||||
Curve &curve = *reinterpret_cast<Curve *>(object.data);
|
||||
if (curve.vfont == nullptr) {
|
||||
if (builtin == nullptr) {
|
||||
builtin = BKE_vfont_builtin_ensure();
|
||||
}
|
||||
curve.vfont = builtin;
|
||||
builtin->id.us++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json evaluated_non_mesh_report(Depsgraph *graph, Object *object)
|
||||
{
|
||||
Object *object_eval = DEG_get_evaluated(graph, object);
|
||||
if (object_eval == nullptr) return json();
|
||||
Mesh *mesh = BKE_object_get_evaluated_mesh(object_eval);
|
||||
bool owns_mesh = false;
|
||||
if (mesh == nullptr) {
|
||||
mesh = BKE_mesh_new_from_object(graph, object_eval, false, false, true);
|
||||
owns_mesh = mesh != nullptr;
|
||||
}
|
||||
if (mesh == nullptr) return json();
|
||||
BKE_mesh_wrapper_ensure_mdata(mesh);
|
||||
const Span<float3> positions = mesh->vert_positions();
|
||||
const Span<float3> normals = mesh->vert_normals();
|
||||
const Span<int2> edges = mesh->edges();
|
||||
const Span<int> corner_verts = mesh->corner_verts();
|
||||
const Span<int3> corner_tris = mesh->corner_tris();
|
||||
const Span<int> triangle_faces = mesh->corner_tri_faces();
|
||||
json report = {{"objectId", scene_ir_id("object", object->id)},
|
||||
{"sourceDataId", non_mesh_data_id(*object)},
|
||||
{"sourceType", object_type_name(*object)},
|
||||
{"meshId", "evaluated:" + scene_ir_id("object", object->id)},
|
||||
{"vertexCount", positions.size()},
|
||||
{"edgeCount", edges.size()},
|
||||
{"triangleCount", corner_tris.size()},
|
||||
{"status", "EVALUATED"},
|
||||
{"worldMatrix", json::array()},
|
||||
{"positions", json::array()},
|
||||
{"normals", json::array()},
|
||||
{"edgeVertexIndices", json::array()},
|
||||
{"indices", json::array()},
|
||||
{"uvs", json::array()},
|
||||
{"triangleMaterialIndices", json::array()},
|
||||
{"sourceElementIndices", json::array()},
|
||||
{"materialSlotIds", json::array()},
|
||||
{"sourceMappingStatus", corner_tris.is_empty() && !edges.is_empty() ?
|
||||
"EVALUATED_EDGE" :
|
||||
"EVALUATED_FACE"}};
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int column = 0; column < 4; column++) {
|
||||
report["worldMatrix"].push_back(object_eval->object_to_world()[column][row]);
|
||||
}
|
||||
}
|
||||
if (positions.size() > 1000000 || edges.size() > 2000000 ||
|
||||
corner_tris.size() > 2000000)
|
||||
{
|
||||
report["status"] = "BLOCKED";
|
||||
report["errorCode"] = "NON_MESH_DATA_BUDGET_EXCEEDED";
|
||||
for (const char *field : {"positions", "normals", "edgeVertexIndices", "indices", "uvs", "triangleMaterialIndices", "sourceElementIndices"}) {
|
||||
report.erase(field);
|
||||
}
|
||||
if (owns_mesh) BKE_id_free(nullptr, mesh);
|
||||
return report;
|
||||
}
|
||||
for (const float3 &position : positions) {
|
||||
report["positions"].push_back(position.x);
|
||||
report["positions"].push_back(position.y);
|
||||
report["positions"].push_back(position.z);
|
||||
}
|
||||
for (const float3 &normal : normals) {
|
||||
report["normals"].push_back(normal.x);
|
||||
report["normals"].push_back(normal.y);
|
||||
report["normals"].push_back(normal.z);
|
||||
}
|
||||
for (const int2 &edge : edges) {
|
||||
report["edgeVertexIndices"].push_back(edge[0]);
|
||||
report["edgeVertexIndices"].push_back(edge[1]);
|
||||
}
|
||||
const bke::AttributeAccessor attributes = mesh->attributes();
|
||||
const bke::GAttributeReader material_reader = attributes.lookup(
|
||||
"material_index", bke::AttrDomain::Face, bke::AttrType::Int32);
|
||||
const VArray<int> material_indices = material_reader ?
|
||||
material_reader.varray.typed<int>() :
|
||||
VArray<int>();
|
||||
const StringRef active_uv = mesh->active_uv_map_name();
|
||||
const bke::AttributeReader<float2> uv_reader = active_uv.is_empty() ?
|
||||
bke::AttributeReader<float2>() :
|
||||
attributes.lookup<float2>(active_uv,
|
||||
bke::AttrDomain::Corner);
|
||||
for (size_t triangle_index = 0; triangle_index < corner_tris.size(); triangle_index++) {
|
||||
const int3 &triangle = corner_tris[triangle_index];
|
||||
for (int index = 0; index < 3; index++) {
|
||||
report["indices"].push_back(corner_verts[triangle[index]]);
|
||||
if (uv_reader) {
|
||||
const float2 uv = uv_reader.varray[triangle[index]];
|
||||
report["uvs"].push_back(uv.x);
|
||||
report["uvs"].push_back(uv.y);
|
||||
}
|
||||
}
|
||||
const int face_index = triangle_index < triangle_faces.size() ? triangle_faces[triangle_index] :
|
||||
int(triangle_index);
|
||||
report["sourceElementIndices"].push_back(face_index);
|
||||
report["triangleMaterialIndices"].push_back(
|
||||
!material_indices.is_empty() && face_index >= 0 && face_index < material_indices.size() ?
|
||||
std::max(0, material_indices[face_index]) :
|
||||
0);
|
||||
}
|
||||
for (short slot = 1; slot <= BKE_object_material_count_eval(object_eval); slot++) {
|
||||
if (Material *material = BKE_object_material_get(object_eval, slot)) {
|
||||
report["materialSlotIds"].push_back(scene_ir_id("material", material->id));
|
||||
}
|
||||
else {
|
||||
report["materialSlotIds"].push_back("");
|
||||
}
|
||||
}
|
||||
if (owns_mesh) BKE_id_free(nullptr, mesh);
|
||||
return report;
|
||||
}
|
||||
|
||||
json evaluated_armature_report(const Depsgraph *graph, Object *object)
|
||||
{
|
||||
Object *object_eval = DEG_get_evaluated(graph, object);
|
||||
if (object_eval == nullptr || object_eval->pose == nullptr || object_eval->data == nullptr) {
|
||||
return json();
|
||||
}
|
||||
const ID &armature_id = *static_cast<const ID *>(object->data);
|
||||
json bones = json::array();
|
||||
for (const bPoseChannel *channel = static_cast<const bPoseChannel *>(object_eval->pose->chanbase.first);
|
||||
channel != nullptr;
|
||||
channel = channel->next)
|
||||
{
|
||||
const std::string bone_id = "bone:" + scene_ir_id("armature", armature_id) + ":" +
|
||||
std::string(channel->name);
|
||||
json pose_matrix = json::array();
|
||||
for (int row = 0; row < 4; row++) {
|
||||
for (int column = 0; column < 4; column++) pose_matrix.push_back(channel->pose_mat[column][row]);
|
||||
}
|
||||
json constraints = json::array();
|
||||
for (const bConstraint *constraint = static_cast<const bConstraint *>(channel->constraints.first);
|
||||
constraint != nullptr;
|
||||
constraint = constraint->next)
|
||||
{
|
||||
json entry = {{"name", std::string(constraint->name)},
|
||||
{"typeCode", int(constraint->type)},
|
||||
{"influence", constraint->enforce},
|
||||
{"flag", int(constraint->flag)}};
|
||||
const Object *target = constraint->space_object;
|
||||
if (constraint->type == CONSTRAINT_TYPE_ROTLIKE && constraint->data != nullptr) {
|
||||
target = reinterpret_cast<const bRotateLikeConstraint *>(constraint->data)->tar;
|
||||
}
|
||||
if (constraint->type == CONSTRAINT_TYPE_KINEMATIC && constraint->data != nullptr) {
|
||||
const bKinematicConstraint *kinematic = reinterpret_cast<const bKinematicConstraint *>(
|
||||
constraint->data);
|
||||
target = kinematic->tar;
|
||||
if (kinematic->poletar != nullptr) {
|
||||
entry["poleTargetObjectId"] = scene_ir_id("object", kinematic->poletar->id);
|
||||
}
|
||||
}
|
||||
if (target != nullptr) entry["targetObjectId"] = scene_ir_id("object", target->id);
|
||||
constraints.push_back(std::move(entry));
|
||||
}
|
||||
bones.push_back({{"id", bone_id}, {"name", std::string(channel->name)}, {"poseMatrix", pose_matrix}, {"constraints", constraints}});
|
||||
}
|
||||
return json{{"id", scene_ir_id("armature", armature_id)},
|
||||
{"objectId", scene_ir_id("object", object->id)},
|
||||
{"bones", std::move(bones)}};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void web_engine_blender_runtime_initialize()
|
||||
{
|
||||
initialize_blender_runtime();
|
||||
}
|
||||
|
||||
WebDepsgraphEvaluationResult web_engine_evaluate_blender_depsgraph(const uint8_t *data,
|
||||
const uint32_t length,
|
||||
const int requested_frame)
|
||||
{
|
||||
WebDepsgraphEvaluationResult result;
|
||||
if (data == nullptr || length == 0 || length > uint32_t(std::numeric_limits<int>::max())) {
|
||||
result.error = "Depsgraph requires a non-empty addressable blend buffer";
|
||||
return result;
|
||||
}
|
||||
if (const std::optional<std::string> blocked = preflight_unsafe_resource(data, length)) {
|
||||
result.error = *blocked;
|
||||
return result;
|
||||
}
|
||||
WEB_DEPSGRAPH_TRACE("runtime-init-begin");
|
||||
initialize_blender_runtime();
|
||||
WEB_DEPSGRAPH_TRACE("runtime-init-end");
|
||||
BlendFileData *blend = BLO_read_from_memory(data, int(length), BLO_READ_SKIP_USERDEF, nullptr);
|
||||
WEB_DEPSGRAPH_TRACE("blend-read-end");
|
||||
if (blend == nullptr || blend->main == nullptr) {
|
||||
result.error = "Blender full loader rejected the blend buffer";
|
||||
if (blend != nullptr) BLO_blendfiledata_free(blend);
|
||||
return result;
|
||||
}
|
||||
Scene *scene = blend->curscene != nullptr ? blend->curscene :
|
||||
static_cast<Scene *>(blend->main->scenes.first);
|
||||
ViewLayer *view_layer = blend->cur_view_layer != nullptr ? blend->cur_view_layer :
|
||||
(scene != nullptr ? static_cast<ViewLayer *>(scene->view_layers.first) : nullptr);
|
||||
if (scene == nullptr || view_layer == nullptr) {
|
||||
result.error = "Blender full loader found no scene/view layer";
|
||||
BLO_blendfiledata_free(blend);
|
||||
return result;
|
||||
}
|
||||
BKE_scene_frame_set(scene,
|
||||
float(std::clamp(requested_frame, scene->r.sfra, scene->r.efra)));
|
||||
for (ViewLayer &candidate : scene->view_layers) {
|
||||
BKE_view_layer_synced_ensure(*blend->main, scene, &candidate);
|
||||
}
|
||||
WEB_DEPSGRAPH_TRACE("view-layer-sync-end");
|
||||
#ifdef WEB_ENGINE_ENABLE_UNSAFE_DEPSGRAPH
|
||||
trace_loaded_scene(blend->main, scene);
|
||||
#endif
|
||||
{
|
||||
GlobalMainScope global_main_scope(blend->main);
|
||||
ensure_builtin_font_links(blend->main);
|
||||
WEB_DEPSGRAPH_TRACE("global-main-swap-end");
|
||||
Depsgraph *graph = DEG_graph_new(blend->main, scene, view_layer, DAG_EVAL_VIEWPORT);
|
||||
WEB_DEPSGRAPH_TRACE("graph-new-end");
|
||||
if (graph == nullptr) {
|
||||
result.error = "Blender Depsgraph allocation failed";
|
||||
}
|
||||
else {
|
||||
DEG_graph_build_from_view_layer(graph);
|
||||
WEB_DEPSGRAPH_TRACE("graph-build-end");
|
||||
BKE_scene_graph_update_for_newframe(graph);
|
||||
WEB_DEPSGRAPH_TRACE("frame-change-end");
|
||||
for (Object *object = static_cast<Object *>(blend->main->objects.first); object != nullptr;
|
||||
object = static_cast<Object *>(object->id.next)) {
|
||||
if (Object *object_eval = DEG_get_evaluated(graph, object)) {
|
||||
BKE_object_where_is_calc_time(
|
||||
graph, DEG_get_evaluated_scene(graph), object_eval, DEG_get_ctime(graph));
|
||||
}
|
||||
}
|
||||
/* The reduced RNA registry cannot always expose transform dependencies to the graph
|
||||
* builder. Re-run the time update after the explicit transform pass so constraints consume
|
||||
* animated target matrices in the same frame. */
|
||||
BKE_scene_graph_update_for_newframe(graph);
|
||||
for (Object *object = static_cast<Object *>(blend->main->objects.first); object != nullptr;
|
||||
object = static_cast<Object *>(object->id.next)) {
|
||||
if (Object *object_eval = DEG_get_evaluated(graph, object)) {
|
||||
BKE_object_where_is_calc_time(
|
||||
graph, DEG_get_evaluated_scene(graph), object_eval, DEG_get_ctime(graph));
|
||||
}
|
||||
}
|
||||
size_t object_count = 0;
|
||||
size_t mesh_object_count = 0;
|
||||
json evaluated_meshes = json::array();
|
||||
json evaluated_non_mesh_geometries = json::array();
|
||||
json evaluated_objects = json::array();
|
||||
json evaluated_armatures = json::array();
|
||||
for (Object *object = static_cast<Object *>(blend->main->objects.first); object != nullptr;
|
||||
object = static_cast<Object *>(object->id.next)) {
|
||||
object_count++;
|
||||
Object *object_eval = DEG_get_evaluated(graph, object);
|
||||
evaluated_objects.push_back({{"objectId", scene_ir_id("object", object->id)},
|
||||
{"type", object_type_name(*object)},
|
||||
{"modifierCount", BLI_listbase_count(&object->modifiers)},
|
||||
{"modifiers", evaluated_modifier_report(*object, object_eval)}});
|
||||
if (object->type == OB_MESH) {
|
||||
mesh_object_count++;
|
||||
const json mesh = evaluated_mesh_report(graph, object);
|
||||
if (!mesh.is_null()) evaluated_meshes.push_back(mesh);
|
||||
}
|
||||
else if (object->type == OB_ARMATURE) {
|
||||
const json armature = evaluated_armature_report(graph, object);
|
||||
if (!armature.is_null()) evaluated_armatures.push_back(armature);
|
||||
}
|
||||
else if (object->type == OB_CURVES_LEGACY || object->type == OB_SURF ||
|
||||
object->type == OB_FONT || object->type == OB_MBALL)
|
||||
{
|
||||
const json geometry = evaluated_non_mesh_report(graph, object);
|
||||
if (!geometry.is_null()) evaluated_non_mesh_geometries.push_back(geometry);
|
||||
}
|
||||
}
|
||||
result.report = json{{"engine", "BlenderDepsgraph"},
|
||||
{"status", "EVALUATED"},
|
||||
{"scene", scene->id.name + 2},
|
||||
{"viewLayer", view_layer->name},
|
||||
{"frame", scene->r.cfra},
|
||||
{"objectCount", object_count},
|
||||
{"meshObjectCount", mesh_object_count},
|
||||
{"objects", evaluated_objects},
|
||||
{"meshes", evaluated_meshes},
|
||||
{"nonMeshGeometries", evaluated_non_mesh_geometries},
|
||||
{"armatures", evaluated_armatures}}
|
||||
.dump();
|
||||
result.ok = true;
|
||||
DEG_graph_free(graph);
|
||||
WEB_DEPSGRAPH_TRACE("graph-free-end");
|
||||
}
|
||||
}
|
||||
WEB_DEPSGRAPH_TRACE("global-main-restore-end");
|
||||
BLO_blendfiledata_free(blend);
|
||||
WEB_DEPSGRAPH_TRACE("blend-free-end");
|
||||
return result;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct WebDepsgraphEvaluationResult {
|
||||
bool ok = false;
|
||||
std::string error;
|
||||
std::string report;
|
||||
};
|
||||
|
||||
void web_engine_blender_runtime_initialize();
|
||||
|
||||
WebDepsgraphEvaluationResult web_engine_evaluate_blender_depsgraph(const uint8_t *data,
|
||||
uint32_t length,
|
||||
int frame);
|
||||
@@ -0,0 +1,414 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/*
|
||||
* Headless data-engine boundary.
|
||||
*
|
||||
* Blender's datablock and dependency-graph code intentionally keeps a few
|
||||
* references to desktop systems. The browser build renders with Three.js and
|
||||
* therefore has no GPU context, render engine, window manager, sequencer
|
||||
* playback thread, or geometry-node editor. These narrowly-scoped fallbacks
|
||||
* make those boundaries explicit; they never fabricate evaluated geometry.
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
#include "BKE_image.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_sequence_types.h"
|
||||
#include "ED_asset_shelf.hh"
|
||||
#include "ED_viewer_path.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
#include "NOD_compositor_nodes_srna.hh"
|
||||
#include "NOD_geometry_nodes_gizmos.hh"
|
||||
#include "NOD_geometry_nodes_lazy_function.hh"
|
||||
#include "NOD_geometry_nodes_srna.hh"
|
||||
#include "NOD_shader.h"
|
||||
#include "NOD_sync_sockets.hh"
|
||||
#include "NOD_texture.h"
|
||||
#include "RE_pipeline.h"
|
||||
#include "RE_texture.h"
|
||||
#include "rna_internal.hh"
|
||||
#include "SEQ_relations.hh"
|
||||
#include "SEQ_modifier.hh"
|
||||
#include "SEQ_sequencer.hh"
|
||||
#include "SEQ_iterator.hh"
|
||||
#include "WM_api.hh"
|
||||
|
||||
extern "C" unsigned long build_commit_timestamp = 0;
|
||||
extern "C" char build_hash[16] = "unknown";
|
||||
|
||||
namespace blender {
|
||||
|
||||
bke::bNodeTreeType *ntreeType_Geometry;
|
||||
|
||||
static void web_geometry_node_tree_update(bNodeTree *ntree)
|
||||
{
|
||||
bke::node_tree_set_output(*ntree);
|
||||
}
|
||||
|
||||
static bool web_geometry_node_tree_validate_link(const eNodeSocketDatatype type_a,
|
||||
const eNodeSocketDatatype type_b)
|
||||
{
|
||||
return type_a == type_b;
|
||||
}
|
||||
|
||||
static void register_web_geometry_node_types()
|
||||
{
|
||||
bke::bNodeTreeType *tree_type = ntreeType_Geometry = MEM_new<bke::bNodeTreeType>(__func__);
|
||||
tree_type->type = NTREE_GEOMETRY;
|
||||
tree_type->idname = "GeometryNodeTree"_ustr;
|
||||
tree_type->group_idname = "GeometryNodeGroup"_ustr;
|
||||
tree_type->update = web_geometry_node_tree_update;
|
||||
tree_type->valid_socket_type = [](bke::bNodeTreeType *, bke::bNodeSocketType *) { return true; };
|
||||
tree_type->validate_link = web_geometry_node_tree_validate_link;
|
||||
bke::node_tree_type_add(*tree_type);
|
||||
|
||||
}
|
||||
|
||||
void WEB_headless_node_system_init()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
bke::NodeTreeTypeUndefined.type = NTREE_UNDEFINED;
|
||||
bke::NodeTreeTypeUndefined.idname = "NodeTreeUndefined"_ustr;
|
||||
bke::NodeTypeUndefined.idname = "NodeUndefined"_ustr;
|
||||
bke::NodeSocketTypeUndefined.idname = "NodeSocketUndefined"_ustr;
|
||||
|
||||
static bke::bNodeTreeType compositor_type;
|
||||
static bke::bNodeTreeType texture_type;
|
||||
compositor_type.type = NTREE_COMPOSIT;
|
||||
compositor_type.idname = "CompositorNodeTree"_ustr;
|
||||
texture_type.type = NTREE_TEXTURE;
|
||||
texture_type.idname = "TextureNodeTree"_ustr;
|
||||
bke::node_tree_type_add(compositor_type);
|
||||
bke::node_tree_type_add(texture_type);
|
||||
|
||||
static bke::bNodeTreeType shader_type;
|
||||
shader_type.type = NTREE_SHADER;
|
||||
shader_type.idname = "ShaderNodeTree"_ustr;
|
||||
bke::node_tree_type_add(shader_type);
|
||||
ntreeType_Shader = &shader_type;
|
||||
|
||||
register_web_geometry_node_types();
|
||||
}
|
||||
|
||||
bool rna_property_override_apply_default(Main * /*bmain*/,
|
||||
RNAPropertyOverrideApplyContext & /*context*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---------------------------- GPU / Render ---------------------------- */
|
||||
|
||||
GPUContext *GPU_context_active_get()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GPU_memory_barrier(const GPUBarrier /*barrier*/) {}
|
||||
|
||||
void *GPU_texture_read(gpu::Texture * /*texture*/, eGPUDataFormat /*data_format*/, int /*mip_level*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GPU_texture_read(gpu::Texture * /*texture*/,
|
||||
eGPUDataFormat /*data_format*/,
|
||||
int /*mip_level*/,
|
||||
void * /*dst*/)
|
||||
{
|
||||
}
|
||||
|
||||
namespace gpu {
|
||||
void GPU_activate_secondary_context(const GPUSecondaryContextData & /*data*/) {}
|
||||
void GPU_deactivate_secondary_context(const GPUSecondaryContextData & /*data*/) {}
|
||||
} // namespace gpu
|
||||
|
||||
void GPU_material_free(ListBaseT<LinkData> * /*gpumaterial*/) {}
|
||||
|
||||
void BKE_image_free_gputextures(Image * /*image*/) {}
|
||||
|
||||
Render *RE_GetSceneRender(const Scene * /*scene*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void RE_FreeRenderResult(RenderResult * /*result*/) {}
|
||||
void RE_AcquireResultImage(Render * /*render*/, RenderResult * /*result*/, int /*view_id*/) {}
|
||||
void RE_ReleaseResultImage(Render * /*render*/) {}
|
||||
|
||||
bool RE_RenderResult_is_stereo(const RenderResult * /*result*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RE_HasCombinedLayer(const RenderResult * /*result*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
RenderResult *RE_MultilayerConvert(ExrHandle * /*exr_handle*/,
|
||||
const char * /*colorspace*/,
|
||||
bool /*predivide*/,
|
||||
int /*width*/,
|
||||
int /*height*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RenderEngineType *RE_engines_find(const char * /*idname*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool RE_texture_evaluate(const MTex * /*mtex*/,
|
||||
const float /*vec*/[3],
|
||||
int /*thread*/,
|
||||
ImagePool * /*pool*/,
|
||||
bool /*skip_load_image*/,
|
||||
bool /*texnode_preview*/,
|
||||
float *r_intensity,
|
||||
float r_rgba[4])
|
||||
{
|
||||
if (r_intensity) {
|
||||
*r_intensity = 0.0f;
|
||||
}
|
||||
if (r_rgba) {
|
||||
r_rgba[0] = r_rgba[1] = r_rgba[2] = 0.0f;
|
||||
r_rgba[3] = 1.0f;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float texture_value_blend(float /*tex*/, float out, float /*fact*/, float /*facg*/, int /*blendtype*/)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
int multitex_ext(Tex * /*tex*/,
|
||||
const float /*texvec*/[3],
|
||||
TexResult *texres,
|
||||
short /*thread*/,
|
||||
ImagePool * /*pool*/,
|
||||
bool /*scene_color_manage*/,
|
||||
bool /*skip_load_image*/)
|
||||
{
|
||||
if (texres) {
|
||||
texres->tin = 0.0f;
|
||||
texres->trgba[0] = texres->trgba[1] = texres->trgba[2] = 0.0f;
|
||||
texres->trgba[3] = 1.0f;
|
||||
texres->talpha = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int multitex_ext_safe(Tex *tex,
|
||||
const float texvec[3],
|
||||
TexResult *texres,
|
||||
ImagePool *pool,
|
||||
bool scene_color_manage,
|
||||
bool skip_load_image)
|
||||
{
|
||||
return multitex_ext(tex, texvec, texres, 0, pool, scene_color_manage, skip_load_image);
|
||||
}
|
||||
|
||||
/* --------------------------- Sequencer data --------------------------- */
|
||||
|
||||
Editing::Editing() = default;
|
||||
Editing::~Editing() = default;
|
||||
|
||||
bool StripModifierData::is_type_sound() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
namespace seq {
|
||||
|
||||
void compositor_nodes_update_interface(Scene & /*sequencer_scene*/,
|
||||
SequencerCompositorModifierData & /*cmd*/)
|
||||
{
|
||||
}
|
||||
|
||||
SequencerToolSettings *tool_settings_init()
|
||||
{
|
||||
return MEM_new<SequencerToolSettings>(__func__);
|
||||
}
|
||||
|
||||
SequencerToolSettings *tool_settings_copy(SequencerToolSettings *settings)
|
||||
{
|
||||
if (!settings) {
|
||||
return nullptr;
|
||||
}
|
||||
return MEM_dupalloc(settings);
|
||||
}
|
||||
|
||||
void tool_settings_free(SequencerToolSettings *settings)
|
||||
{
|
||||
MEM_delete(settings);
|
||||
}
|
||||
|
||||
Editing *editing_get(const Scene *scene)
|
||||
{
|
||||
return scene ? scene->ed : nullptr;
|
||||
}
|
||||
|
||||
void editing_free(Scene *scene, bool /*do_id_user*/)
|
||||
{
|
||||
if (scene && scene->ed) {
|
||||
MEM_delete(scene->ed);
|
||||
scene->ed = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_valid_strip_channel(const Strip *strip)
|
||||
{
|
||||
return strip == nullptr || strip->channel >= 1;
|
||||
}
|
||||
|
||||
void seqbase_duplicate_recursive(Main * /*bmain*/,
|
||||
const Scene * /*scene_src*/,
|
||||
Scene * /*scene_dst*/,
|
||||
ListBaseT<Strip> * /*seqbase_dst*/,
|
||||
const ListBaseT<Strip> * /*seqbase_src*/,
|
||||
StripDuplicate /*dupe_flag*/,
|
||||
int /*copy_flag*/)
|
||||
{
|
||||
}
|
||||
|
||||
void blend_write(BlendWriter * /*writer*/, ListBaseT<Strip> * /*seqbase*/) {}
|
||||
void blend_read(BlendDataReader * /*reader*/, ListBaseT<Strip> * /*seqbase*/) {}
|
||||
void eval_strips(Depsgraph * /*depsgraph*/, Scene * /*scene*/, ListBaseT<Strip> * /*seqbase*/) {}
|
||||
|
||||
void foreach_strip(ListBaseT<Strip> *seqbase, ForEachFunc callback, void *user_data)
|
||||
{
|
||||
if (!seqbase || !callback) {
|
||||
return;
|
||||
}
|
||||
for (Strip *strip = static_cast<Strip *>(seqbase->first); strip;) {
|
||||
Strip *next = strip->next;
|
||||
if (!callback(strip, user_data)) {
|
||||
break;
|
||||
}
|
||||
strip = next;
|
||||
}
|
||||
}
|
||||
|
||||
void foreach_strip(ListBaseT<Strip> *seqbase, FunctionRef<bool(Strip *)> callback)
|
||||
{
|
||||
if (!seqbase) {
|
||||
return;
|
||||
}
|
||||
for (Strip *strip = static_cast<Strip *>(seqbase->first); strip;) {
|
||||
Strip *next = strip->next;
|
||||
if (!callback(strip)) {
|
||||
break;
|
||||
}
|
||||
strip = next;
|
||||
}
|
||||
}
|
||||
|
||||
VectorSet<Strip *> query_all_strips_recursive(const ListBaseT<Strip> *seqbase)
|
||||
{
|
||||
VectorSet<Strip *> result;
|
||||
if (!seqbase) {
|
||||
return result;
|
||||
}
|
||||
for (Strip *strip = static_cast<Strip *>(seqbase->first); strip; strip = strip->next) {
|
||||
result.add(strip);
|
||||
result.add_multiple(query_all_strips_recursive(&strip->seqbase));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void relations_check_uids_unique_and_report(const Scene * /*scene*/) {}
|
||||
|
||||
} // namespace seq
|
||||
|
||||
/* ----------------------- Nodes / editor boundaries ------------------- */
|
||||
|
||||
namespace nodes {
|
||||
|
||||
const std::shared_ptr<const GeometryNodesLazyFunctionGraphInfo> &
|
||||
ensure_geometry_nodes_lazy_function_graph(const bNodeTree & /*tree*/)
|
||||
{
|
||||
static const std::shared_ptr<const GeometryNodesLazyFunctionGraphInfo> empty;
|
||||
return empty;
|
||||
}
|
||||
|
||||
std::shared_ptr<GeneratedTreeSrnaData> create_geometry_nodes_rna_for_modifier(
|
||||
const bNodeTree & /*tree*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
std::shared_ptr<GeneratedTreeSrnaData> create_compositor_nodes_rna_for_strip_modifier(
|
||||
const bNodeTree & /*tree*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
void node_can_sync_cache_clear(Main & /*bmain*/) {}
|
||||
|
||||
namespace gizmos {
|
||||
bool update_tree_gizmo_propagation(bNodeTree & /*tree*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
bool is_builtin_gizmo_node(const bNode & /*node*/)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} // namespace gizmos
|
||||
|
||||
} // namespace nodes
|
||||
|
||||
namespace bke {
|
||||
bNode *node_get_active_paint_canvas(bNodeTree & /*tree*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace bke
|
||||
|
||||
void ntreeTexCheckCyclics(bNodeTree * /*tree*/) {}
|
||||
void ntreeShaderEndExecTree(bNodeTreeExec * /*exec*/) {}
|
||||
void ntreeTexEndExecTree(bNodeTreeExec * /*exec*/) {}
|
||||
bke::bNodeTreeType *ntreeType_Shader = nullptr;
|
||||
|
||||
void MOD_nodes_update_interface(Object * /*object*/, NodesModifierData * /*modifier*/) {}
|
||||
|
||||
} // namespace blender
|
||||
|
||||
namespace blender::ed::asset::shelf {
|
||||
void region_blend_read_data(BlendDataReader * /*reader*/, ARegion * /*region*/) {}
|
||||
} // namespace blender::ed::asset::shelf
|
||||
|
||||
namespace blender::ed::viewer_path {
|
||||
std::optional<ViewerPathForGeometryNodesViewer> parse_geometry_nodes_viewer(
|
||||
const ViewerPath & /*viewer_path*/)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const ComputeContext *compute_context_for_viewer_path_elem(
|
||||
const ViewerPathElem & /*elem*/,
|
||||
bke::ComputeContextCache & /*cache*/,
|
||||
const ComputeContext * /*parent_context*/)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace blender::ed::viewer_path
|
||||
|
||||
namespace blender {
|
||||
void WM_main_add_notifier(unsigned int /*type*/, void * /*reference*/) {}
|
||||
} // namespace blender
|
||||
4113
blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp
Normal file
4113
blender-5.2.0/source/blender/web_engine/web_engine_main_state.cpp
Normal file
File diff suppressed because it is too large
Load Diff
402
blender-5.2.0/source/blender/web_engine/web_engine_main_state.h
Normal file
402
blender-5.2.0/source/blender/web_engine/web_engine_main_state.h
Normal file
@@ -0,0 +1,402 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct WebBlendMainState;
|
||||
|
||||
struct WebMetaballElementEdit {
|
||||
int type = 0;
|
||||
std::array<float, 3> position = {0.0f, 0.0f, 0.0f};
|
||||
float radius = 1.0f;
|
||||
std::array<float, 3> scale = {1.0f, 1.0f, 1.0f};
|
||||
};
|
||||
|
||||
struct WebCurveTopologyEdit {
|
||||
std::vector<int> spline_types;
|
||||
std::vector<bool> cyclic_u;
|
||||
std::vector<bool> cyclic_v;
|
||||
std::vector<int> handle_types;
|
||||
std::vector<float> handle_points;
|
||||
};
|
||||
|
||||
struct WebCurveSplineEdit {
|
||||
int type = 0;
|
||||
int point_count = 0;
|
||||
int order_u = 0;
|
||||
bool cyclic_u = false;
|
||||
};
|
||||
|
||||
struct WebSurfaceSplineEdit {
|
||||
int u = 0;
|
||||
int v = 0;
|
||||
int order_u = 0;
|
||||
int order_v = 0;
|
||||
bool cyclic_u = false;
|
||||
bool cyclic_v = false;
|
||||
};
|
||||
|
||||
struct WebFontPropertiesEdit {
|
||||
std::string alignment;
|
||||
std::string align_y;
|
||||
float extrude = -1.0f;
|
||||
float bevel_depth = -1.0f;
|
||||
int bevel_resolution = -1;
|
||||
float offset = -1.0f;
|
||||
float spacing = -1.0f;
|
||||
float line_distance = -1.0f;
|
||||
float word_space = -1.0f;
|
||||
float shear = -11.0f;
|
||||
float font_size = -1.0f;
|
||||
float offset_x = -100001.0f;
|
||||
float offset_y = -100001.0f;
|
||||
};
|
||||
|
||||
struct WebFontCharacterEdit {
|
||||
float kern = 0.0f;
|
||||
int material_index = 0;
|
||||
int style_flags = 0;
|
||||
};
|
||||
|
||||
struct WebFontTextBoxEdit {
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
};
|
||||
|
||||
struct WebGreasePencilPointEdit {
|
||||
std::array<float, 3> position = {0.0f, 0.0f, 0.0f};
|
||||
float radius = 0.01f;
|
||||
float opacity = 1.0f;
|
||||
std::array<float, 4> vertex_color = {0.0f, 0.0f, 0.0f, 0.0f};
|
||||
};
|
||||
|
||||
struct WebGreasePencilStrokeEdit {
|
||||
bool cyclic = false;
|
||||
int material_index = 0;
|
||||
std::vector<WebGreasePencilPointEdit> points;
|
||||
};
|
||||
|
||||
void web_engine_blender_runtime_initialize();
|
||||
|
||||
WebBlendMainState *web_engine_blend_main_open(const uint8_t *data,
|
||||
uint32_t length,
|
||||
std::string &error);
|
||||
void web_engine_blend_main_free(WebBlendMainState *state);
|
||||
|
||||
bool web_engine_blend_main_set_frame(WebBlendMainState *state, int frame, std::string &error);
|
||||
bool web_engine_blend_main_set_visibility(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
bool visible,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_modifier_enabled(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *modifier_uuid,
|
||||
bool enabled,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_modifier_visibility(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *modifier_uuid,
|
||||
bool show_viewport,
|
||||
bool show_render,
|
||||
bool show_edit_mode,
|
||||
bool show_on_cage,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_replace_mesh(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const std::vector<float> &positions,
|
||||
const std::vector<uint32_t> &indices,
|
||||
const std::vector<float> &uvs,
|
||||
const std::vector<float> &colors,
|
||||
const std::vector<uint32_t> &material_indices,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_create_primitive(WebBlendMainState *state,
|
||||
const char *primitive,
|
||||
const char *name,
|
||||
const std::vector<float> &location,
|
||||
std::string &object_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_duplicate_object(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const std::vector<float> &offset,
|
||||
bool linked,
|
||||
std::string &new_object_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_delete_object(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_transform(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const std::vector<float> &translation,
|
||||
const std::vector<float> &rotation_euler,
|
||||
const std::vector<float> &scale,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_translate_vertices(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const std::vector<uint32_t> &vertex_indices,
|
||||
const std::vector<float> &offset,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_sculpt_attributes(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const std::vector<float> &mask,
|
||||
const std::vector<uint32_t> &face_sets,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_sculpt_stroke(WebBlendMainState *state,
|
||||
const char *stroke_json,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_mesh_edit(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *operation,
|
||||
const char *selection_mode,
|
||||
const std::vector<uint32_t> &element_indices,
|
||||
const std::vector<float> &offset,
|
||||
float amount,
|
||||
int segments,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_create_uv_map(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *name,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_active_uv_map(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *name,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_unwrap_uv(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const std::vector<uint32_t> &face_indices,
|
||||
const char *method,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_add_material_slot(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *name,
|
||||
std::string &material_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_remove_material_slot(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
int slot_index,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_assign_material_faces(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const std::vector<uint32_t> &face_indices,
|
||||
int slot_index,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_material_principled(WebBlendMainState *state,
|
||||
const char *material_id,
|
||||
const std::vector<float> &base_color,
|
||||
float roughness,
|
||||
float metallic,
|
||||
const std::vector<float> &emission_color,
|
||||
float alpha,
|
||||
float ior,
|
||||
float specular_ior_level,
|
||||
float transmission_weight,
|
||||
float coat_weight,
|
||||
float coat_roughness,
|
||||
float emission_strength,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_import_image(WebBlendMainState *state,
|
||||
const char *name,
|
||||
const char *mime_type,
|
||||
int width,
|
||||
int height,
|
||||
const char *base64,
|
||||
std::string &image_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_material_image(WebBlendMainState *state,
|
||||
const char *material_id,
|
||||
const char *image_id,
|
||||
const char *usage,
|
||||
const char *uv_map,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_shader_graph(WebBlendMainState *state,
|
||||
const char *material_id,
|
||||
const char *graph_json,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_insert_keyframe(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
int frame,
|
||||
const char *property,
|
||||
const char *interpolation,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_delete_keyframe(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
int frame,
|
||||
const char *property,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_fcurve_interpolation(WebBlendMainState *state,
|
||||
const char *animation_id,
|
||||
const char *path,
|
||||
const char *interpolation,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_active_action(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *action_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_nla_stack(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *tracks_json,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_constraint(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *constraint_name,
|
||||
bool enabled,
|
||||
float influence,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_parent(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *parent_id,
|
||||
bool keep_transform,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_create_collection(WebBlendMainState *state,
|
||||
const char *name,
|
||||
const char *parent_collection_id,
|
||||
std::string &collection_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_move_to_collection(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *collection_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_rename_id(WebBlendMainState *state,
|
||||
const char *id,
|
||||
const char *name,
|
||||
std::string &renamed_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_join_objects(WebBlendMainState *state,
|
||||
const char *active_object_id,
|
||||
const std::vector<std::string> &object_ids,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_separate_faces(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const std::vector<uint32_t> &face_indices,
|
||||
const char *name,
|
||||
std::string &new_object_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_apply_transform(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_origin(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *mode,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_create_curve(WebBlendMainState *state,
|
||||
const char *curve_type,
|
||||
const char *name,
|
||||
const std::vector<float> &control_points,
|
||||
bool cyclic,
|
||||
int resolution,
|
||||
std::string &object_id,
|
||||
std::string &data_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_delete_non_mesh_data(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_curve_control_points(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<float> &control_points,
|
||||
const std::vector<uint32_t> &spline_offsets,
|
||||
int resolution,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_curve_handle(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
uint32_t point_index,
|
||||
bool right_handle,
|
||||
const std::vector<float> &position,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_curve_topology(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const WebCurveTopologyEdit &edit,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_curve_splines(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebCurveSplineEdit> &splines,
|
||||
const std::vector<float> &control_points,
|
||||
const std::vector<float> &point_weights,
|
||||
const std::vector<int> &handle_types,
|
||||
const std::vector<float> &handle_points,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_surface_topology(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebSurfaceSplineEdit> &splines,
|
||||
const std::vector<float> &control_points,
|
||||
const std::vector<float> &point_weights,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_font_body(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *body,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_font_properties(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const WebFontPropertiesEdit &properties,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_font_advanced(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebFontCharacterEdit> &characters,
|
||||
const std::vector<WebFontTextBoxEdit> &text_boxes,
|
||||
int active_text_box,
|
||||
std::string &error);
|
||||
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_metaball_elements(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const std::vector<WebMetaballElementEdit> &elements,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_grease_pencils_json(WebBlendMainState *state,
|
||||
std::string &grease_pencils_json,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_create_grease_pencil_layer(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *name,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_remove_grease_pencil_layer(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *layer_id,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_move_grease_pencil_layer(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *layer_id,
|
||||
const char *direction,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_insert_grease_pencil_frame(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *layer_id,
|
||||
int frame,
|
||||
int duration,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_remove_grease_pencil_frame(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *layer_id,
|
||||
int frame,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_grease_pencil_strokes(WebBlendMainState *state,
|
||||
const char *data_id,
|
||||
const char *layer_id,
|
||||
int frame,
|
||||
const std::vector<WebGreasePencilStrokeEdit> &strokes,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_vertex_colors(WebBlendMainState *state,
|
||||
const char *mesh_id,
|
||||
const char *attribute_name,
|
||||
const char *domain,
|
||||
const std::vector<uint32_t> &indices,
|
||||
const std::vector<float> &colors,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_vertex_weights(WebBlendMainState *state,
|
||||
const char *object_id,
|
||||
const char *vertex_group,
|
||||
const std::vector<uint32_t> &indices,
|
||||
const std::vector<float> &values,
|
||||
bool normalize,
|
||||
bool mirror,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_set_render_properties(WebBlendMainState *state,
|
||||
const char *target_id,
|
||||
const char *properties_json,
|
||||
std::string &error);
|
||||
bool web_engine_blend_main_write(WebBlendMainState *state,
|
||||
std::vector<uint8_t> &bytes,
|
||||
std::string &error);
|
||||
44
blender-5.2.0/source/blender/web_engine/web_gpu_fallback.cpp
Normal file
44
blender-5.2.0/source/blender/web_engine/web_gpu_fallback.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* Web data-engine GPU boundary.
|
||||
*
|
||||
* Blender's datablock code keeps a few GPU cleanup and material inspection
|
||||
* calls even when the browser build delegates rendering to Three.js. No GPU
|
||||
* objects are created by this target, so these functions deliberately provide
|
||||
* the lifetime boundary without pulling a desktop backend into the module.
|
||||
*/
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_material.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
GPUBackendType GPU_backend_get_type()
|
||||
{
|
||||
return GPU_BACKEND_NONE;
|
||||
}
|
||||
|
||||
int GPU_max_compute_shader_storage_blocks()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GPU_batch_discard(gpu::Batch * /*batch*/) {}
|
||||
|
||||
void GPU_vertbuf_discard(gpu::VertBuf * /*verts*/) {}
|
||||
|
||||
void GPU_texture_free(gpu::Texture * /*texture*/) {}
|
||||
|
||||
ListBaseT<GPUMaterialTexture> GPU_material_textures(GPUMaterial * /*material*/)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
507
blender-5.2.0/source/blender/web_engine/web_rna_fallback.cpp
Normal file
507
blender-5.2.0/source/blender/web_engine/web_rna_fallback.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_action_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_listbase_iterator.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "rna_internal.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
StructRNA *RNA_Struct = nullptr;
|
||||
StructRNA *RNA_Property = nullptr;
|
||||
|
||||
extern StructRNA *RNA_ID;
|
||||
extern StructRNA *RNA_Action;
|
||||
extern StructRNA *RNA_Armature;
|
||||
extern StructRNA *RNA_Constraint;
|
||||
extern StructRNA *RNA_NlaTrack;
|
||||
extern StructRNA *RNA_CameraBackgroundImage;
|
||||
extern StructRNA *RNA_Object;
|
||||
extern StructRNA *RNA_PoseBone;
|
||||
extern StructRNA *RNA_Scene;
|
||||
|
||||
static StructRNA web_rna_struct;
|
||||
static StructRNA web_rna_property;
|
||||
static StructRNA web_rna_id;
|
||||
static StructRNA web_rna_action;
|
||||
static StructRNA web_rna_armature;
|
||||
static StructRNA web_rna_constraint;
|
||||
static StructRNA web_rna_object;
|
||||
static StructRNA web_rna_pose_bone;
|
||||
static StructRNA web_rna_scene;
|
||||
|
||||
static FloatPropertyRNA web_object_location;
|
||||
static FloatPropertyRNA web_object_rotation_euler;
|
||||
static FloatPropertyRNA web_object_scale;
|
||||
static FloatPropertyRNA web_pose_rotation_euler;
|
||||
static BoolPropertyRNA web_object_hide_viewport;
|
||||
static BoolPropertyRNA web_object_hide_render;
|
||||
|
||||
static float *web_object_float_vector(PointerRNA *ptr, const FloatPropertyRNA *prop)
|
||||
{
|
||||
Object *object = static_cast<Object *>(ptr->data);
|
||||
if (prop == &web_object_location) {
|
||||
return object->loc;
|
||||
}
|
||||
if (prop == &web_object_rotation_euler) {
|
||||
return object->rot;
|
||||
}
|
||||
return object->scale;
|
||||
}
|
||||
|
||||
static void web_object_location_get(PointerRNA *ptr, float values[3])
|
||||
{
|
||||
memcpy(values, web_object_float_vector(ptr, &web_object_location), sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_object_location_set(PointerRNA *ptr, const float values[3])
|
||||
{
|
||||
memcpy(web_object_float_vector(ptr, &web_object_location), values, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_object_rotation_get(PointerRNA *ptr, float values[3])
|
||||
{
|
||||
memcpy(values, web_object_float_vector(ptr, &web_object_rotation_euler), sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_object_rotation_set(PointerRNA *ptr, const float values[3])
|
||||
{
|
||||
memcpy(web_object_float_vector(ptr, &web_object_rotation_euler), values, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_object_scale_get(PointerRNA *ptr, float values[3])
|
||||
{
|
||||
memcpy(values, web_object_float_vector(ptr, &web_object_scale), sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_object_scale_set(PointerRNA *ptr, const float values[3])
|
||||
{
|
||||
memcpy(web_object_float_vector(ptr, &web_object_scale), values, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_pose_rotation_get(PointerRNA *ptr, float values[3])
|
||||
{
|
||||
memcpy(values, static_cast<bPoseChannel *>(ptr->data)->eul, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static void web_pose_rotation_set(PointerRNA *ptr, const float values[3])
|
||||
{
|
||||
memcpy(static_cast<bPoseChannel *>(ptr->data)->eul, values, sizeof(float) * 3);
|
||||
}
|
||||
|
||||
static bool web_object_hide_viewport_get(PointerRNA *ptr)
|
||||
{
|
||||
return (static_cast<Object *>(ptr->data)->visibility_flag & OB_HIDE_VIEWPORT) != 0;
|
||||
}
|
||||
|
||||
static void web_object_hide_viewport_set(PointerRNA *ptr, bool value)
|
||||
{
|
||||
Object *object = static_cast<Object *>(ptr->data);
|
||||
if (value) {
|
||||
object->visibility_flag |= OB_HIDE_VIEWPORT;
|
||||
}
|
||||
else {
|
||||
object->visibility_flag &= ~OB_HIDE_VIEWPORT;
|
||||
}
|
||||
}
|
||||
|
||||
static bool web_object_hide_render_get(PointerRNA *ptr)
|
||||
{
|
||||
return (static_cast<Object *>(ptr->data)->visibility_flag & OB_HIDE_RENDER) != 0;
|
||||
}
|
||||
|
||||
static void web_object_hide_render_set(PointerRNA *ptr, bool value)
|
||||
{
|
||||
Object *object = static_cast<Object *>(ptr->data);
|
||||
if (value) {
|
||||
object->visibility_flag |= OB_HIDE_RENDER;
|
||||
}
|
||||
else {
|
||||
object->visibility_flag &= ~OB_HIDE_RENDER;
|
||||
}
|
||||
}
|
||||
|
||||
static void web_init_float_property(FloatPropertyRNA &prop,
|
||||
const char *identifier,
|
||||
PropertySubType subtype,
|
||||
PropFloatArrayGetFunc getarray,
|
||||
PropFloatArraySetFunc setarray)
|
||||
{
|
||||
prop = {};
|
||||
prop.magic = RNA_MAGIC;
|
||||
prop.identifier = identifier;
|
||||
prop.flag = PROP_ANIMATABLE | PROP_EDITABLE;
|
||||
prop.type = PROP_FLOAT;
|
||||
prop.subtype = subtype;
|
||||
prop.softmin = std::numeric_limits<float>::lowest();
|
||||
prop.softmax = std::numeric_limits<float>::max();
|
||||
prop.hardmin = std::numeric_limits<float>::lowest();
|
||||
prop.hardmax = std::numeric_limits<float>::max();
|
||||
prop.step = 0.1f;
|
||||
prop.precision = 3;
|
||||
prop.arraydimension = 1;
|
||||
prop.arraylength[0] = 3;
|
||||
prop.totarraylength = 3;
|
||||
prop.getarray = getarray;
|
||||
prop.setarray = setarray;
|
||||
}
|
||||
|
||||
static void web_init_bool_property(BoolPropertyRNA &prop,
|
||||
const char *identifier,
|
||||
PropBooleanGetFunc get,
|
||||
PropBooleanSetFunc set)
|
||||
{
|
||||
prop = {};
|
||||
prop.magic = RNA_MAGIC;
|
||||
prop.identifier = identifier;
|
||||
prop.flag = PROP_ANIMATABLE | PROP_EDITABLE;
|
||||
prop.type = PROP_BOOLEAN;
|
||||
prop.get = get;
|
||||
prop.set = set;
|
||||
}
|
||||
|
||||
static void web_append_property(StructRNA &type, PropertyRNA &prop)
|
||||
{
|
||||
BLI_addtail(&type.cont.properties, &prop);
|
||||
}
|
||||
|
||||
void web_rna_init()
|
||||
{
|
||||
static bool initialized = false;
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
|
||||
web_rna_struct = {};
|
||||
web_rna_struct.identifier = "Struct";
|
||||
web_rna_property = {};
|
||||
web_rna_property.identifier = "Property";
|
||||
web_rna_id = {};
|
||||
web_rna_id.identifier = "ID";
|
||||
web_rna_id.flag = STRUCT_ID;
|
||||
web_rna_action = {};
|
||||
web_rna_action.identifier = "Action";
|
||||
web_rna_action.flag = STRUCT_ID;
|
||||
web_rna_armature = {};
|
||||
web_rna_armature.identifier = "Armature";
|
||||
web_rna_armature.flag = STRUCT_ID;
|
||||
web_rna_constraint = {};
|
||||
web_rna_constraint.identifier = "Constraint";
|
||||
web_rna_object = {};
|
||||
web_rna_object.identifier = "Object";
|
||||
web_rna_object.flag = STRUCT_ID;
|
||||
web_rna_object.base = &web_rna_id;
|
||||
web_rna_pose_bone = {};
|
||||
web_rna_pose_bone.identifier = "PoseBone";
|
||||
web_rna_scene = {};
|
||||
web_rna_scene.identifier = "Scene";
|
||||
web_rna_scene.flag = STRUCT_ID;
|
||||
web_rna_scene.base = &web_rna_id;
|
||||
|
||||
web_init_float_property(
|
||||
web_object_location, "location", PROP_TRANSLATION, web_object_location_get, web_object_location_set);
|
||||
web_init_float_property(web_object_rotation_euler,
|
||||
"rotation_euler",
|
||||
PROP_EULER,
|
||||
web_object_rotation_get,
|
||||
web_object_rotation_set);
|
||||
web_init_float_property(web_object_scale, "scale", PROP_XYZ, web_object_scale_get, web_object_scale_set);
|
||||
web_init_float_property(
|
||||
web_pose_rotation_euler, "rotation_euler", PROP_EULER, web_pose_rotation_get, web_pose_rotation_set);
|
||||
web_init_bool_property(
|
||||
web_object_hide_viewport, "hide_viewport", web_object_hide_viewport_get, web_object_hide_viewport_set);
|
||||
web_init_bool_property(
|
||||
web_object_hide_render, "hide_render", web_object_hide_render_get, web_object_hide_render_set);
|
||||
|
||||
web_append_property(web_rna_object, web_object_location);
|
||||
web_append_property(web_rna_object, web_object_rotation_euler);
|
||||
web_append_property(web_rna_object, web_object_scale);
|
||||
web_append_property(web_rna_object, web_object_hide_viewport);
|
||||
web_append_property(web_rna_object, web_object_hide_render);
|
||||
web_append_property(web_rna_pose_bone, web_pose_rotation_euler);
|
||||
|
||||
RNA_Struct = &web_rna_struct;
|
||||
RNA_Property = &web_rna_property;
|
||||
RNA_ID = &web_rna_id;
|
||||
RNA_Action = &web_rna_action;
|
||||
RNA_Armature = &web_rna_armature;
|
||||
RNA_Constraint = &web_rna_constraint;
|
||||
RNA_Object = &web_rna_object;
|
||||
RNA_PoseBone = &web_rna_pose_bone;
|
||||
RNA_Scene = &web_rna_scene;
|
||||
}
|
||||
|
||||
StructRNA *web_rna_id_type(const short idcode)
|
||||
{
|
||||
switch (ID_Type(idcode)) {
|
||||
case ID_OB:
|
||||
return &web_rna_object;
|
||||
case ID_AR:
|
||||
return &web_rna_armature;
|
||||
case ID_SCE:
|
||||
return &web_rna_scene;
|
||||
case ID_AC:
|
||||
return &web_rna_action;
|
||||
default:
|
||||
return &web_rna_id;
|
||||
}
|
||||
}
|
||||
|
||||
bool web_rna_path_resolve_property(const PointerRNA *ptr,
|
||||
const char *path,
|
||||
PointerRNA *r_ptr,
|
||||
PropertyRNA **r_prop)
|
||||
{
|
||||
if (!ptr || !ptr->data || !path || !r_ptr || !r_prop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (STREQ(path, "location")) {
|
||||
*r_ptr = *ptr;
|
||||
*r_prop = &web_object_location;
|
||||
return true;
|
||||
}
|
||||
if (STREQ(path, "rotation_euler")) {
|
||||
*r_ptr = *ptr;
|
||||
*r_prop = (ptr->type == &web_rna_pose_bone) ? static_cast<PropertyRNA *>(&web_pose_rotation_euler) : static_cast<PropertyRNA *>(&web_object_rotation_euler);
|
||||
return true;
|
||||
}
|
||||
if (STREQ(path, "scale")) {
|
||||
*r_ptr = *ptr;
|
||||
*r_prop = &web_object_scale;
|
||||
return true;
|
||||
}
|
||||
|
||||
constexpr char prefix[] = "pose.bones[\"";
|
||||
if (ptr->type != &web_rna_object || !STRPREFIX(path, prefix)) {
|
||||
return false;
|
||||
}
|
||||
const char *name_begin = path + sizeof(prefix) - 1;
|
||||
const char *name_end = strstr(name_begin, "\"]");
|
||||
if (!name_end || !STREQ(name_end + 2, ".rotation_euler")) {
|
||||
return false;
|
||||
}
|
||||
const Object *object = static_cast<const Object *>(ptr->data);
|
||||
if (!object->pose) {
|
||||
return false;
|
||||
}
|
||||
for (bPoseChannel &pchan : object->pose->chanbase) {
|
||||
if (size_t(name_end - name_begin) == strlen(pchan.name) &&
|
||||
strncmp(name_begin, pchan.name, size_t(name_end - name_begin)) == 0)
|
||||
{
|
||||
*r_ptr = {ptr->owner_id, &web_rna_pose_bone, &pchan};
|
||||
*r_prop = &web_pose_rotation_euler;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* The Web build intentionally omits rna_rna.cc because its global RNA
|
||||
* registry pulls editor-only schemas into the data-only engine. Generated
|
||||
* RNA structs still point at these builtin callbacks, so keep the small
|
||||
* property iterator implementation here. */
|
||||
static bool web_rna_property_builtin(CollectionPropertyIterator * /*iter*/, void *data)
|
||||
{
|
||||
const PropertyRNA *prop = static_cast<const PropertyRNA *>(data);
|
||||
return (prop->flag_internal & PROP_INTERN_BUILTIN) != 0;
|
||||
}
|
||||
|
||||
static void web_rna_inheritance_next_level_restart(CollectionPropertyIterator *iter,
|
||||
PointerRNA *ptr,
|
||||
IteratorSkipFunc skip)
|
||||
{
|
||||
while (!iter->valid && iter->level > 0) {
|
||||
StructRNA *srna = static_cast<StructRNA *>(iter->parent.data);
|
||||
iter->level--;
|
||||
for (int i = iter->level; i > 0; i--) {
|
||||
srna = srna->base;
|
||||
}
|
||||
|
||||
rna_iterator_listbase_end(iter);
|
||||
rna_iterator_listbase_begin(iter, ptr, &srna->cont.properties, skip);
|
||||
}
|
||||
}
|
||||
|
||||
static void web_rna_struct_properties_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
StructRNA *srna = static_cast<StructRNA *>(ptr->data);
|
||||
while (srna->base) {
|
||||
iter->level++;
|
||||
srna = srna->base;
|
||||
}
|
||||
|
||||
rna_iterator_listbase_begin(
|
||||
iter, ptr, &srna->cont.properties, web_rna_property_builtin);
|
||||
web_rna_inheritance_next_level_restart(iter, ptr, web_rna_property_builtin);
|
||||
}
|
||||
|
||||
static void web_rna_struct_properties_next(CollectionPropertyIterator *iter)
|
||||
{
|
||||
rna_iterator_listbase_next(iter);
|
||||
web_rna_inheritance_next_level_restart(iter, &iter->parent, web_rna_property_builtin);
|
||||
}
|
||||
|
||||
static PointerRNA web_rna_struct_properties_get(CollectionPropertyIterator *iter)
|
||||
{
|
||||
return RNA_pointer_create_discrete(nullptr, RNA_Property, rna_iterator_listbase_get(iter));
|
||||
}
|
||||
|
||||
void rna_builtin_properties_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
|
||||
{
|
||||
PointerRNA newptr;
|
||||
newptr.type = RNA_Struct;
|
||||
newptr.data = ptr->type;
|
||||
newptr.owner_id = (ptr->type->flag & STRUCT_ID) ? static_cast<ID *>(ptr->data) : nullptr;
|
||||
|
||||
iter->parent = newptr;
|
||||
iter->builtin_parent = *ptr;
|
||||
web_rna_struct_properties_begin(iter, &newptr);
|
||||
}
|
||||
|
||||
void rna_builtin_properties_next(CollectionPropertyIterator *iter)
|
||||
{
|
||||
web_rna_struct_properties_next(iter);
|
||||
}
|
||||
|
||||
PointerRNA rna_builtin_properties_get(CollectionPropertyIterator *iter)
|
||||
{
|
||||
return web_rna_struct_properties_get(iter);
|
||||
}
|
||||
|
||||
bool rna_builtin_properties_lookup_string(PointerRNA *ptr, const char *key, PointerRNA *r_ptr)
|
||||
{
|
||||
for (StructRNA *srna = ptr->type; srna != nullptr; srna = srna->base) {
|
||||
for (PropertyRNA *prop = static_cast<PropertyRNA *>(srna->cont.properties.first); prop;
|
||||
prop = prop->next)
|
||||
{
|
||||
if (!(prop->flag_internal & PROP_INTERN_BUILTIN) && STREQ(prop->identifier, key)) {
|
||||
*r_ptr = {nullptr, RNA_Property, prop};
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_ptr = {};
|
||||
return false;
|
||||
}
|
||||
|
||||
PointerRNA rna_builtin_type_get(PointerRNA *ptr)
|
||||
{
|
||||
return RNA_pointer_create_discrete(nullptr, RNA_Struct, ptr->type);
|
||||
}
|
||||
|
||||
/* Versioning code refers to editor-only enum tables. Their values are not
|
||||
* consulted by the model evaluator; retain a valid terminated table so old
|
||||
* files can be loaded without linking the UI RNA schema. */
|
||||
#define WEB_RNA_EMPTY_ENUM(name) \
|
||||
extern const EnumPropertyItem name[] = {{0, nullptr, 0, nullptr, nullptr}}
|
||||
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_asset_access_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_boidrule_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_color_space_convert_default_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_dummy_DEFAULT_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_dummy_NULL_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_fileselect_params_sort_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_region_panel_category_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_region_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_action_mode_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_file_browse_mode_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_graph_mode_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_image_mode_all_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_image_mode_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_sequencer_view_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_space_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_strip_modifier_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_strip_sound_modifier_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_strip_video_modifier_type_items);
|
||||
WEB_RNA_EMPTY_ENUM(rna_enum_property_subtype_items);
|
||||
|
||||
#undef WEB_RNA_EMPTY_ENUM
|
||||
|
||||
/* These type handles belong to excluded UI/media RNA definitions. A null
|
||||
* handle is intentional: the Web evaluator rejects those data domains before
|
||||
* asking RNA to resolve properties on them. */
|
||||
#define WEB_RNA_NULL_TYPE(name) StructRNA *name = nullptr
|
||||
|
||||
WEB_RNA_NULL_TYPE(RNA_Annotation);
|
||||
WEB_RNA_NULL_TYPE(RNA_AnnotationLayer);
|
||||
WEB_RNA_NULL_TYPE(RNA_Area);
|
||||
WEB_RNA_NULL_TYPE(RNA_Brush);
|
||||
WEB_RNA_NULL_TYPE(RNA_CacheFile);
|
||||
WEB_RNA_NULL_TYPE(RNA_Camera);
|
||||
WEB_RNA_NULL_TYPE(RNA_Curves);
|
||||
WEB_RNA_NULL_TYPE(RNA_FreestyleLineStyle);
|
||||
WEB_RNA_NULL_TYPE(RNA_Light);
|
||||
WEB_RNA_NULL_TYPE(RNA_LightProbe);
|
||||
WEB_RNA_NULL_TYPE(RNA_Mask);
|
||||
WEB_RNA_NULL_TYPE(RNA_MetaBall);
|
||||
WEB_RNA_NULL_TYPE(RNA_Modifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_MovieClip);
|
||||
WEB_RNA_NULL_TYPE(RNA_MovieTrackingStabilization);
|
||||
WEB_RNA_NULL_TYPE(RNA_MovieTrackingTrack);
|
||||
WEB_RNA_NULL_TYPE(RNA_NlaStrip);
|
||||
WEB_RNA_NULL_TYPE(RNA_NlaTrack);
|
||||
WEB_RNA_NULL_TYPE(RNA_CameraBackgroundImage);
|
||||
WEB_RNA_NULL_TYPE(RNA_Node);
|
||||
WEB_RNA_NULL_TYPE(RNA_NodeSocket);
|
||||
WEB_RNA_NULL_TYPE(RNA_NodeSocketStandard);
|
||||
WEB_RNA_NULL_TYPE(RNA_NodeTree);
|
||||
WEB_RNA_NULL_TYPE(RNA_NodesModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_PaintCurve);
|
||||
WEB_RNA_NULL_TYPE(RNA_Palette);
|
||||
WEB_RNA_NULL_TYPE(RNA_ParticleSettings);
|
||||
WEB_RNA_NULL_TYPE(RNA_PointCloud);
|
||||
WEB_RNA_NULL_TYPE(RNA_PrimitiveString);
|
||||
WEB_RNA_NULL_TYPE(RNA_Region);
|
||||
WEB_RNA_NULL_TYPE(RNA_CurveModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_ArmatureModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_ArrayModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_BevelModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_BooleanModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_BuildModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_DecimateModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_LatticeModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_MirrorModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_HookModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_MeshDeformModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_ShrinkwrapModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_SimpleDeformModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_SolidifyModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_SubsurfModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_SurfaceDeformModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_TriangulateModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_WaveModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_WeldModifier);
|
||||
WEB_RNA_NULL_TYPE(RNA_Screen);
|
||||
WEB_RNA_NULL_TYPE(RNA_ShaderFx);
|
||||
WEB_RNA_NULL_TYPE(RNA_ShaderNode);
|
||||
WEB_RNA_NULL_TYPE(RNA_Sound);
|
||||
WEB_RNA_NULL_TYPE(RNA_Speaker);
|
||||
WEB_RNA_NULL_TYPE(RNA_Strip);
|
||||
WEB_RNA_NULL_TYPE(RNA_SequencerCompositorModifierProperties);
|
||||
WEB_RNA_NULL_TYPE(RNA_Text);
|
||||
WEB_RNA_NULL_TYPE(RNA_Texture);
|
||||
WEB_RNA_NULL_TYPE(RNA_VectorFont);
|
||||
WEB_RNA_NULL_TYPE(RNA_Volume);
|
||||
WEB_RNA_NULL_TYPE(RNA_WindowManager);
|
||||
WEB_RNA_NULL_TYPE(RNA_WorkSpace);
|
||||
|
||||
#undef WEB_RNA_NULL_TYPE
|
||||
|
||||
void rna_property_override_diff_default(Main * /*bmain*/,
|
||||
RNAPropertyOverrideDiffContext & /*rnadiff_ctx*/)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
Reference in New Issue
Block a user