Add Chromium-only Blender WebEngine parity work
This commit is contained in:
163
blender-5.2.0/source/blender/python/BPY_extern.hh
Normal file
163
blender-5.2.0/source/blender/python/BPY_extern.hh
Normal file
@@ -0,0 +1,163 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup python
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#ifdef WITH_INTERNATIONAL
|
||||
# include "BLI_string_ref.hh"
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ARegionType;
|
||||
struct AnimationEvalContext;
|
||||
struct ChannelDriver; /* DNA_anim_types.h */
|
||||
struct ID; /* DNA_ID.h */
|
||||
struct ListBase; /* DNA_listBase.h */
|
||||
struct Object; /* DNA_object_types.h */
|
||||
struct PathResolvedRNA;
|
||||
struct Text; /* defined in DNA_text_types.h */
|
||||
struct bConstraint; /* DNA_constraint_types.h */
|
||||
struct bConstraintOb; /* DNA_constraint_types.h */
|
||||
struct bConstraintTarget; /* DNA_constraint_types.h */
|
||||
struct bContext;
|
||||
struct bContextDataResult;
|
||||
struct StructRNA;
|
||||
struct wmWindowManager;
|
||||
|
||||
/* global interpreter lock */
|
||||
|
||||
using BPy_ThreadStatePtr = void *;
|
||||
|
||||
/**
|
||||
* Analogue of #PyEval_SaveThread()
|
||||
*/
|
||||
[[nodiscard]] BPy_ThreadStatePtr BPY_thread_save();
|
||||
/**
|
||||
* Analogue of #PyEval_RestoreThread()
|
||||
*/
|
||||
void BPY_thread_restore(BPy_ThreadStatePtr tstate);
|
||||
|
||||
/** Our own wrappers to #Py_BEGIN_ALLOW_THREADS / #Py_END_ALLOW_THREADS */
|
||||
#define BPy_BEGIN_ALLOW_THREADS \
|
||||
{ \
|
||||
BPy_ThreadStatePtr _bpy_saved_tstate = BPY_thread_save(); \
|
||||
(void)0
|
||||
#define BPy_END_ALLOW_THREADS \
|
||||
BPY_thread_restore(_bpy_saved_tstate); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Print the Python backtrace of the current thread state.
|
||||
*
|
||||
* Should be safe to call from anywhere at any point, may not output anything if there is no valid
|
||||
* python thread state available.
|
||||
*/
|
||||
void BPY_thread_backtrace_print();
|
||||
|
||||
void BPY_text_free_code(Text *text);
|
||||
/**
|
||||
* Needed so the #Main pointer in `bpy.data` doesn't become out of date.
|
||||
*/
|
||||
void BPY_modules_update();
|
||||
void BPY_modules_load_user(bContext *C);
|
||||
|
||||
void BPY_app_handlers_reset(bool do_all);
|
||||
|
||||
/**
|
||||
* Run on exit to free any cached data.
|
||||
*/
|
||||
void BPY_driver_exit();
|
||||
|
||||
/**
|
||||
* Update function, it gets rid of python-drivers global dictionary: `bpy.app.driver_namespace`,
|
||||
* forcing #BPY_driver_exec to recreate it. Use this when loading a new `.blend` file
|
||||
* so any variables setup by the previous blend file are cleared.
|
||||
*/
|
||||
void BPY_driver_reset();
|
||||
|
||||
/**
|
||||
* This evaluates Python driver expressions, `driver_orig->expression`
|
||||
* is a Python expression that should evaluate to a float number, which is returned.
|
||||
*/
|
||||
[[nodiscard]] float BPY_driver_exec(PathResolvedRNA *anim_rna,
|
||||
ChannelDriver *driver,
|
||||
ChannelDriver *driver_orig,
|
||||
const AnimationEvalContext *anim_eval_context);
|
||||
|
||||
/**
|
||||
* Acquire the global-interpreter-lock (GIL) and wrap `Py_DECREF`.
|
||||
* as there are some cases when this needs to be called outside the Python API code.
|
||||
*/
|
||||
void BPY_DECREF(void *pyob_ptr);
|
||||
|
||||
void BPY_DECREF_RNA_INVALIDATE(void *pyob_ptr);
|
||||
[[nodiscard]] bool BPY_context_member_get(bContext *C,
|
||||
const char *member,
|
||||
bContextDataResult *result);
|
||||
/**
|
||||
* Set the global `bpy.context` for Python.
|
||||
* \param C: The context to set. In rare cases C can be null.
|
||||
*/
|
||||
void BPY_context_set(bContext *C);
|
||||
/**
|
||||
* Use for updating while a python script runs - in case of file load.
|
||||
* \param C: The context to activate as `bpy.context`. In rare cases C can be null.
|
||||
*/
|
||||
void BPY_context_update(bContext *C);
|
||||
|
||||
/**
|
||||
* Use for `CTX_*_set(..)` functions need to set values which are later read back as expected.
|
||||
* In this case we don't want the Python context to override the values as it causes problems
|
||||
* see #66256.
|
||||
*
|
||||
* \param dict_p: A pointer to #bContext.data.py_context so we can assign a new value.
|
||||
* \param dict_orig: The value of #bContext.data.py_context_orig to check if we need to copy.
|
||||
*/
|
||||
void BPY_context_dict_clear_members_array(void **dict_p,
|
||||
void *dict_orig,
|
||||
const char *context_members[],
|
||||
uint context_members_len);
|
||||
|
||||
void BPY_id_release(ID *id);
|
||||
|
||||
/**
|
||||
* Free (actually dereference) the Python type object representing the given #StrucRNA type,
|
||||
* if it is defined.
|
||||
*/
|
||||
void BPY_free_srna_pytype(StructRNA *srna);
|
||||
|
||||
/**
|
||||
* Avoids duplicating keyword list.
|
||||
*/
|
||||
[[nodiscard]] bool BPY_string_is_keyword(const char *str);
|
||||
|
||||
/**
|
||||
* Get current Python stack location.
|
||||
* Returns a string like `filename.py:123` if available, #std::nullopt otherwise.
|
||||
*/
|
||||
[[nodiscard]] std::optional<std::string> BPY_python_current_file_and_line();
|
||||
|
||||
/* `bpy_rna_callback.cc` */
|
||||
|
||||
void BPY_callback_screen_free(ARegionType *art);
|
||||
void BPY_callback_wm_free(wmWindowManager *wm);
|
||||
|
||||
/* I18n for addons */
|
||||
#ifdef WITH_INTERNATIONAL
|
||||
[[nodiscard]] std::optional<StringRefNull> BPY_app_translations_py_pgettext(StringRef msgctxt,
|
||||
StringRef msgid);
|
||||
#endif
|
||||
|
||||
} // namespace blender
|
||||
21
blender-5.2.0/source/blender/python/BPY_extern_clog.hh
Normal file
21
blender-5.2.0/source/blender/python/BPY_extern_clog.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup python
|
||||
*
|
||||
* Logging defines.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
struct CLG_LogRef;
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* bpy_interface.cc */
|
||||
|
||||
extern CLG_LogRef *BPY_LOG_RNA;
|
||||
|
||||
} // namespace blender
|
||||
36
blender-5.2.0/source/blender/python/BPY_extern_python.hh
Normal file
36
blender-5.2.0/source/blender/python/BPY_extern_python.hh
Normal file
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup python
|
||||
*
|
||||
* Functionality relating to Python setup & tear down.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
/* For 'FILE'. */
|
||||
#include <cstdio>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
|
||||
/* `bpy_interface.cc` */
|
||||
|
||||
/** Call #BPY_context_set first. */
|
||||
void BPY_python_start(bContext *C, int argc, const char **argv);
|
||||
void BPY_python_end(bool do_python_exit);
|
||||
void BPY_python_reset(bContext *C);
|
||||
void BPY_python_use_system_env();
|
||||
void BPY_python_use_user_env();
|
||||
[[nodiscard]] bool BPY_python_use_system_env_get();
|
||||
void BPY_python_backtrace(FILE *fp);
|
||||
|
||||
/* `bpy_app.cc` */
|
||||
|
||||
/** Access `main_args_help_as_string` needed to resolve bad level call. */
|
||||
extern char *(*BPY_python_app_help_text_fn)(bool all);
|
||||
|
||||
} // namespace blender
|
||||
254
blender-5.2.0/source/blender/python/BPY_extern_run.hh
Normal file
254
blender-5.2.0/source/blender/python/BPY_extern_run.hh
Normal file
@@ -0,0 +1,254 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup python
|
||||
*
|
||||
* \subsection common_args Common Arguments
|
||||
*
|
||||
* - `C` the #bContext (never NULL).
|
||||
*
|
||||
* - `imports`: This is simply supported for convenience since imports can make constructing
|
||||
* strings more cumbersome as otherwise small expressions become multi-line code-blocks.
|
||||
* Optional (ignored when NULL), otherwise this is a NULL terminated array of module names.
|
||||
*
|
||||
* Failure to import any modules prevents any further execution.
|
||||
*
|
||||
* - `err_info` #BPy_RunErrInfo is passed to some functions so errors can be forwarded to the UI.
|
||||
* Option (when NULL errors are printed to the `stdout` and cleared).
|
||||
* However this should be used in any case the error would be useful to show to the user.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_string_ref.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ReportList;
|
||||
struct Text;
|
||||
struct bContext;
|
||||
struct IDProperty;
|
||||
|
||||
/* `bpy_interface_run.cc` */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Run File/Text as a Script
|
||||
*
|
||||
* \note #BPY_run_filepath and #BPY_run_filepath have almost identical behavior
|
||||
* one operates on a file-path, the other on a blender text-block.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Execute `filepath` as a Python script.
|
||||
*
|
||||
* Wrapper for `PyRun_File` (similar to calling python with a script argument).
|
||||
* Used for the `--python` command line argument.
|
||||
*
|
||||
* \param C: The context (never NULL).
|
||||
* \param filepath: The file path to execute.
|
||||
* \param reports: Failure to execute the script will report the exception here (may be NULL).
|
||||
* \return true on success, otherwise false with an error reported to `reports`.
|
||||
*
|
||||
* \note Python scripts could consider `bpy.utils.execfile`, which has the advantage of returning
|
||||
* the object as a module for data access & caching `pyc` file for faster re-execution.
|
||||
*/
|
||||
bool BPY_run_filepath(bContext *C, const char *filepath, ReportList *reports) ATTR_NONNULL(1, 2);
|
||||
/**
|
||||
* Execute a Blender `text` block as a Python script.
|
||||
*
|
||||
* Wrapper for `Py_CompileStringObject` & `PyEval_EvalCode`.
|
||||
* Used for the `--python-text` command line argument.
|
||||
*
|
||||
* \param C: The context (never NULL).
|
||||
* \param text: The text-block to execute.
|
||||
* \param reports: Failure to execute the script will report the exception here (may be NULL).
|
||||
* \param do_jump: When true, any error moves the cursor to the location of that error.
|
||||
* Useful for executing scripts interactively from the text editor.
|
||||
* \return true on success, otherwise false with an error reported to `reports`.
|
||||
*
|
||||
* \note The `__file__` is constructed by joining the blend file-path to the name of the text.
|
||||
* This is done so error messages give useful output however there are rare cases causes problems
|
||||
* with introspection tools which attempt to load `__file__`.
|
||||
*/
|
||||
bool BPY_run_text(bContext *C, Text *text, ReportList *reports, bool do_jump) ATTR_NONNULL(1, 2);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Run a String as a Script
|
||||
*
|
||||
* - Use 'eval' for simple single-line expressions.
|
||||
* - Use 'exec' for full multi-line scripts.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Compile the python expression and \return true if no errors occurred during compilation,
|
||||
* otherwise \return false.
|
||||
*
|
||||
* \param C: Optional context (may be null).
|
||||
*/
|
||||
bool BPY_string_compile_check(const char *expr);
|
||||
/**
|
||||
* Run an entire script, matches: `exec(compile(..., "exec"))`
|
||||
*
|
||||
* \param C: Optional context (may be null),
|
||||
* used for `bpy.context` and reporting errors to `CTX_wm_reports(C)`.
|
||||
*/
|
||||
bool BPY_run_string_exec(bContext *C, const char *imports[], const char *expr);
|
||||
/**
|
||||
* Run an expression, matches: `exec(compile(..., "eval"))`.
|
||||
*
|
||||
* \param C: Optional context (may be null),
|
||||
* used for `bpy.context` and reporting errors to `CTX_wm_reports(C)`.
|
||||
*/
|
||||
bool BPY_run_string_eval(bContext *C, const char *imports[], const char *expr);
|
||||
|
||||
/**
|
||||
* Run a script, with the given local variables.
|
||||
*
|
||||
* \param C: Optional context (may be null),
|
||||
* used for `bpy.context` and reporting errors to `CTX_wm_reports(C)`.
|
||||
*
|
||||
* \param script: The Python script to run, can be multiple lines.
|
||||
*
|
||||
* \param locals: group property with string keys, defining the script's local variables.
|
||||
*/
|
||||
bool BPY_run_string_exec_with_locals(bContext *C, StringRefNull script, IDProperty &locals);
|
||||
|
||||
/**
|
||||
* Run a script, with the given local variables, and return a result.
|
||||
*
|
||||
* The script should assign a value to a script-local variable. Its value will be returned as
|
||||
* IDProperty. This is limited to simple values (None, bool, int, float, string), where integers
|
||||
* are limited to signed 32-bit values.
|
||||
*
|
||||
* \param C: Optional context (may be null), used for `bpy.context` and reporting errors to
|
||||
* `CTX_wm_reports(C)`.
|
||||
*
|
||||
* \param script: The Python script to run, can be multiple lines.
|
||||
*
|
||||
* \param locals: group property with string keys, defining the script's local variables.
|
||||
*
|
||||
* \param result_var_name: the name of the local variable that the script assigns its result to.
|
||||
* This MUST be a valid Python identifier.
|
||||
*
|
||||
* \returns the value of the result variable (see above) after the script finished. If the result
|
||||
* value was not set, `std::noopt` is returned. A value of `None` will return in a `nullptr` value
|
||||
* of the `std::optional`.
|
||||
*/
|
||||
std::optional<IDProperty *> BPY_run_string_exec_with_locals_return_idprop(
|
||||
bContext *C, StringRefNull script, IDProperty &locals, StringRefNull result_var_name);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Run a String as a Script & Return the Result
|
||||
*
|
||||
* Convenience functions for executing a script and returning the result as an expected type.
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* \note When this struct is passed in as NULL,
|
||||
* print errors to the `stdout` and clear.
|
||||
*/
|
||||
struct BPy_RunErrInfo {
|
||||
/** Brief text, single line (can show this in status bar for example). */
|
||||
bool use_single_line_error;
|
||||
|
||||
/** Report with optional prefix (when non-NULL). */
|
||||
ReportList *reports;
|
||||
const char *report_prefix;
|
||||
|
||||
/** Allocated exception text (assign when non-NULL). */
|
||||
char **r_string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Evaluate `expr` as a number (double).
|
||||
*
|
||||
* \param C: See \ref common_args.
|
||||
* \param imports: See \ref common_args.
|
||||
* \param expr: The expression to evaluate.
|
||||
* \param err_info: See \ref common_args.
|
||||
* \param r_value: The resulting value.
|
||||
* \return Success.
|
||||
*/
|
||||
[[nodiscard]] bool BPY_run_string_as_number(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
double *r_value) ATTR_NONNULL(1, 3, 5);
|
||||
/**
|
||||
* Evaluate `expr` as an integer or pointer.
|
||||
*
|
||||
* \note Support both int and pointers.
|
||||
*
|
||||
* \param C: See \ref common_args.
|
||||
* \param imports: See \ref common_args.
|
||||
* \param expr: The expression to evaluate.
|
||||
* \param err_info: See \ref common_args.
|
||||
* \param r_value: The resulting value.
|
||||
* \return Success.
|
||||
*/
|
||||
[[nodiscard]] bool BPY_run_string_as_intptr(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
intptr_t *r_value) ATTR_NONNULL(1, 3, 5);
|
||||
/**
|
||||
* Evaluate `expr` as a string.
|
||||
*
|
||||
* \param C: See \ref common_args.
|
||||
* \param imports: See \ref common_args.
|
||||
* \param expr: The expression to evaluate.
|
||||
* \param err_info: See \ref common_args.
|
||||
* \param r_value: The resulting value.
|
||||
* \return Success.
|
||||
*/
|
||||
[[nodiscard]] bool BPY_run_string_as_string_and_len(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
char **r_value,
|
||||
size_t *r_value_len) ATTR_NONNULL(1, 3, 5, 6);
|
||||
|
||||
/** See #BPY_run_string_as_string_and_len */
|
||||
[[nodiscard]] bool BPY_run_string_as_string(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
char **r_value) ATTR_NONNULL(1, 3, 5);
|
||||
|
||||
/**
|
||||
* Evaluate `expr` as a string or None.
|
||||
* Where a success return value with `*r_value == nullptr` indicates a value of None.
|
||||
*
|
||||
* \param C: See \ref common_args.
|
||||
* \param imports: See \ref common_args.
|
||||
* \param expr: The expression to evaluate.
|
||||
* \param err_info: See \ref common_args.
|
||||
* \param r_value: The resulting value.
|
||||
* \return Success.
|
||||
*/
|
||||
[[nodiscard]] bool BPY_run_string_as_string_and_len_or_none(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
char **r_value,
|
||||
size_t *r_value_len)
|
||||
ATTR_NONNULL(1, 3, 5, 6);
|
||||
|
||||
/** See #BPY_run_string_as_string_and_len */
|
||||
[[nodiscard]] bool BPY_run_string_as_string_or_none(bContext *C,
|
||||
const char *imports[],
|
||||
const char *expr,
|
||||
BPy_RunErrInfo *err_info,
|
||||
char **r_value) ATTR_NONNULL(1, 3, 5);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
13
blender-5.2.0/source/blender/python/CMakeLists.txt
Normal file
13
blender-5.2.0/source/blender/python/CMakeLists.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
if(WITH_PYTHON_MODULE)
|
||||
add_definitions(-DPy_ENABLE_SHARED)
|
||||
endif()
|
||||
|
||||
add_subdirectory(intern)
|
||||
add_subdirectory(generic)
|
||||
add_subdirectory(gpu)
|
||||
add_subdirectory(mathutils)
|
||||
add_subdirectory(bmesh)
|
||||
54
blender-5.2.0/source/blender/python/bmesh/CMakeLists.txt
Normal file
54
blender-5.2.0/source/blender/python/bmesh/CMakeLists.txt
Normal file
@@ -0,0 +1,54 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
bmesh_py_api.cc
|
||||
bmesh_py_geometry.cc
|
||||
bmesh_py_ops.cc
|
||||
bmesh_py_ops_call.cc
|
||||
bmesh_py_types.cc
|
||||
bmesh_py_types_customdata.cc
|
||||
bmesh_py_types_meshdata.cc
|
||||
bmesh_py_types_select.cc
|
||||
bmesh_py_utils.cc
|
||||
|
||||
bmesh_py_api.hh
|
||||
bmesh_py_geometry.hh
|
||||
bmesh_py_ops.hh
|
||||
bmesh_py_ops_call.hh
|
||||
bmesh_py_types.hh
|
||||
bmesh_py_types_customdata.hh
|
||||
bmesh_py_types_meshdata.hh
|
||||
bmesh_py_types_select.hh
|
||||
bmesh_py_utils.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
bf_python_mathutils
|
||||
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
)
|
||||
|
||||
if(WITH_FREESTYLE)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_GMP)
|
||||
add_definitions(-DWITH_GMP)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python_bmesh "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
234
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.cc
Normal file
234
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.cc
Normal file
@@ -0,0 +1,234 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh' module.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
#include "bmesh_py_types_customdata.hh"
|
||||
#include "bmesh_py_types_meshdata.hh"
|
||||
#include "bmesh_py_types_select.hh"
|
||||
|
||||
#include "bmesh_py_geometry.hh"
|
||||
#include "bmesh_py_ops.hh"
|
||||
#include "bmesh_py_utils.hh"
|
||||
|
||||
#include "BKE_editmesh.hh"
|
||||
#include "BKE_mesh_types.hh"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "bmesh_py_api.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_new_doc,
|
||||
".. function:: new(*, use_operators=True)\n"
|
||||
"\n"
|
||||
" :param use_operators: Support calling operators in :mod:`bmesh.ops` (uses some "
|
||||
"extra memory per vert/edge/face).\n"
|
||||
" :type use_operators: bool\n"
|
||||
" :return: Return a new, empty BMesh.\n"
|
||||
" :rtype: :class:`bmesh.types.BMesh`\n");
|
||||
static PyObject *bpy_bm_new(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static const char *kwlist[] = {"use_operators", nullptr};
|
||||
BMesh *bm;
|
||||
|
||||
bool use_operators = true;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(
|
||||
args, kw, "|$O&:new", const_cast<char **>(kwlist), PyC_ParseBool, &use_operators))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BMeshCreateParams params{};
|
||||
params.use_toolflags = use_operators;
|
||||
bm = BM_mesh_create(&bm_mesh_allocsize_default, ¶ms);
|
||||
bm->selectmode = SCE_SELECT_VERTEX;
|
||||
|
||||
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_NOP);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_from_edit_mesh_doc,
|
||||
".. function:: from_edit_mesh(mesh)\n"
|
||||
"\n"
|
||||
" Return a BMesh from this mesh, currently the mesh must already be in editmode.\n"
|
||||
"\n"
|
||||
" :param mesh: The editmode mesh.\n"
|
||||
" :type mesh: :class:`bpy.types.Mesh`\n"
|
||||
" :return: the BMesh associated with this mesh.\n"
|
||||
" :rtype: :class:`bmesh.types.BMesh`\n");
|
||||
static PyObject *bpy_bm_from_edit_mesh(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BMesh *bm;
|
||||
Mesh *mesh = static_cast<Mesh *>(PyC_RNA_AsPointer(value, "Mesh"));
|
||||
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mesh->runtime->edit_mesh == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "The mesh must be in editmode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bm = mesh->runtime->edit_mesh->bm;
|
||||
|
||||
return BPy_BMesh_CreatePyObject(bm, BPY_BMFLAG_IS_WRAPPED);
|
||||
}
|
||||
|
||||
void EDBM_update_extern(Mesh *mesh, const bool do_tessface, const bool is_destructive);
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_update_edit_mesh_doc,
|
||||
".. function:: update_edit_mesh(mesh, *, loop_triangles=True, destructive=True)\n"
|
||||
"\n"
|
||||
" Update the mesh after changes to the BMesh in editmode,\n"
|
||||
" optionally recalculating n-gon tessellation.\n"
|
||||
"\n"
|
||||
" :param mesh: The editmode mesh.\n"
|
||||
" :type mesh: :class:`bpy.types.Mesh`\n"
|
||||
" :param loop_triangles: Option to recalculate n-gon tessellation.\n"
|
||||
" :type loop_triangles: bool\n"
|
||||
" :param destructive: Use when geometry has been added or removed.\n"
|
||||
" :type destructive: bool\n");
|
||||
static PyObject *bpy_bm_update_edit_mesh(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static const char *kwlist[] = {"mesh", "loop_triangles", "destructive", nullptr};
|
||||
PyObject *py_me;
|
||||
Mesh *mesh;
|
||||
bool do_loop_triangles = true;
|
||||
bool is_destructive = true;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args,
|
||||
kw,
|
||||
"O|$O&O&:update_edit_mesh",
|
||||
const_cast<char **>(kwlist),
|
||||
&py_me,
|
||||
PyC_ParseBool,
|
||||
&do_loop_triangles,
|
||||
PyC_ParseBool,
|
||||
&is_destructive))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mesh = static_cast<Mesh *>(PyC_RNA_AsPointer(py_me, "Mesh"));
|
||||
|
||||
if (mesh == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (mesh->runtime->edit_mesh == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "The mesh must be in editmode");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
EDBM_update_extern(mesh, do_loop_triangles, is_destructive);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef BPy_BM_methods[] = {
|
||||
{"new",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_new),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_bm_new_doc},
|
||||
{"from_edit_mesh",
|
||||
static_cast<PyCFunction>(bpy_bm_from_edit_mesh),
|
||||
METH_O,
|
||||
bpy_bm_from_edit_mesh_doc},
|
||||
{"update_edit_mesh",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_update_edit_mesh),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_bm_update_edit_mesh_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_doc,
|
||||
"This module provides access to Blender's bmesh data structures.\n"
|
||||
"\n"
|
||||
".. include:: include__bmesh.rst\n");
|
||||
static PyModuleDef BPy_BM_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh",
|
||||
/*m_doc*/ BPy_BM_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh()
|
||||
{
|
||||
PyObject *mod;
|
||||
PyObject *submodule;
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
|
||||
BPy_BM_init_types();
|
||||
BPy_BM_init_types_select();
|
||||
BPy_BM_init_types_customdata();
|
||||
BPy_BM_init_types_meshdata();
|
||||
|
||||
mod = PyModule_Create(&BPy_BM_module_def);
|
||||
|
||||
/* bmesh.types */
|
||||
PyModule_AddObject(mod, "types", (submodule = BPyInit_bmesh_types()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
/* bmesh.ops (not a real module, exposes module like access). */
|
||||
PyModule_AddObject(mod, "ops", (submodule = BPyInit_bmesh_ops()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "utils", (submodule = BPyInit_bmesh_utils()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "geometry", (submodule = BPyInit_bmesh_geometry()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_api.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,89 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh.geometry' module.
|
||||
* Utility functions for operating on 'bmesh.types'
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "bmesh_py_geometry.hh" /* own include */
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bm_geometry_intersect_face_point_doc,
|
||||
".. function:: intersect_face_point(face, point)\n"
|
||||
"\n"
|
||||
" Tests if the projection of a point is inside a face (using the face's normal).\n"
|
||||
"\n"
|
||||
" :param face: The face to test.\n"
|
||||
" :type face: :class:`bmesh.types.BMFace`\n"
|
||||
" :param point: The 3D point to test.\n"
|
||||
" :type point: tuple[float, float, float] | Sequence[float]\n"
|
||||
" :return: True when the projection of the point is in the face.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *bpy_bm_geometry_intersect_face_point(BPy_BMFace * /*self*/, PyObject *args)
|
||||
{
|
||||
BPy_BMFace *py_face;
|
||||
PyObject *py_point;
|
||||
float point[3];
|
||||
bool ret;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O!O:intersect_face_point", &BPy_BMFace_Type, &py_face, &py_point)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_OBJ(py_face);
|
||||
if (mathutils_array_parse(point, 3, 3, py_point, "intersect_face_point") == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ret = BM_face_point_inside_test(py_face->f, point);
|
||||
|
||||
return PyBool_FromLong(ret);
|
||||
}
|
||||
|
||||
static PyMethodDef BPy_BM_geometry_methods[] = {
|
||||
{"intersect_face_point",
|
||||
reinterpret_cast<PyCFunction>(bpy_bm_geometry_intersect_face_point),
|
||||
METH_VARARGS,
|
||||
bpy_bm_geometry_intersect_face_point_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_utils_doc,
|
||||
"This module provides access to bmesh geometry evaluation functions.\n");
|
||||
static PyModuleDef BPy_BM_geometry_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh.geometry",
|
||||
/*m_doc*/ BPy_BM_utils_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_geometry_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh_geometry()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&BPy_BM_geometry_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_geometry();
|
||||
|
||||
} // namespace blender
|
||||
286
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.cc
Normal file
286
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.cc
Normal file
@@ -0,0 +1,286 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the 'bmesh.ops' module.
|
||||
* Operators from 'opdefines' are wrapped.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_dynstr.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_ops.hh" /* own include */
|
||||
#include "bmesh_py_ops_call.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* bmesh operator 'bmesh.ops.*' callable types
|
||||
* ******************************************* */
|
||||
|
||||
static PyObject *bpy_bmesh_op_repr(BPy_BMeshOpFunc *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("<%.200s bmesh.ops.%.200s()>", Py_TYPE(self)->tp_name, self->opname);
|
||||
}
|
||||
|
||||
/* methods
|
||||
* ======= */
|
||||
|
||||
/* __doc__
|
||||
* ------- */
|
||||
|
||||
static char *bmp_slots_as_args(const BMOSlotType slot_types[BMO_OP_MAX_SLOTS], const bool is_out)
|
||||
{
|
||||
DynStr *dyn_str = BLI_dynstr_new();
|
||||
char *ret;
|
||||
bool quoted;
|
||||
bool set;
|
||||
|
||||
int i = 0;
|
||||
|
||||
while (*slot_types[i].name) {
|
||||
quoted = false;
|
||||
set = false;
|
||||
/* Cut off `.out` by using a string size argument. */
|
||||
const int name_len = is_out ? (strchr(slot_types[i].name, '.') - slot_types[i].name) :
|
||||
sizeof(slot_types[i].name);
|
||||
const char *value = "<Unknown>";
|
||||
switch (slot_types[i].type) {
|
||||
case BMO_OP_SLOT_BOOL:
|
||||
value = "False";
|
||||
break;
|
||||
case BMO_OP_SLOT_INT:
|
||||
if (slot_types[i].subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_ENUM) {
|
||||
value = slot_types[i].enum_flags[0].identifier;
|
||||
quoted = true;
|
||||
}
|
||||
else if (slot_types[i].subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_FLAG) {
|
||||
value = "";
|
||||
set = true;
|
||||
}
|
||||
else {
|
||||
value = "0";
|
||||
}
|
||||
break;
|
||||
case BMO_OP_SLOT_FLT:
|
||||
value = "0.0";
|
||||
break;
|
||||
case BMO_OP_SLOT_PTR:
|
||||
value = "None";
|
||||
break;
|
||||
case BMO_OP_SLOT_MAT:
|
||||
value = "Matrix()";
|
||||
break;
|
||||
case BMO_OP_SLOT_VEC:
|
||||
value = "Vector()";
|
||||
break;
|
||||
case BMO_OP_SLOT_ELEMENT_BUF:
|
||||
value = (slot_types[i].subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) ? "None" : "[]";
|
||||
break;
|
||||
case BMO_OP_SLOT_MAPPING:
|
||||
value = "{}";
|
||||
break;
|
||||
}
|
||||
BLI_dynstr_appendf(dyn_str,
|
||||
i ? ", %.*s=%s%s%s%s%s" : "%.*s=%s%s%s%s%s",
|
||||
name_len,
|
||||
slot_types[i].name,
|
||||
set ? "{" : "",
|
||||
quoted ? "'" : "",
|
||||
value,
|
||||
quoted ? "'" : "",
|
||||
set ? "}" : "");
|
||||
i++;
|
||||
}
|
||||
|
||||
ret = BLI_dynstr_get_cstring(dyn_str);
|
||||
BLI_dynstr_free(dyn_str);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_op_doc_get(BPy_BMeshOpFunc *self, void * /*closure*/)
|
||||
{
|
||||
PyObject *ret;
|
||||
char *slot_in;
|
||||
char *slot_out;
|
||||
int i;
|
||||
|
||||
i = BMO_opcode_from_opname(self->opname);
|
||||
|
||||
slot_in = bmp_slots_as_args(bmo_opdefines[i]->slot_types_in, false);
|
||||
slot_out = bmp_slots_as_args(bmo_opdefines[i]->slot_types_out, true);
|
||||
|
||||
ret = PyUnicode_FromFormat("%.200s bmesh.ops.%.200s(bmesh, %s)\n -> dict(%s)",
|
||||
Py_TYPE(self)->tp_name,
|
||||
self->opname,
|
||||
slot_in,
|
||||
slot_out);
|
||||
|
||||
MEM_delete(slot_in);
|
||||
MEM_delete(slot_out);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmesh_op_getseters[] = {
|
||||
{"__doc__",
|
||||
reinterpret_cast<getter>(bpy_bmesh_op_doc_get),
|
||||
static_cast<setter>(nullptr),
|
||||
nullptr,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
/* Types
|
||||
* ===== */
|
||||
|
||||
static PyTypeObject bmesh_op_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "BMeshOpFunc",
|
||||
/*tp_basicsize*/ sizeof(BPy_BMeshOpFunc),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(bpy_bmesh_op_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ reinterpret_cast<ternaryfunc>(BPy_BMO_call),
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ nullptr,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ bpy_bmesh_op_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/* bmesh module 'bmesh.ops'
|
||||
* ************************ */
|
||||
|
||||
static PyObject *bpy_bmesh_op_CreatePyObject(const char *opname)
|
||||
{
|
||||
BPy_BMeshOpFunc *self = PyObject_New(BPy_BMeshOpFunc, &bmesh_op_Type);
|
||||
|
||||
self->opname = opname;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_ops_module_getattro(PyObject * /*self*/, PyObject *pyname)
|
||||
{
|
||||
const char *opname = PyUnicode_AsUTF8(pyname);
|
||||
|
||||
if (BMO_opcode_from_opname(opname) != -1) {
|
||||
return bpy_bmesh_op_CreatePyObject(opname);
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError, "BMeshOpsModule: operator \"%.200s\" doesn't exist", opname);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmesh_ops_module_dir(PyObject * /*self*/)
|
||||
{
|
||||
const uint tot = bmo_opdefines_total;
|
||||
uint i;
|
||||
PyObject *ret;
|
||||
|
||||
ret = PyList_New(bmo_opdefines_total);
|
||||
|
||||
for (i = 0; i < tot; i++) {
|
||||
PyList_SET_ITEM(ret, i, PyUnicode_FromString(bmo_opdefines[i]->opname));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef BPy_BM_ops_methods[] = {
|
||||
{"__getattr__", static_cast<PyCFunction>(bpy_bmesh_ops_module_getattro), METH_O, nullptr},
|
||||
{"__dir__", reinterpret_cast<PyCFunction>(bpy_bmesh_ops_module_dir), METH_NOARGS, nullptr},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPy_BM_ops_doc,
|
||||
"Access to BMesh operators.");
|
||||
static PyModuleDef BPy_BM_ops_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bmesh.ops",
|
||||
/*m_doc*/ BPy_BM_ops_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BPy_BM_ops_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_bmesh_ops()
|
||||
{
|
||||
PyObject *submodule = PyModule_Create(&BPy_BM_ops_module_def);
|
||||
|
||||
if (PyType_Ready(&bmesh_op_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_ops();
|
||||
|
||||
} // namespace blender
|
||||
870
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops_call.cc
Normal file
870
blender-5.2.0/source/blender/python/bmesh/bmesh_py_ops_call.cc
Normal file
@@ -0,0 +1,870 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file provides __call__ aka BPy_BMO_call for
|
||||
* the bmesh operator and has been given its own file
|
||||
* because argument conversion is involved.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_ops_call.hh" /* own include */
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
BLI_STATIC_ASSERT(sizeof(PyC_FlagSet) == sizeof(BMO_FlagSet), "size mismatch");
|
||||
|
||||
static int bpy_bm_op_as_py_error(BMesh *bm)
|
||||
{
|
||||
if (BMO_error_occurred_at_level(bm, BMO_ERROR_FATAL)) {
|
||||
/* NOTE: we could have multiple errors. */
|
||||
const char *errmsg;
|
||||
if (BMO_error_get(bm, &errmsg, nullptr, nullptr)) {
|
||||
PyErr_Format(PyExc_RuntimeError, "bmesh operator: %.200s", errmsg);
|
||||
BMO_error_clear(bm);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Utility function to check BMVert/BMEdge/BMFace's
|
||||
*
|
||||
* \param value:
|
||||
* \param bm: Check the \a value against this.
|
||||
* \param htype: Test \a value matches this type.
|
||||
* \param descr: Description text.
|
||||
*/
|
||||
static int bpy_slot_from_py_elem_check(BPy_BMElem *value,
|
||||
BMesh *bm,
|
||||
const char htype,
|
||||
/* for error messages */
|
||||
const char *opname,
|
||||
const char *slot_name,
|
||||
const char *descr)
|
||||
{
|
||||
if (!BPy_BMElem_Check(value) || !(value->ele->head.htype & htype)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, expected a %.200s not *.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
descr,
|
||||
BPy_BMElem_StringFromHType(htype),
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s invalidated element",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm != bm) { /* we may want to make this check optional by setting 'bm' to nullptr */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s invalidated element",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Utility function to check BMVertSeq/BMEdgeSeq/BMFaceSeq's
|
||||
*
|
||||
* \param value: Caller must check its a BMeshSeq
|
||||
* \param bm: Check the \a value against this.
|
||||
* \param htype_py: The type(s) of \a value.
|
||||
* \param htype_bmo: The type(s) supported by the target slot.
|
||||
* \param descr: Description text.
|
||||
*/
|
||||
static int bpy_slot_from_py_elemseq_check(BPy_BMGeneric *value,
|
||||
BMesh *bm,
|
||||
const char htype_py,
|
||||
const char htype_bmo,
|
||||
/* for error messages */
|
||||
const char *opname,
|
||||
const char *slot_name,
|
||||
const char *descr)
|
||||
{
|
||||
if (value->bm == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if (value->bm != bm) { /* we may want to make this check optional by setting 'bm' to nullptr */
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, invalidated sequence",
|
||||
opname,
|
||||
slot_name,
|
||||
descr);
|
||||
return -1;
|
||||
}
|
||||
if ((htype_py & htype_bmo) == 0) {
|
||||
char str_bmo[32];
|
||||
char str_py[32];
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" %.200s, expected "
|
||||
"a sequence of %.200s not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
descr,
|
||||
BPy_BMElem_StringFromHType_ex(htype_bmo, str_bmo),
|
||||
BPy_BMElem_StringFromHType_ex(htype_py, str_py));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use for giving py args to an operator.
|
||||
*/
|
||||
static int bpy_slot_from_py(BMesh *bm,
|
||||
BMOperator *bmop,
|
||||
BMOpSlot *slot,
|
||||
PyObject *value,
|
||||
/* the are just for exception messages */
|
||||
const char *opname,
|
||||
const char *slot_name)
|
||||
{
|
||||
switch (slot->slot_type) {
|
||||
case BMO_OP_SLOT_BOOL: {
|
||||
const int param = PyC_Long_AsBool(value);
|
||||
|
||||
if (param == -1) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected True/False or 0/1, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_BOOL(slot) = param;
|
||||
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_INT: {
|
||||
if (slot->slot_subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_ENUM) {
|
||||
int enum_val = -1;
|
||||
PyC_FlagSet *items = reinterpret_cast<PyC_FlagSet *>(slot->data.enum_data.flags);
|
||||
const char *enum_str = PyUnicode_AsUTF8(value);
|
||||
|
||||
if (enum_str == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a string, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (PyC_FlagSet_ValueFromID(items, enum_str, &enum_val, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = enum_val;
|
||||
}
|
||||
else if (slot->slot_subtype.intg == BMO_OP_SLOT_SUBTYPE_INT_FLAG) {
|
||||
int flag = 0;
|
||||
PyC_FlagSet *items = reinterpret_cast<PyC_FlagSet *>(slot->data.enum_data.flags);
|
||||
|
||||
if (PyC_FlagSet_ToBitfield(items, value, &flag, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = flag;
|
||||
}
|
||||
else {
|
||||
const int param = PyC_Long_AsI32(value);
|
||||
|
||||
if (param == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected an int, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_INT(slot) = param;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_FLT: {
|
||||
const float param = PyFloat_AsDouble(value);
|
||||
if (param == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a float, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_SLOT_AS_FLOAT(slot) = param;
|
||||
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAT: {
|
||||
/* XXX: BMesh operator design is crappy here, operator slot should define matrix size,
|
||||
* not the caller! */
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_ParseAny(value, &pymat)) {
|
||||
return -1;
|
||||
}
|
||||
const ushort size = pymat->col_num;
|
||||
if ((size != pymat->row_num) || !ELEM(size, 3, 4)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected a 3x3 or 4x4 matrix",
|
||||
opname,
|
||||
slot_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_mat_set(bmop, bmop->slots_in, slot_name, pymat->matrix, size);
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_VEC: {
|
||||
/* passing slot name here is a bit non-descriptive */
|
||||
if (mathutils_array_parse(BMO_SLOT_AS_VECTOR(slot), 3, 3, value, slot_name) == -1) {
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_ELEMENT_BUF: {
|
||||
if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(value),
|
||||
bm,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"single element") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_single(
|
||||
bmop, slot, &(reinterpret_cast<BPy_BMElem *>(value))->ele->head);
|
||||
}
|
||||
else {
|
||||
/* there are many ways we could interpret arguments, for now...
|
||||
* - verts/edges/faces from the mesh direct,
|
||||
* this way the operator takes every item.
|
||||
* - `TODO` a plain python sequence (list) of elements.
|
||||
* - `TODO` an iterator. eg.
|
||||
* face.verts
|
||||
* - `TODO` (type, flag) pair, eg.
|
||||
* ('VERT', {'TAG'})
|
||||
*/
|
||||
|
||||
if (BPy_BMVertSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_VERT,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_VERT);
|
||||
}
|
||||
else if (BPy_BMEdgeSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_EDGE,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_EDGE);
|
||||
}
|
||||
else if (BPy_BMFaceSeq_Check(value)) {
|
||||
if (bpy_slot_from_py_elemseq_check(reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
BM_FACE,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
BMO_slot_buffer_from_all(bm, bmop, bmop->slots_in, slot_name, BM_FACE);
|
||||
}
|
||||
|
||||
else if (BPy_BMElemSeq_Check(value)) {
|
||||
BMIter iter;
|
||||
BMHeader *ele;
|
||||
int tot;
|
||||
uint i;
|
||||
|
||||
if (bpy_slot_from_py_elemseq_check(
|
||||
reinterpret_cast<BPy_BMGeneric *>(value),
|
||||
bm,
|
||||
bm_iter_itype_htype_map[(reinterpret_cast<BPy_BMElemSeq *>(value))->itype],
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
opname,
|
||||
slot_name,
|
||||
"element buffer") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
/* this will loop over all elements which is a shame but
|
||||
* we need to know this before alloc */
|
||||
/* calls bpy_bmelemseq_length() */
|
||||
tot = Py_TYPE(value)->tp_as_sequence->sq_length(value);
|
||||
|
||||
BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, tot);
|
||||
|
||||
i = 0;
|
||||
BM_ITER_BPY_BM_SEQ (ele, &iter, ((BPy_BMElemSeq *)value)) {
|
||||
slot->data.buf[i] = ele;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* keep this last */
|
||||
else if (PySequence_Check(value)) {
|
||||
BMElem **elem_array = nullptr;
|
||||
Py_ssize_t elem_array_len;
|
||||
|
||||
elem_array = static_cast<BMElem **>(
|
||||
BPy_BMElem_PySeq_As_Array(&bm,
|
||||
value,
|
||||
0,
|
||||
PY_SSIZE_T_MAX,
|
||||
&elem_array_len,
|
||||
(slot->slot_subtype.elem & BM_ALL_NOLOOP),
|
||||
true,
|
||||
true,
|
||||
slot_name));
|
||||
|
||||
/* error is set above */
|
||||
if (elem_array == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_buffer_alloc(bmop, bmop->slots_in, slot_name, elem_array_len);
|
||||
memcpy(slot->data.buf, elem_array, sizeof(void *) * elem_array_len);
|
||||
PyMem_FREE(elem_array);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a bmesh sequence, list, (htype, flag) pair, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAPPING: {
|
||||
/* first check types */
|
||||
if (slot->slot_subtype.map != BMO_OP_SLOT_SUBTYPE_MAP_EMPTY) {
|
||||
if (!PyDict_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!PySet_Check(value)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a set, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
switch (slot->slot_subtype.map) {
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_ELEM: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_value),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid value in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
BMO_slot_map_elem_insert(bmop,
|
||||
slot,
|
||||
(reinterpret_cast<BPy_BMElem *>(arg_key))->ele,
|
||||
(reinterpret_cast<BPy_BMElem *>(arg_value))->ele);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_FLT: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
float value_f;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_f = PyFloat_AsDouble(arg_value);
|
||||
|
||||
if (value_f == -1.0f && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with float values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_float_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INT: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
int value_i;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_i = PyC_Long_AsI32(arg_value);
|
||||
|
||||
if (value_i == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with int values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_int_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_i);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_BOOL: {
|
||||
if (PyDict_Size(value) > 0) {
|
||||
PyObject *arg_key, *arg_value;
|
||||
Py_ssize_t arg_pos = 0;
|
||||
while (PyDict_Next(value, &arg_pos, &arg_key, &arg_value)) {
|
||||
int value_i;
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in dict") == -1)
|
||||
{
|
||||
return -1; /* error is set in bpy_slot_from_py_elem_check() */
|
||||
}
|
||||
|
||||
value_i = PyC_Long_AsI32(arg_value);
|
||||
|
||||
if (value_i == -1 && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" expected "
|
||||
"a dict with bool values, not %.200s",
|
||||
opname,
|
||||
slot_name,
|
||||
Py_TYPE(arg_value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BMO_slot_map_bool_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele, value_i != 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY: {
|
||||
if (PySet_GET_SIZE(value) > 0) {
|
||||
PyObject *it = PyObject_GetIter(value);
|
||||
PyObject *arg_key;
|
||||
while ((arg_key = PyIter_Next(it))) {
|
||||
/* Borrow from the set. */
|
||||
Py_DECREF(arg_key);
|
||||
|
||||
if (bpy_slot_from_py_elem_check(reinterpret_cast<BPy_BMElem *>(arg_key),
|
||||
bm,
|
||||
BM_ALL_NOLOOP,
|
||||
opname,
|
||||
slot_name,
|
||||
"invalid key in set") == -1)
|
||||
{
|
||||
/* Error is set in #bpy_slot_from_py_elem_check(). */
|
||||
break;
|
||||
}
|
||||
|
||||
BMO_slot_map_empty_insert(
|
||||
bmop, slot, (reinterpret_cast<BPy_BMElem *>(arg_key))->ele);
|
||||
}
|
||||
Py_DECREF(it);
|
||||
if (arg_key) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL: {
|
||||
/* can't convert from these */
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"This arguments mapping subtype %d is not supported",
|
||||
slot->slot_subtype.map);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* TODO: many others. */
|
||||
PyErr_Format(PyExc_NotImplementedError,
|
||||
"%.200s: keyword \"%.200s\" type %d not working yet!",
|
||||
opname,
|
||||
slot_name,
|
||||
slot->slot_type);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* all is well */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use for getting return values from an operator that's already executed.
|
||||
*
|
||||
* \note Don't throw any exceptions and should always return a valid (PyObject *).
|
||||
*/
|
||||
static PyObject *bpy_slot_to_py(BMesh *bm, BMOpSlot *slot)
|
||||
{
|
||||
PyObject *item = nullptr;
|
||||
|
||||
/* keep switch in same order as above */
|
||||
switch (slot->slot_type) {
|
||||
case BMO_OP_SLOT_BOOL:
|
||||
item = PyBool_FromLong(BMO_SLOT_AS_BOOL(slot));
|
||||
break;
|
||||
case BMO_OP_SLOT_INT:
|
||||
item = PyLong_FromLong(BMO_SLOT_AS_INT(slot));
|
||||
break;
|
||||
case BMO_OP_SLOT_FLT:
|
||||
item = PyFloat_FromDouble(double(BMO_SLOT_AS_FLOAT(slot)));
|
||||
break;
|
||||
case BMO_OP_SLOT_MAT:
|
||||
item = Matrix_CreatePyObject(
|
||||
reinterpret_cast<float *> BMO_SLOT_AS_MATRIX(slot), 4, 4, nullptr);
|
||||
break;
|
||||
case BMO_OP_SLOT_VEC:
|
||||
item = Vector_CreatePyObject(BMO_SLOT_AS_VECTOR(slot), slot->len, nullptr);
|
||||
break;
|
||||
case BMO_OP_SLOT_PTR:
|
||||
BLI_assert(0); /* currently we don't have any pointer return values in use */
|
||||
item = Py_NewRef(Py_None);
|
||||
break;
|
||||
case BMO_OP_SLOT_ELEMENT_BUF: {
|
||||
if (slot->slot_subtype.elem & BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE) {
|
||||
BMHeader *ele = static_cast<BMHeader *>(BMO_slot_buffer_get_single(slot));
|
||||
item = ele ? BPy_BMElem_CreatePyObject(bm, ele) : Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
const int size = slot->len;
|
||||
void **buffer = BMO_SLOT_AS_BUFFER(slot);
|
||||
int j;
|
||||
|
||||
item = PyList_New(size);
|
||||
for (j = 0; j < size; j++) {
|
||||
BMHeader *ele = static_cast<BMHeader *>(buffer[j]);
|
||||
PyList_SET_ITEM(item, j, BPy_BMElem_CreatePyObject(bm, ele));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_MAPPING: {
|
||||
GHash *slot_hash = BMO_SLOT_AS_GHASH(slot);
|
||||
GHashIterator hash_iter;
|
||||
|
||||
switch (slot->slot_subtype.map) {
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_ELEM: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = BPy_BMElem_CreatePyObject(bm, static_cast<BMHeader *>(ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_FLT: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyFloat_FromDouble(*reinterpret_cast<float *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INT: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyLong_FromLong(*reinterpret_cast<int *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_BOOL: {
|
||||
item = _PyDict_NewPresized(slot_hash ? BLI_ghash_len(slot_hash) : 0);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
void *ele_val = BLI_ghashIterator_getValue(&hash_iter);
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
PyObject *py_val = PyBool_FromLong(*reinterpret_cast<bool *>(&ele_val));
|
||||
|
||||
PyDict_SetItem(item, py_key, py_val);
|
||||
Py_DECREF(py_key);
|
||||
Py_DECREF(py_val);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_EMPTY: {
|
||||
item = PySet_New(nullptr);
|
||||
if (slot_hash) {
|
||||
GHASH_ITER (hash_iter, slot_hash) {
|
||||
BMHeader *ele_key = static_cast<BMHeader *>(BLI_ghashIterator_getKey(&hash_iter));
|
||||
|
||||
PyObject *py_key = BPy_BMElem_CreatePyObject(bm, ele_key);
|
||||
|
||||
PySet_Add(item, py_key);
|
||||
|
||||
Py_DECREF(py_key);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BMO_OP_SLOT_SUBTYPE_MAP_INTERNAL:
|
||||
/* can't convert from these */
|
||||
item = Py_NewRef(Py_None);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
BLI_assert(item != nullptr);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *ret;
|
||||
BPy_BMesh *py_bm;
|
||||
BMesh *bm;
|
||||
|
||||
BMOperator bmop;
|
||||
|
||||
if ((PyTuple_GET_SIZE(args) == 1) &&
|
||||
(py_bm = reinterpret_cast<BPy_BMesh *> PyTuple_GET_ITEM(args, 0)) && BPy_BMesh_Check(py_bm))
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(py_bm);
|
||||
bm = py_bm->bm;
|
||||
|
||||
if (bm->use_toolflags == false) {
|
||||
PyErr_SetString(PyExc_ValueError, "bmesh created with 'use_operators=False'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* could complain about entering with exceptions... */
|
||||
BMO_error_clear(bm);
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"bmesh operators expect a single BMesh positional argument, all other args "
|
||||
"must be keywords");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* TODO: error check this!, though we do the error check on attribute access. */
|
||||
/* TODO: make flags optional. */
|
||||
BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, self->opname);
|
||||
|
||||
if (kw && PyDict_Size(kw) > 0) {
|
||||
/* Setup properties, see `bpy_rna.cc`: #pyrna_py_to_prop()
|
||||
* which shares this logic for parsing properties. */
|
||||
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(kw, &pos, &key, &value)) {
|
||||
const char *slot_name = PyUnicode_AsUTF8(key);
|
||||
BMOpSlot *slot;
|
||||
|
||||
if (!BMO_slot_exists(bmop.slots_in, slot_name)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s: keyword \"%.200s\" is invalid for this operator",
|
||||
self->opname,
|
||||
slot_name);
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
slot = BMO_slot_get(bmop.slots_in, slot_name);
|
||||
|
||||
/* now assign the value */
|
||||
if (bpy_slot_from_py(bm, &bmop, slot, value, self->opname, slot_name) == -1) {
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_exec(bm, &bmop);
|
||||
|
||||
/* from here until the end of the function, no returns, just set 'ret' */
|
||||
if (UNLIKELY(bpy_bm_op_as_py_error(bm) == -1)) {
|
||||
ret = nullptr; /* exception raised above */
|
||||
}
|
||||
else if (bmop.slots_out[0].slot_name == nullptr) {
|
||||
ret = Py_NewRef(Py_None);
|
||||
}
|
||||
else {
|
||||
/* build return value */
|
||||
int i;
|
||||
ret = PyDict_New();
|
||||
|
||||
for (i = 0; bmop.slots_out[i].slot_name; i++) {
|
||||
// BMOpDefine *op_def = opdefines[bmop.type];
|
||||
// BMOSlotType *slot_type = op_def->slot_types_out[i];
|
||||
BMOpSlot *slot = &bmop.slots_out[i];
|
||||
PyObject *item;
|
||||
|
||||
/* this function doesn't throw exceptions */
|
||||
item = bpy_slot_to_py(bm, slot);
|
||||
if (item == nullptr) {
|
||||
item = Py_NewRef(Py_None);
|
||||
}
|
||||
|
||||
#if 1
|
||||
/* Temporary code, strip off `.out` while we keep this convention. */
|
||||
{
|
||||
char slot_name_strip[MAX_SLOTNAME];
|
||||
const char *ch = strchr(slot->slot_name, '.'); /* can't fail! */
|
||||
const int tot = ch - slot->slot_name;
|
||||
BLI_assert(ch != nullptr);
|
||||
memcpy(slot_name_strip, slot->slot_name, tot);
|
||||
slot_name_strip[tot] = '\0';
|
||||
PyDict_SetItemString(ret, slot_name_strip, item);
|
||||
}
|
||||
#else
|
||||
PyDict_SetItemString(ret, slot->slot_name, item);
|
||||
#endif
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
BMO_op_finish(bm, &bmop);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BPy_BMeshOpFunc {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
const char *opname;
|
||||
};
|
||||
|
||||
/**
|
||||
* This is the `__call__` for `bmesh.ops.xxx()`.
|
||||
*/
|
||||
[[nodiscard]] PyObject *BPy_BMO_call(BPy_BMeshOpFunc *self, PyObject *args, PyObject *kw);
|
||||
|
||||
} // namespace blender
|
||||
5715
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.cc
Normal file
5715
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.cc
Normal file
File diff suppressed because it is too large
Load Diff
283
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.hh
Normal file
283
blender-5.2.0/source/blender/python/bmesh/bmesh_py_types.hh
Normal file
@@ -0,0 +1,283 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMesh;
|
||||
struct BMEdge;
|
||||
struct BMElem;
|
||||
struct BMFace;
|
||||
struct BMLoop;
|
||||
struct BMVert;
|
||||
|
||||
extern PyTypeObject BPy_BMesh_Type;
|
||||
extern PyTypeObject BPy_BMVert_Type;
|
||||
extern PyTypeObject BPy_BMEdge_Type;
|
||||
extern PyTypeObject BPy_BMFace_Type;
|
||||
extern PyTypeObject BPy_BMLoop_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMElemSeq_Type;
|
||||
extern PyTypeObject BPy_BMVertSeq_Type;
|
||||
extern PyTypeObject BPy_BMEdgeSeq_Type;
|
||||
extern PyTypeObject BPy_BMFaceSeq_Type;
|
||||
extern PyTypeObject BPy_BMLoopSeq_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMIter_Type;
|
||||
|
||||
#define BPy_BMesh_Check(v) (Py_TYPE(v) == &BPy_BMesh_Type)
|
||||
#define BPy_BMVert_Check(v) (Py_TYPE(v) == &BPy_BMVert_Type)
|
||||
#define BPy_BMEdge_Check(v) (Py_TYPE(v) == &BPy_BMEdge_Type)
|
||||
#define BPy_BMFace_Check(v) (Py_TYPE(v) == &BPy_BMFace_Type)
|
||||
#define BPy_BMLoop_Check(v) (Py_TYPE(v) == &BPy_BMLoop_Type)
|
||||
#define BPy_BMElemSeq_Check(v) (Py_TYPE(v) == &BPy_BMElemSeq_Type)
|
||||
#define BPy_BMVertSeq_Check(v) (Py_TYPE(v) == &BPy_BMVertSeq_Type)
|
||||
#define BPy_BMEdgeSeq_Check(v) (Py_TYPE(v) == &BPy_BMEdgeSeq_Type)
|
||||
#define BPy_BMFaceSeq_Check(v) (Py_TYPE(v) == &BPy_BMFaceSeq_Type)
|
||||
#define BPy_BMLoopSeq_Check(v) (Py_TYPE(v) == &BPy_BMLoopSeq_Type)
|
||||
#define BPy_BMIter_Check(v) (Py_TYPE(v) == &BPy_BMIter_Type)
|
||||
/* trick since we know they share a hash function */
|
||||
#define BPy_BMElem_Check(v) (Py_TYPE(v)->tp_hash == BPy_BMVert_Type.tp_hash)
|
||||
|
||||
/* cast from _any_ bmesh type - they all have BMesh first */
|
||||
struct BPy_BMGeneric {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
};
|
||||
|
||||
/* BPy_BMVert/BPy_BMEdge/BPy_BMFace/BPy_BMLoop can cast to this */
|
||||
struct BPy_BMElem {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMElem *ele;
|
||||
};
|
||||
|
||||
struct BPy_BMesh {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
int flag;
|
||||
};
|
||||
|
||||
/* element types */
|
||||
struct BPy_BMVert {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMVert *v;
|
||||
};
|
||||
|
||||
struct BPy_BMEdge {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMEdge *e;
|
||||
};
|
||||
|
||||
struct BPy_BMFace {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMFace *f;
|
||||
};
|
||||
|
||||
struct BPy_BMLoop {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMLoop *l;
|
||||
};
|
||||
|
||||
/* iterators */
|
||||
|
||||
/* used for ...
|
||||
* - BPy_BMElemSeq_Type
|
||||
* - BPy_BMVertSeq_Type
|
||||
* - BPy_BMEdgeSeq_Type
|
||||
* - BPy_BMFaceSeq_Type
|
||||
* - BPy_BMLoopSeq_Type
|
||||
*/
|
||||
struct BPy_BMElemSeq {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
|
||||
/* if this is a sequence on an existing element,
|
||||
* loops of faces for eg.
|
||||
* If this variable is set, it will be used */
|
||||
|
||||
/* we hold a reference to this.
|
||||
* check in case the owner becomes invalid on access */
|
||||
/* TODO: make this a GC'd object!, will function OK without this though. */
|
||||
BPy_BMElem *py_ele;
|
||||
|
||||
/* iterator type */
|
||||
short itype;
|
||||
};
|
||||
|
||||
struct BPy_BMIter {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMIter iter;
|
||||
};
|
||||
|
||||
void BPy_BM_init_types();
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_types();
|
||||
|
||||
enum {
|
||||
BPY_BMFLAG_NOP = 0, /* do nothing */
|
||||
BPY_BMFLAG_IS_WRAPPED = 1, /* the mesh is owned by editmode */
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMesh_CreatePyObject(BMesh *bm, int flag);
|
||||
[[nodiscard]] PyObject *BPy_BMVert_CreatePyObject(BMesh *bm, BMVert *v);
|
||||
[[nodiscard]] PyObject *BPy_BMEdge_CreatePyObject(BMesh *bm, BMEdge *e);
|
||||
[[nodiscard]] PyObject *BPy_BMFace_CreatePyObject(BMesh *bm, BMFace *f);
|
||||
[[nodiscard]] PyObject *BPy_BMLoop_CreatePyObject(BMesh *bm, BMLoop *l);
|
||||
[[nodiscard]] PyObject *BPy_BMElemSeq_CreatePyObject(BMesh *bm, BPy_BMElem *py_ele, char itype);
|
||||
[[nodiscard]] PyObject *BPy_BMVertSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMEdgeSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMFaceSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopSeq_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMIter_CreatePyObject(BMesh *bm);
|
||||
|
||||
/** Just checks type and creates vert/edge/face/loop. */
|
||||
[[nodiscard]] PyObject *BPy_BMElem_CreatePyObject(BMesh *bm, BMHeader *ele);
|
||||
|
||||
/**
|
||||
* Generic python seq as BMVert/Edge/Face array,
|
||||
* return value must be freed with PyMem_FREE(...);
|
||||
*
|
||||
* The 'bm_r' value is assigned when empty, and used when set.
|
||||
*/
|
||||
[[nodiscard]] void *BPy_BMElem_PySeq_As_Array_FAST(BMesh **r_bm,
|
||||
PyObject *seq_fast,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
char htype,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] void *BPy_BMElem_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
char htype,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] BMVert **BPy_BMVert_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] BMEdge **BPy_BMEdge_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] BMFace **BPy_BMFace_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] BMLoop **BPy_BMLoop_PySeq_As_Array(BMesh **r_bm,
|
||||
PyObject *seq,
|
||||
Py_ssize_t min,
|
||||
Py_ssize_t max,
|
||||
Py_ssize_t *r_seq_num,
|
||||
bool do_unique_check,
|
||||
bool do_bm_check,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMElem_Array_As_Tuple(BMesh *bm, BMHeader **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMVert_Array_As_Tuple(BMesh *bm, BMVert **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMEdge_Array_As_Tuple(BMesh *bm, BMEdge **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMFace_Array_As_Tuple(BMesh *bm, BMFace **elem, Py_ssize_t elem_num);
|
||||
[[nodiscard]] PyObject *BPy_BMLoop_Array_As_Tuple(BMesh *bm,
|
||||
BMLoop *const *elem,
|
||||
Py_ssize_t elem_num);
|
||||
|
||||
[[nodiscard]] int BPy_BMElem_CheckHType(PyTypeObject *type, char htype);
|
||||
/**
|
||||
* Use for error strings only, not thread safe,
|
||||
*
|
||||
* \return a string like '(BMVert/BMEdge/BMFace/BMLoop)'
|
||||
*/
|
||||
[[nodiscard]] char *BPy_BMElem_StringFromHType_ex(char htype, char ret[32]);
|
||||
[[nodiscard]] char *BPy_BMElem_StringFromHType(char htype);
|
||||
|
||||
// void bpy_bm_generic_invalidate(BPy_BMGeneric *self);
|
||||
[[nodiscard]] int bpy_bm_generic_valid_check(BPy_BMGeneric *self);
|
||||
[[nodiscard]] int bpy_bm_generic_valid_check_source(BMesh *bm_source,
|
||||
const char *error_prefix,
|
||||
void **args,
|
||||
uint args_tot) ATTR_NONNULL(1, 2);
|
||||
[[nodiscard]] int bpy_bm_check_uv_select_sync_valid(BMesh *bm, const char *error_prefix);
|
||||
[[nodiscard]] int bpy_bm_uv_layer_offset_or_error(BMesh *bm, const char *error_prefix);
|
||||
[[nodiscard]] int bpy_bm_check_bm_match_or_error(BMesh *bm_a,
|
||||
BMesh *bm_b,
|
||||
const char *error_prefix);
|
||||
|
||||
#define BPY_BM_CHECK_OBJ(obj) \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check((BPy_BMGeneric *)obj) == -1)) { \
|
||||
return NULL; \
|
||||
} \
|
||||
(void)0
|
||||
#define BPY_BM_CHECK_INT(obj) \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check((BPy_BMGeneric *)obj) == -1)) { \
|
||||
return -1; \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Macros like `BPY_BM_CHECK_OBJ/BPY_BM_CHECK_INT` that ensure we're from the right #BMesh.
|
||||
*/
|
||||
#define BPY_BM_CHECK_SOURCE_OBJ(bm, errmsg, ...) \
|
||||
{ \
|
||||
void *_args[] = {__VA_ARGS__}; \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check_source(bm, errmsg, _args, ARRAY_SIZE(_args)) == -1)) \
|
||||
{ \
|
||||
return NULL; \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
#define BPY_BM_CHECK_SOURCE_INT(bm, errmsg, ...) \
|
||||
{ \
|
||||
void *_args[] = {__VA_ARGS__}; \
|
||||
if (UNLIKELY(bpy_bm_generic_valid_check_source(bm, errmsg, _args, ARRAY_SIZE(_args)) == -1)) \
|
||||
{ \
|
||||
return -1; \
|
||||
} \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
#define BPY_BM_IS_VALID(obj) (LIKELY((obj)->bm != NULL))
|
||||
|
||||
#define BM_ITER_BPY_BM_SEQ(ele, iter, bpy_bmelemseq) \
|
||||
for (BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_new( \
|
||||
iter, \
|
||||
(bpy_bmelemseq)->bm, \
|
||||
(bpy_bmelemseq)->itype, \
|
||||
(bpy_bmelemseq)->py_ele ? ((BPy_BMElem *)(bpy_bmelemseq)->py_ele)->ele : NULL); \
|
||||
ele; \
|
||||
BM_CHECK_TYPE_ELEM_ASSIGN(ele) = BM_iter_step(iter))
|
||||
|
||||
} // namespace blender
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,73 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMesh;
|
||||
|
||||
/* All use #BPy_BMLayerAccess struct. */
|
||||
|
||||
extern PyTypeObject BPy_BMLayerAccessVert_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessEdge_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessFace_Type;
|
||||
extern PyTypeObject BPy_BMLayerAccessLoop_Type;
|
||||
|
||||
extern PyTypeObject BPy_BMLayerCollection_Type;
|
||||
extern PyTypeObject BPy_BMLayerItem_Type;
|
||||
|
||||
#define BPy_BMLayerAccess_Check(v) (Py_TYPE(v) == &BPy_BMLayerAccess_Type)
|
||||
#define BPy_BMLayerCollection_Check(v) (Py_TYPE(v) == &BPy_BMLayerCollection_Type)
|
||||
#define BPy_BMLayerItem_Check(v) (Py_TYPE(v) == &BPy_BMLayerItem_Type)
|
||||
|
||||
/** All layers for vert/edge/face/loop. */
|
||||
struct BPy_BMLayerAccess {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
};
|
||||
|
||||
/** Access different layer types deform/uv/vertex-color. */
|
||||
struct BPy_BMLayerCollection {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
int type; /* customdata type - CD_XXX */
|
||||
};
|
||||
|
||||
/** Access a specific layer directly. */
|
||||
struct BPy_BMLayerItem {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
char htype;
|
||||
int type; /* customdata type - CD_XXX */
|
||||
int index; /* index of this layer type */
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMLayerAccess_CreatePyObject(BMesh *bm, char htype);
|
||||
[[nodiscard]] PyObject *BPy_BMLayerCollection_CreatePyObject(BMesh *bm, char htype, int type);
|
||||
[[nodiscard]] PyObject *BPy_BMLayerItem_CreatePyObject(BMesh *bm, char htype, int type, int index);
|
||||
|
||||
void BPy_BM_init_types_customdata();
|
||||
|
||||
/**
|
||||
* \brief `BMElem.__getitem__() / __setitem__()`
|
||||
*
|
||||
* Assume all error checks are done, eg: `uv = vert[uv_layer]`
|
||||
*/
|
||||
[[nodiscard]] PyObject *BPy_BMLayerItem_GetItem(BPy_BMElem *py_ele, BPy_BMLayerItem *py_layer);
|
||||
[[nodiscard]] int BPy_BMLayerItem_SetItem(BPy_BMElem *py_ele,
|
||||
BPy_BMLayerItem *py_layer,
|
||||
PyObject *value);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,787 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines custom-data types which can't be accessed as primitive
|
||||
* Python types such as #MDeformVert. It also exposed UV map data in a way
|
||||
* compatible with the (deprecated) #MLoopUV type.
|
||||
* MLoopUV used to be a struct containing both the UV information and various
|
||||
* selection flags. This has since been split up into a float2 attribute
|
||||
* and three boolean attributes for the selection/pin states.
|
||||
* For backwards compatibility, the original #MLoopUV is emulated in the
|
||||
* python API. This comes at a performance penalty however, and the plan is
|
||||
* to provide direct access to the boolean layers for faster access. Eventually
|
||||
* (probably in 4.0) #BPy_BMLoopUV should be removed on the Python side as well.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BKE_customdata.hh"
|
||||
|
||||
#include "BLI_math_base.h"
|
||||
#include "BLI_math_color.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_deform.hh"
|
||||
|
||||
#include "bmesh.hh"
|
||||
#include "bmesh_py_types_meshdata.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Mesh Loop UV
|
||||
* ************ */
|
||||
|
||||
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
|
||||
|
||||
struct BPy_BMLoopUV {
|
||||
PyObject_HEAD
|
||||
float *uv;
|
||||
/**
|
||||
* Pin may be null, signifying the layer doesn't exist.
|
||||
*
|
||||
* Currently its always created on a #BMesh because adding UV layers to an existing #BMesh is
|
||||
* slow and invalidates existing Python objects having pointers into the original data-blocks
|
||||
* (since adding a layer re-generates all blocks).
|
||||
* But eventually the plan is to lazily allocate the boolean layers "on demand".
|
||||
* Therefore the code handles cases where the pin layer doesn't exist.
|
||||
*/
|
||||
bool *pin;
|
||||
BMLoop *loop;
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmloopuv_uv_doc,
|
||||
"Loop UV (as a 2D Vector).\n"
|
||||
"\n"
|
||||
":type: :class:`mathutils.Vector`\n");
|
||||
static PyObject *bpy_bmloopuv_uv_get(BPy_BMLoopUV *self, void * /*closure*/)
|
||||
{
|
||||
return Vector_CreatePyObject_wrap(self->uv, 2, nullptr);
|
||||
}
|
||||
|
||||
static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
float tvec[2];
|
||||
if (mathutils_array_parse(tvec, 2, 2, value, "BMLoopUV.uv") != -1) {
|
||||
copy_v2_v2(self->uv, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool bpy_bmloopuv_pin_uv_ok_or_error(const BPy_BMLoopUV *self)
|
||||
{
|
||||
if (self->pin == nullptr) {
|
||||
PyErr_SetString(PyExc_RuntimeError,
|
||||
"active uv layer has no associated pin layer. This is a bug!");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmloopuv_pin_uv_doc,
|
||||
"UV pin state.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
static PyObject *bpy_bmloopuv_pin_uv_get(BPy_BMLoopUV *self, void * /*closure*/)
|
||||
{
|
||||
/* A non existing pin layer means nothing is currently pinned. */
|
||||
if (UNLIKELY(!bpy_bmloopuv_pin_uv_ok_or_error(self))) {
|
||||
return nullptr;
|
||||
}
|
||||
return PyBool_FromLong(*self->pin);
|
||||
}
|
||||
|
||||
static int bpy_bmloopuv_pin_uv_set(BPy_BMLoopUV *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
/* TODO: if we add lazy allocation of the associated uv map bool layers to BMesh we need
|
||||
* to add a pin layer and update self->pin in the case of self->pin being nullptr.
|
||||
* This isn't easy to do currently as adding CustomData layers to a BMesh invalidates
|
||||
* existing python objects. So for now lazy allocation isn't done and self->pin should
|
||||
* never be nullptr. */
|
||||
BLI_assert(self->pin);
|
||||
if (UNLIKELY(!bpy_bmloopuv_pin_uv_ok_or_error(self))) {
|
||||
return -1;
|
||||
}
|
||||
*self->pin = PyC_Long_AsBool(value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmloopuv_getseters[] = {
|
||||
/* attributes match rna_def_mloopuv. */
|
||||
{"uv",
|
||||
reinterpret_cast<getter>(bpy_bmloopuv_uv_get),
|
||||
reinterpret_cast<setter>(bpy_bmloopuv_uv_set),
|
||||
bpy_bmloopuv_uv_doc,
|
||||
nullptr},
|
||||
{"pin_uv",
|
||||
reinterpret_cast<getter>(bpy_bmloopuv_pin_uv_get),
|
||||
reinterpret_cast<setter>(bpy_bmloopuv_pin_uv_set),
|
||||
bpy_bmloopuv_pin_uv_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject BPy_BMLoopUV_Type; /* bm.loops.layers.uv.active */
|
||||
|
||||
static void bm_init_types_bmloopuv()
|
||||
{
|
||||
BPy_BMLoopUV_Type.tp_basicsize = sizeof(BPy_BMLoopUV);
|
||||
|
||||
BPy_BMLoopUV_Type.tp_name = "BMLoopUV";
|
||||
|
||||
BPy_BMLoopUV_Type.tp_doc = nullptr; /* todo */
|
||||
|
||||
BPy_BMLoopUV_Type.tp_getset = bpy_bmloopuv_getseters;
|
||||
|
||||
BPy_BMLoopUV_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMLoopUV_Type);
|
||||
}
|
||||
|
||||
int BPy_BMLoopUV_AssignPyObject(BMesh *bm, BMLoop *loop, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMLoopUV_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMLoopUV, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
BPy_BMLoopUV *src = reinterpret_cast<BPy_BMLoopUV *>(value);
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_get(bm);
|
||||
|
||||
float *luv = BM_ELEM_CD_GET_FLOAT_P(loop, offsets.uv);
|
||||
copy_v2_v2(luv, src->uv);
|
||||
if (src->pin) {
|
||||
BM_ELEM_CD_SET_BOOL(loop, offsets.pin, *src->pin);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMLoopUV_CreatePyObject(BMesh *bm, BMLoop *loop, int layer)
|
||||
{
|
||||
BPy_BMLoopUV *self = PyObject_New(BPy_BMLoopUV, &BPy_BMLoopUV_Type);
|
||||
|
||||
const BMUVOffsets offsets = BM_uv_map_offsets_from_layer(bm, layer);
|
||||
|
||||
self->uv = BM_ELEM_CD_GET_FLOAT_P(loop, offsets.uv);
|
||||
self->pin = offsets.pin >= 0 ? BM_ELEM_CD_GET_BOOL_P(loop, offsets.pin) : nullptr;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Loop UV --- */
|
||||
|
||||
/* Mesh Vert Skin
|
||||
* ************ */
|
||||
|
||||
#define BPy_BMVertSkin_Check(v) (Py_TYPE(v) == &BPy_BMVertSkin_Type)
|
||||
|
||||
struct BPy_BMVertSkin {
|
||||
PyObject_HEAD
|
||||
MVertSkin *data;
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_radius_doc,
|
||||
"Vert skin radii (as a 2D Vector).\n"
|
||||
"\n"
|
||||
":type: :class:`mathutils.Vector`\n");
|
||||
static PyObject *bpy_bmvertskin_radius_get(BPy_BMVertSkin *self, void * /*closure*/)
|
||||
{
|
||||
return Vector_CreatePyObject_wrap(self->data->radius, 2, nullptr);
|
||||
}
|
||||
|
||||
static int bpy_bmvertskin_radius_set(BPy_BMVertSkin *self, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
float tvec[2];
|
||||
if (mathutils_array_parse(tvec, 2, 2, value, "BMVertSkin.radius") != -1) {
|
||||
copy_v2_v2(self->data->radius, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_flag__use_root_doc,
|
||||
"Use as root vertex. Setting this flag does not clear other roots in the same mesh island.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmvertskin_flag__use_loose_doc,
|
||||
"Use loose vertex.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
static PyObject *bpy_bmvertskin_flag_get(BPy_BMVertSkin *self, void *flag_p)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(flag_p);
|
||||
return PyBool_FromLong(self->data->flag & flag);
|
||||
}
|
||||
|
||||
static int bpy_bmvertskin_flag_set(BPy_BMVertSkin *self, PyObject *value, void *flag_p)
|
||||
{
|
||||
const eMVertSkinFlag flag = eMVertSkinFlag(POINTER_AS_INT(flag_p));
|
||||
|
||||
switch (PyC_Long_AsBool(value)) {
|
||||
case true:
|
||||
self->data->flag |= flag;
|
||||
return 0;
|
||||
case false:
|
||||
self->data->flag &= ~flag;
|
||||
return 0;
|
||||
default:
|
||||
/* error is set */
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmvertskin_getseters[] = {
|
||||
/* attributes match rna_mesh_gen. */
|
||||
{"radius",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_radius_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_radius_set),
|
||||
bpy_bmvertskin_radius_doc,
|
||||
nullptr},
|
||||
{"use_root",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_flag_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_flag_set),
|
||||
bpy_bmvertskin_flag__use_root_doc,
|
||||
reinterpret_cast<void *>(MVERT_SKIN_ROOT)},
|
||||
{"use_loose",
|
||||
reinterpret_cast<getter>(bpy_bmvertskin_flag_get),
|
||||
reinterpret_cast<setter>(bpy_bmvertskin_flag_set),
|
||||
bpy_bmvertskin_flag__use_loose_doc,
|
||||
reinterpret_cast<void *>(MVERT_SKIN_LOOSE)},
|
||||
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyTypeObject BPy_BMVertSkin_Type; /* bm.loops.layers.skin.active */
|
||||
|
||||
static void bm_init_types_bmvertskin()
|
||||
{
|
||||
BPy_BMVertSkin_Type.tp_basicsize = sizeof(BPy_BMVertSkin);
|
||||
|
||||
BPy_BMVertSkin_Type.tp_name = "BMVertSkin";
|
||||
|
||||
BPy_BMVertSkin_Type.tp_doc = "Skin vertex data for the skin modifier.";
|
||||
|
||||
BPy_BMVertSkin_Type.tp_getset = bpy_bmvertskin_getseters;
|
||||
|
||||
BPy_BMVertSkin_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMVertSkin_Type);
|
||||
}
|
||||
|
||||
int BPy_BMVertSkin_AssignPyObject(MVertSkin *mvertskin, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMVertSkin_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMVertSkin, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*(mvertskin) = *((reinterpret_cast<BPy_BMVertSkin *>(value))->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMVertSkin_CreatePyObject(MVertSkin *mvertskin)
|
||||
{
|
||||
BPy_BMVertSkin *self = PyObject_New(BPy_BMVertSkin, &BPy_BMVertSkin_Type);
|
||||
self->data = mvertskin;
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Vert Skin --- */
|
||||
|
||||
/* Mesh Loop Color
|
||||
* *************** */
|
||||
|
||||
/* This simply provides a color wrapper for
|
||||
* color which uses mathutils callbacks for mathutils.Color
|
||||
*/
|
||||
|
||||
#define MLOOPCOL_FROM_CAPSULE(color_capsule) \
|
||||
((MLoopCol *)PyCapsule_GetPointer(color_capsule, nullptr))
|
||||
|
||||
static void mloopcol_to_float(const MLoopCol *mloopcol, float r_col[4])
|
||||
{
|
||||
rgba_uchar_to_float(r_col, static_cast<const uchar *>(&mloopcol->r));
|
||||
}
|
||||
|
||||
static void mloopcol_from_float(MLoopCol *mloopcol, const float col[4])
|
||||
{
|
||||
rgba_float_to_uchar(static_cast<uchar *>(&mloopcol->r), col);
|
||||
}
|
||||
|
||||
static uchar mathutils_bmloopcol_cb_index = -1;
|
||||
|
||||
static int mathutils_bmloopcol_check(BaseMathObject * /*bmo*/)
|
||||
{
|
||||
/* always ok */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_get(BaseMathObject *bmo, int /*subtype*/)
|
||||
{
|
||||
MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
|
||||
mloopcol_to_float(mloopcol, bmo->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_set(BaseMathObject *bmo, int /*subtype*/)
|
||||
{
|
||||
MLoopCol *mloopcol = MLOOPCOL_FROM_CAPSULE(bmo->cb_user);
|
||||
mloopcol_from_float(mloopcol, bmo->data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_get_index(BaseMathObject *bmo, int subtype, int /*index*/)
|
||||
{
|
||||
/* Lazy, avoid repeating the case statement. */
|
||||
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mathutils_bmloopcol_set_index(BaseMathObject *bmo, int subtype, int index)
|
||||
{
|
||||
const float f = bmo->data[index];
|
||||
|
||||
/* Lazy, avoid repeating the case statement. */
|
||||
if (mathutils_bmloopcol_get(bmo, subtype) == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
bmo->data[index] = f;
|
||||
return mathutils_bmloopcol_set(bmo, subtype);
|
||||
}
|
||||
|
||||
static Mathutils_Callback mathutils_bmloopcol_cb = {
|
||||
mathutils_bmloopcol_check,
|
||||
mathutils_bmloopcol_get,
|
||||
mathutils_bmloopcol_set,
|
||||
mathutils_bmloopcol_get_index,
|
||||
mathutils_bmloopcol_set_index,
|
||||
};
|
||||
|
||||
static void bm_init_types_bmloopcol()
|
||||
{
|
||||
/* pass */
|
||||
mathutils_bmloopcol_cb_index = Mathutils_RegisterCallback(&mathutils_bmloopcol_cb);
|
||||
}
|
||||
|
||||
int BPy_BMLoopColor_AssignPyObject(MLoopCol *mloopcol, PyObject *value)
|
||||
{
|
||||
float tvec[4];
|
||||
if (mathutils_array_parse(tvec, 4, 4, value, "BMLoopCol") != -1) {
|
||||
mloopcol_from_float(mloopcol, tvec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMLoopColor_CreatePyObject(MLoopCol *mloopcol)
|
||||
{
|
||||
PyObject *color_capsule;
|
||||
color_capsule = PyCapsule_New(mloopcol, nullptr, nullptr);
|
||||
return Vector_CreatePyObject_cb(color_capsule, 4, mathutils_bmloopcol_cb_index, 0);
|
||||
}
|
||||
|
||||
#undef MLOOPCOL_FROM_CAPSULE
|
||||
|
||||
/* --- End Mesh Loop Color --- */
|
||||
|
||||
/* Mesh Deform Vert
|
||||
* **************** */
|
||||
|
||||
/**
|
||||
* This is python type wraps a deform vert as a python dictionary,
|
||||
* hiding the #MDeformWeight on access, since the mapping is very close, eg:
|
||||
*
|
||||
* \code{.c}
|
||||
* weight = BKE_defvert_find_weight(dv, group_nr);
|
||||
* BKE_defvert_remove_group(dv, dw)
|
||||
* \endcode
|
||||
*
|
||||
* \code{.py}
|
||||
* weight = dv[group_nr]
|
||||
* del dv[group_nr]
|
||||
* \endcode
|
||||
*
|
||||
* \note There is nothing BMesh specific here,
|
||||
* its only that BMesh is the only part of blender that uses a hand written API like this.
|
||||
* This type could eventually be used to access lattice weights.
|
||||
*
|
||||
* \note Many of Blender-API's dictionary-like-wrappers act like ordered dictionaries,
|
||||
* This is intentionally _not_ ordered, the weights can be in any order and it won't matter,
|
||||
* the order should not be used in the API in any meaningful way (as with a python dict)
|
||||
* only expose as mapping, not a sequence.
|
||||
*/
|
||||
|
||||
#define BPy_BMDeformVert_Check(v) (Py_TYPE(v) == &BPy_BMDeformVert_Type)
|
||||
|
||||
struct BPy_BMDeformVert {
|
||||
PyObject_HEAD
|
||||
MDeformVert *data;
|
||||
};
|
||||
|
||||
/* Mapping Protocols
|
||||
* ================= */
|
||||
|
||||
static Py_ssize_t bpy_bmdeformvert_len(BPy_BMDeformVert *self)
|
||||
{
|
||||
return self->data->totweight;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmdeformvert_subscript(BPy_BMDeformVert *self, PyObject *key)
|
||||
{
|
||||
if (PyIndex_Check(key)) {
|
||||
int i;
|
||||
i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, i);
|
||||
|
||||
if (dw == nullptr) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"key not found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PyFloat_FromDouble(dw->weight);
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "BMDeformVert keys must be integers, not %.200s", Py_TYPE(key)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int bpy_bmdeformvert_ass_subscript(BPy_BMDeformVert *self, PyObject *key, PyObject *value)
|
||||
{
|
||||
if (PyIndex_Check(key)) {
|
||||
int i;
|
||||
|
||||
i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (value) {
|
||||
/* Handle `dvert[group_index] = 0.5`. */
|
||||
if (i < 0) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"weight keys cannot be negative");
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_ensure_index(self->data, i);
|
||||
const float f = PyFloat_AsDouble(value);
|
||||
if (f == -1 && PyErr_Occurred()) { /* Parsed key not a number. */
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"BMDeformVert[key] = x: "
|
||||
"assigned value not a number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
dw->weight = clamp_f(f, 0.0f, 1.0f);
|
||||
}
|
||||
else {
|
||||
/* Handle `del dvert[group_index]`. */
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, i);
|
||||
|
||||
if (dw == nullptr) {
|
||||
PyErr_SetString(PyExc_KeyError,
|
||||
"del BMDeformVert[key]: "
|
||||
"key not found");
|
||||
}
|
||||
BKE_defvert_remove_group(self->data, dw);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "BMDeformVert keys must be integers, not %.200s", Py_TYPE(key)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int bpy_bmdeformvert_contains(BPy_BMDeformVert *self, PyObject *value)
|
||||
{
|
||||
const int key = PyLong_AsSsize_t(value);
|
||||
|
||||
if (key == -1 && PyErr_Occurred()) {
|
||||
PyErr_SetString(PyExc_TypeError, "BMDeformVert.__contains__: expected an int");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (BKE_defvert_find_index(self->data, key) != nullptr) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* only defined for __contains__ */
|
||||
static PySequenceMethods bpy_bmdeformvert_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(bpy_bmdeformvert_len),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/* NOTE: if this is set #PySequence_Check() returns True,
|
||||
* but in this case we don't want to be treated as a seq. */
|
||||
/*sq_item*/ nullptr,
|
||||
/*was_sq_slice*/ nullptr, /* DEPRECATED. */
|
||||
/*sq_ass_item*/ nullptr,
|
||||
/*was_sq_ass_slice*/ nullptr, /* DEPRECATED. */
|
||||
/*sq_contains*/ reinterpret_cast<objobjproc>(bpy_bmdeformvert_contains),
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods bpy_bmdeformvert_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(bpy_bmdeformvert_len),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(bpy_bmdeformvert_subscript),
|
||||
/*mp_ass_subscript*/ reinterpret_cast<objobjargproc>(bpy_bmdeformvert_ass_subscript),
|
||||
};
|
||||
|
||||
/* Methods
|
||||
* ======= */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_keys_doc,
|
||||
".. method:: keys()\n"
|
||||
"\n"
|
||||
" Return the group indices used by this vertex\n"
|
||||
" (matching Python's dict.keys() functionality).\n"
|
||||
"\n"
|
||||
" :return: The deform group indices this vertex uses.\n"
|
||||
" :rtype: list[int]\n");
|
||||
static PyObject *bpy_bmdeformvert_keys(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
PyList_SET_ITEM(ret, i, PyLong_FromLong(dw->def_nr));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_values_doc,
|
||||
".. method:: values()\n"
|
||||
"\n"
|
||||
" Return the weights of the deform vertex\n"
|
||||
" (matching Python's dict.values() functionality).\n"
|
||||
"\n"
|
||||
" :return: The weights that influence this vertex\n"
|
||||
" :rtype: list[float]\n");
|
||||
static PyObject *bpy_bmdeformvert_values(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
PyList_SET_ITEM(ret, i, PyFloat_FromDouble(dw->weight));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_items_doc,
|
||||
".. method:: items()\n"
|
||||
"\n"
|
||||
" Return (group, weight) pairs for this vertex\n"
|
||||
" (matching Python's dict.items() functionality).\n"
|
||||
"\n"
|
||||
" :return: (key, value) pairs for each deform weight of this vertex.\n"
|
||||
" :rtype: list[tuple[int, float]]\n");
|
||||
static PyObject *bpy_bmdeformvert_items(BPy_BMDeformVert *self)
|
||||
{
|
||||
PyObject *ret;
|
||||
PyObject *item;
|
||||
int i;
|
||||
MDeformWeight *dw = self->data->dw;
|
||||
|
||||
ret = PyList_New(self->data->totweight);
|
||||
for (i = 0; i < self->data->totweight; i++, dw++) {
|
||||
item = PyTuple_New(2);
|
||||
PyTuple_SET_ITEMS(item, PyLong_FromLong(dw->def_nr), PyFloat_FromDouble(dw->weight));
|
||||
PyList_SET_ITEM(ret, i, item);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_get_doc,
|
||||
".. method:: get(key, default=None)\n"
|
||||
"\n"
|
||||
" Returns the deform weight matching the key or default\n"
|
||||
" when not found (matches Python's dictionary function of the same name).\n"
|
||||
"\n"
|
||||
" :param key: The vertex group index.\n"
|
||||
" :type key: int\n"
|
||||
" :param default: Optional argument for the value to return if\n"
|
||||
" *key* is not found.\n"
|
||||
" :type default: Any\n"
|
||||
" :return: The deform weight or the default when not found.\n"
|
||||
" :rtype: float | Any\n");
|
||||
static PyObject *bpy_bmdeformvert_get(BPy_BMDeformVert *self, PyObject *args)
|
||||
{
|
||||
int key;
|
||||
PyObject *def = Py_None;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "i|O:get", &key, &def)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MDeformWeight *dw = BKE_defvert_find_index(self->data, key);
|
||||
|
||||
if (dw) {
|
||||
return PyFloat_FromDouble(dw->weight);
|
||||
}
|
||||
|
||||
return Py_NewRef(def);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmdeformvert_clear_doc,
|
||||
".. method:: clear()\n"
|
||||
"\n"
|
||||
" Clears all weights.\n");
|
||||
static PyObject *bpy_bmdeformvert_clear(BPy_BMDeformVert *self)
|
||||
{
|
||||
BKE_defvert_clear(self->data);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef bpy_bmdeformvert_methods[] = {
|
||||
{"keys",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_keys),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_keys_doc},
|
||||
{"values",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_values),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_values_doc},
|
||||
{"items",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_items),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_items_doc},
|
||||
{"get",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_get),
|
||||
METH_VARARGS,
|
||||
bpy_bmdeformvert_get_doc},
|
||||
/* BMESH_TODO `pop`, `popitem`, `update`. */
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmdeformvert_clear),
|
||||
METH_NOARGS,
|
||||
bpy_bmdeformvert_clear_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyTypeObject BPy_BMDeformVert_Type; /* bm.loops.layers.uv.active */
|
||||
|
||||
static void bm_init_types_bmdvert()
|
||||
{
|
||||
BPy_BMDeformVert_Type.tp_basicsize = sizeof(BPy_BMDeformVert);
|
||||
|
||||
BPy_BMDeformVert_Type.tp_name = "BMDeformVert";
|
||||
|
||||
BPy_BMDeformVert_Type.tp_doc = nullptr; /* todo */
|
||||
|
||||
BPy_BMDeformVert_Type.tp_as_sequence = &bpy_bmdeformvert_as_sequence;
|
||||
BPy_BMDeformVert_Type.tp_as_mapping = &bpy_bmdeformvert_as_mapping;
|
||||
|
||||
BPy_BMDeformVert_Type.tp_methods = bpy_bmdeformvert_methods;
|
||||
|
||||
BPy_BMDeformVert_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMDeformVert_Type);
|
||||
}
|
||||
|
||||
int BPy_BMDeformVert_AssignPyObject(MDeformVert *dvert, PyObject *value)
|
||||
{
|
||||
if (UNLIKELY(!BPy_BMDeformVert_Check(value))) {
|
||||
PyErr_Format(PyExc_TypeError, "expected BMDeformVert, not a %.200s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
MDeformVert *dvert_src = (reinterpret_cast<BPy_BMDeformVert *>(value))->data;
|
||||
if (LIKELY(dvert != dvert_src)) {
|
||||
BKE_defvert_copy(dvert, dvert_src);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *BPy_BMDeformVert_CreatePyObject(MDeformVert *dvert)
|
||||
{
|
||||
BPy_BMDeformVert *self = PyObject_New(BPy_BMDeformVert, &BPy_BMDeformVert_Type);
|
||||
self->data = dvert;
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/* --- End Mesh Deform Vert --- */
|
||||
|
||||
void BPy_BM_init_types_meshdata()
|
||||
{
|
||||
bm_init_types_bmloopuv();
|
||||
bm_init_types_bmloopcol();
|
||||
bm_init_types_bmdvert();
|
||||
bm_init_types_bmvertskin();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,47 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPy_BMLoopUV_Type;
|
||||
extern PyTypeObject BPy_BMDeformVert_Type;
|
||||
|
||||
#define BPy_BMLoopUV_Check(v) (Py_TYPE(v) == &BPy_BMLoopUV_Type)
|
||||
|
||||
struct BPy_BMGenericMeshData {
|
||||
PyObject_HEAD
|
||||
void *data;
|
||||
};
|
||||
|
||||
struct MDeformVert;
|
||||
struct MLoopCol;
|
||||
struct MVertSkin;
|
||||
struct BMesh;
|
||||
|
||||
[[nodiscard]] int BPy_BMLoopUV_AssignPyObject(struct BMesh *bm, BMLoop *loop, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopUV_CreatePyObject(struct BMesh *bm, BMLoop *loop, int layer);
|
||||
|
||||
[[nodiscard]] int BPy_BMVertSkin_AssignPyObject(struct MVertSkin *mvertskin, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMVertSkin_CreatePyObject(struct MVertSkin *mvertskin);
|
||||
|
||||
[[nodiscard]] int BPy_BMLoopColor_AssignPyObject(struct MLoopCol *mloopcol, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMLoopColor_CreatePyObject(struct MLoopCol *mloopcol);
|
||||
|
||||
[[nodiscard]] int BPy_BMDeformVert_AssignPyObject(struct MDeformVert *dvert, PyObject *value);
|
||||
[[nodiscard]] PyObject *BPy_BMDeformVert_CreatePyObject(struct MDeformVert *dvert);
|
||||
|
||||
/* call to init all types */
|
||||
void BPy_BM_init_types_meshdata();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,491 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*
|
||||
* This file defines the types for 'BMesh.select_history'
|
||||
* sequence and iterator.
|
||||
*
|
||||
* select_history is very loosely based on pythons set() type,
|
||||
* since items can only exist once. however they do have an order.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
#include "bmesh_py_types.hh"
|
||||
#include "bmesh_py_types_select.hh"
|
||||
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_active_doc,
|
||||
"The last selected element or None (read-only).\n"
|
||||
"\n"
|
||||
":type: :class:`bmesh.types.BMVert` | "
|
||||
":class:`bmesh.types.BMEdge` | :class:`bmesh.types.BMFace` | None\n");
|
||||
static PyObject *bpy_bmeditselseq_active_get(BPy_BMEditSelSeq *self, void * /*closure*/)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((ese = static_cast<BMEditSelection *>(self->bm->selected.last))) {
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_bmeditselseq_getseters[] = {
|
||||
{"active",
|
||||
reinterpret_cast<getter>(bpy_bmeditselseq_active_get),
|
||||
static_cast<setter>(nullptr),
|
||||
bpy_bmeditselseq_active_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_validate_doc,
|
||||
".. method:: validate()\n"
|
||||
"\n"
|
||||
" Ensures all elements in the selection history are selected.\n");
|
||||
static PyObject *bpy_bmeditselseq_validate(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
BM_select_history_validate(self->bm);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_clear_doc,
|
||||
".. method:: clear()\n"
|
||||
"\n"
|
||||
" Empties the selection history.\n");
|
||||
static PyObject *bpy_bmeditselseq_clear(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
BM_select_history_clear(self->bm);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_add_doc,
|
||||
".. method:: add(element)\n"
|
||||
"\n"
|
||||
" Add an element to the selection history (no action taken if its already added).\n"
|
||||
"\n"
|
||||
" :param element: The element to add.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_add(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.add(...)";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
BM_select_history_store(self->bm, value->ele);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_remove_doc,
|
||||
".. method:: remove(element)\n"
|
||||
"\n"
|
||||
" Remove an element from the selection history.\n"
|
||||
"\n"
|
||||
" :param element: The element to remove.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_remove(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.remove(...)";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
if (BM_select_history_remove(self->bm, value->ele) == false) {
|
||||
PyErr_Format(PyExc_ValueError, "%s: element not found in selection history", error_prefix);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_bmeditselseq_discard_doc,
|
||||
".. method:: discard(element)\n"
|
||||
"\n"
|
||||
" Discard an element from the selection history.\n"
|
||||
"\n"
|
||||
" Like remove but doesn't raise an error when the element is not in the selection list.\n"
|
||||
"\n"
|
||||
" :param element: The element to discard.\n"
|
||||
" :type element: :class:`BMVert` | :class:`BMEdge` | :class:`BMFace`\n");
|
||||
static PyObject *bpy_bmeditselseq_discard(BPy_BMEditSelSeq *self, BPy_BMElem *value)
|
||||
{
|
||||
const char *error_prefix = "select_history.discard()";
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if ((BPy_BMVert_Check(value) || BPy_BMEdge_Check(value) || BPy_BMFace_Check(value)) == false) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a BMVert/BMedge/BMFace not a %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BPY_BM_CHECK_SOURCE_OBJ(self->bm, error_prefix, value);
|
||||
|
||||
BM_select_history_remove(self->bm, value->ele);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef bpy_bmeditselseq_methods[] = {
|
||||
{"validate",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_validate),
|
||||
METH_NOARGS,
|
||||
bpy_bmeditselseq_validate_doc},
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_clear),
|
||||
METH_NOARGS,
|
||||
bpy_bmeditselseq_clear_doc},
|
||||
|
||||
{"add", reinterpret_cast<PyCFunction>(bpy_bmeditselseq_add), METH_O, bpy_bmeditselseq_add_doc},
|
||||
{"remove",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_remove),
|
||||
METH_O,
|
||||
bpy_bmeditselseq_remove_doc},
|
||||
{"discard",
|
||||
reinterpret_cast<PyCFunction>(bpy_bmeditselseq_discard),
|
||||
METH_O,
|
||||
bpy_bmeditselseq_discard_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Sequences
|
||||
* ========= */
|
||||
|
||||
static Py_ssize_t bpy_bmeditselseq_length(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
return self->bm->selected.count();
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript_int(BPy_BMEditSelSeq *self, Py_ssize_t keynum)
|
||||
{
|
||||
BMEditSelection *ese;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
if (keynum < 0) {
|
||||
ese = static_cast<BMEditSelection *>(BLI_rfindlink(&self->bm->selected, -1 - keynum));
|
||||
}
|
||||
else {
|
||||
ese = static_cast<BMEditSelection *>(BLI_findlink(&self->bm->selected, keynum));
|
||||
}
|
||||
|
||||
if (ese) {
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_IndexError, "BMElemSeq[index]: index %d out of range", keynum);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript_slice(BPy_BMEditSelSeq *self,
|
||||
Py_ssize_t start,
|
||||
Py_ssize_t stop)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
PyObject *list;
|
||||
BMEditSelection *ese;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
|
||||
list = PyList_New(0);
|
||||
|
||||
/* First loop up-until the start. */
|
||||
for (ese = static_cast<BMEditSelection *>(self->bm->selected.first); ese; ese = ese->next) {
|
||||
if (count == start) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
}
|
||||
|
||||
/* Add items until stop. */
|
||||
for (; ese; ese = ese->next) {
|
||||
PyList_APPEND(list, BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head));
|
||||
count++;
|
||||
if (count == stop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditselseq_subscript(BPy_BMEditSelSeq *self, PyObject *key)
|
||||
{
|
||||
/* don't need error check here */
|
||||
if (PyIndex_Check(key)) {
|
||||
const Py_ssize_t i = PyNumber_AsSsize_t(key, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
return bpy_bmeditselseq_subscript_int(self, i);
|
||||
}
|
||||
if (PySlice_Check(key)) {
|
||||
PySliceObject *key_slice = reinterpret_cast<PySliceObject *>(key);
|
||||
Py_ssize_t step = 1;
|
||||
|
||||
if (key_slice->step != Py_None && !_PyEval_SliceIndex(key_slice->step, &step)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (step != 1) {
|
||||
PyErr_SetString(PyExc_TypeError, "BMElemSeq[slice]: slice steps not supported");
|
||||
return nullptr;
|
||||
}
|
||||
if (key_slice->start == Py_None && key_slice->stop == Py_None) {
|
||||
return bpy_bmeditselseq_subscript_slice(self, 0, PY_SSIZE_T_MAX);
|
||||
}
|
||||
|
||||
Py_ssize_t start = 0, stop = PY_SSIZE_T_MAX;
|
||||
|
||||
/* avoid PySlice_GetIndicesEx because it needs to know the length ahead of time. */
|
||||
if (key_slice->start != Py_None && !_PyEval_SliceIndex(key_slice->start, &start)) {
|
||||
return nullptr;
|
||||
}
|
||||
if (key_slice->stop != Py_None && !_PyEval_SliceIndex(key_slice->stop, &stop)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (start < 0 || stop < 0) {
|
||||
/* only get the length for negative values */
|
||||
const Py_ssize_t len = bpy_bmeditselseq_length(self);
|
||||
if (start < 0) {
|
||||
start += len;
|
||||
CLAMP_MIN(start, 0);
|
||||
}
|
||||
if (stop < 0) {
|
||||
stop += len;
|
||||
CLAMP_MIN(stop, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (stop - start <= 0) {
|
||||
return PyList_New(0);
|
||||
}
|
||||
|
||||
return bpy_bmeditselseq_subscript_slice(self, start, stop);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_AttributeError, "BMElemSeq[key]: invalid key, key must be an int");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int bpy_bmeditselseq_contains(BPy_BMEditSelSeq *self, PyObject *value)
|
||||
{
|
||||
BPy_BMElem *value_bm_ele;
|
||||
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
value_bm_ele = reinterpret_cast<BPy_BMElem *>(value);
|
||||
if (value_bm_ele->bm == self->bm) {
|
||||
return BM_select_history_check(self->bm, value_bm_ele->ele);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PySequenceMethods bpy_bmeditselseq_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(bpy_bmeditselseq_length),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/* Only set this so `PySequence_Check()` returns True. */
|
||||
/*sq_item*/ reinterpret_cast<ssizeargfunc>(bpy_bmeditselseq_subscript_int),
|
||||
/*was_sq_slice*/ nullptr,
|
||||
/*sq_ass_item*/ nullptr,
|
||||
/*was_sq_ass_slice*/ nullptr,
|
||||
/*sq_contains*/ reinterpret_cast<objobjproc>(bpy_bmeditselseq_contains),
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods bpy_bmeditselseq_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(bpy_bmeditselseq_length),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(bpy_bmeditselseq_subscript),
|
||||
/*mp_ass_subscript*/ static_cast<objobjargproc>(nullptr),
|
||||
};
|
||||
|
||||
/* Iterator
|
||||
* -------- */
|
||||
|
||||
static PyObject *bpy_bmeditselseq_iter(BPy_BMEditSelSeq *self)
|
||||
{
|
||||
BPy_BMEditSelIter *py_iter;
|
||||
|
||||
BPY_BM_CHECK_OBJ(self);
|
||||
py_iter = reinterpret_cast<BPy_BMEditSelIter *>(BPy_BMEditSelIter_CreatePyObject(self->bm));
|
||||
py_iter->ese = static_cast<BMEditSelection *>(self->bm->selected.first);
|
||||
return reinterpret_cast<PyObject *>(py_iter);
|
||||
}
|
||||
|
||||
static PyObject *bpy_bmeditseliter_next(BPy_BMEditSelIter *self)
|
||||
{
|
||||
BMEditSelection *ese = self->ese;
|
||||
if (ese == nullptr) {
|
||||
PyErr_SetNone(PyExc_StopIteration);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
self->ese = ese->next;
|
||||
return BPy_BMElem_CreatePyObject(self->bm, &ese->ele->head);
|
||||
}
|
||||
|
||||
PyTypeObject BPy_BMEditSelSeq_Type;
|
||||
PyTypeObject BPy_BMEditSelIter_Type;
|
||||
|
||||
PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm)
|
||||
{
|
||||
BPy_BMEditSelSeq *self = PyObject_New(BPy_BMEditSelSeq, &BPy_BMEditSelSeq_Type);
|
||||
self->bm = bm;
|
||||
/* caller must initialize 'iter' member */
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm)
|
||||
{
|
||||
BPy_BMEditSelIter *self = PyObject_New(BPy_BMEditSelIter, &BPy_BMEditSelIter_Type);
|
||||
self->bm = bm;
|
||||
/* caller must initialize 'iter' member */
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void BPy_BM_init_types_select()
|
||||
{
|
||||
BPy_BMEditSelSeq_Type.tp_basicsize = sizeof(BPy_BMEditSelSeq);
|
||||
BPy_BMEditSelIter_Type.tp_basicsize = sizeof(BPy_BMEditSelIter);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_name = "BMEditSelSeq";
|
||||
BPy_BMEditSelIter_Type.tp_name = "BMEditSelIter";
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_doc = nullptr; /* todo */
|
||||
BPy_BMEditSelIter_Type.tp_doc = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_repr = static_cast<reprfunc>(nullptr);
|
||||
BPy_BMEditSelIter_Type.tp_repr = static_cast<reprfunc>(nullptr);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_getset = bpy_bmeditselseq_getseters;
|
||||
BPy_BMEditSelIter_Type.tp_getset = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_methods = bpy_bmeditselseq_methods;
|
||||
BPy_BMEditSelIter_Type.tp_methods = nullptr;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_as_sequence = &bpy_bmeditselseq_as_sequence;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_as_mapping = &bpy_bmeditselseq_as_mapping;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_iter = reinterpret_cast<getiterfunc>(bpy_bmeditselseq_iter);
|
||||
|
||||
/* Only 1 iterator so far. */
|
||||
BPy_BMEditSelIter_Type.tp_iter = PyObject_SelfIter;
|
||||
BPy_BMEditSelIter_Type.tp_iternext = reinterpret_cast<iternextfunc>(bpy_bmeditseliter_next);
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_dealloc = nullptr; //(destructor)bpy_bmeditselseq_dealloc;
|
||||
BPy_BMEditSelIter_Type.tp_dealloc = nullptr; //(destructor)bpy_bmvert_dealloc;
|
||||
|
||||
BPy_BMEditSelSeq_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
BPy_BMEditSelIter_Type.tp_flags = Py_TPFLAGS_DEFAULT;
|
||||
|
||||
PyType_Ready(&BPy_BMEditSelSeq_Type);
|
||||
PyType_Ready(&BPy_BMEditSelIter_Type);
|
||||
}
|
||||
|
||||
/* utility function */
|
||||
|
||||
int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value)
|
||||
{
|
||||
const char *error_prefix = "BMesh.select_history = value";
|
||||
BPY_BM_CHECK_INT(self);
|
||||
|
||||
BMesh *bm = self->bm;
|
||||
|
||||
Py_ssize_t value_num;
|
||||
BMElem **value_array = static_cast<BMElem **>(
|
||||
BPy_BMElem_PySeq_As_Array(&bm,
|
||||
value,
|
||||
0,
|
||||
PY_SSIZE_T_MAX,
|
||||
&value_num,
|
||||
BM_VERT | BM_EDGE | BM_FACE,
|
||||
true,
|
||||
true,
|
||||
error_prefix));
|
||||
|
||||
if (value_array == nullptr) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
BM_select_history_clear(bm);
|
||||
|
||||
for (Py_ssize_t i = 0; i < value_num; i++) {
|
||||
BM_select_history_store_notest(bm, value_array[i]);
|
||||
}
|
||||
|
||||
PyMem_FREE(value_array);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,48 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bmesh.hh"
|
||||
|
||||
struct BBMesh;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BMEditSelection;
|
||||
struct BPy_BMesh;
|
||||
|
||||
extern PyTypeObject BPy_BMEditSelSeq_Type;
|
||||
extern PyTypeObject BPy_BMEditSelIter_Type;
|
||||
|
||||
#define BPy_BMSelectHistory_Check(v) (Py_TYPE(v) == &BPy_BMEditSelSeq_Type)
|
||||
#define BPy_BMSelectHistoryIter_Check(v) (Py_TYPE(v) == &BPy_BMEditSelIter_Type)
|
||||
|
||||
struct BPy_BMEditSelSeq {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
};
|
||||
|
||||
struct BPy_BMEditSelIter {
|
||||
PyObject_HEAD
|
||||
BMesh *bm; /* keep first */
|
||||
BMEditSelection *ese;
|
||||
};
|
||||
|
||||
void BPy_BM_init_types_select();
|
||||
|
||||
[[nodiscard]] PyObject *BPy_BMEditSel_CreatePyObject(BMesh *bm);
|
||||
[[nodiscard]] PyObject *BPy_BMEditSelIter_CreatePyObject(BMesh *bm);
|
||||
/**
|
||||
* \note doesn't actually check selection.
|
||||
*/
|
||||
[[nodiscard]] int BPy_BMEditSel_Assign(BPy_BMesh *self, PyObject *value);
|
||||
|
||||
} // namespace blender
|
||||
1019
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.cc
Normal file
1019
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.cc
Normal file
File diff suppressed because it is too large
Load Diff
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.hh
Normal file
17
blender-5.2.0/source/blender/python/bmesh/bmesh_py_utils.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2012 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pybmesh
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_bmesh_utils();
|
||||
|
||||
} // namespace blender
|
||||
52
blender-5.2.0/source/blender/python/generic/CMakeLists.txt
Normal file
52
blender-5.2.0/source/blender/python/generic/CMakeLists.txt
Normal file
@@ -0,0 +1,52 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
bl_math_py_api.cc
|
||||
blf_py_api.cc
|
||||
bpy_threads.cc
|
||||
idprop_py_api.cc
|
||||
idprop_py_ui_api.cc
|
||||
imbuf_py_api.cc
|
||||
py_capi_rna.cc
|
||||
py_capi_utils.cc
|
||||
python_compat.cc
|
||||
|
||||
bl_math_py_api.hh
|
||||
blf_py_api.hh
|
||||
idprop_py_api.hh
|
||||
idprop_py_ui_api.hh
|
||||
imbuf_py_api.hh
|
||||
py_capi_rna.hh
|
||||
py_capi_utils.hh
|
||||
python_compat.hh
|
||||
|
||||
# header-only
|
||||
python_utildefines.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
)
|
||||
|
||||
if(WITH_PYTHON_MODULE)
|
||||
add_definitions(-DWITH_PYTHON_MODULE)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python_ext "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
156
blender-5.2.0/source/blender/python/generic/bl_math_py_api.cc
Normal file
156
blender-5.2.0/source/blender/python/generic/bl_math_py_api.cc
Normal file
@@ -0,0 +1,156 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup pygen
|
||||
*
|
||||
* This file defines the 'bl_math' module, a module for math utilities.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bl_math_py_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Python Functions
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_bl_math_clamp_doc,
|
||||
".. function:: clamp(value, min=0, max=1)\n"
|
||||
"\n"
|
||||
" Clamps the float value between minimum and maximum. To avoid\n"
|
||||
" confusion, any call must use either one or all three arguments.\n"
|
||||
"\n"
|
||||
" :param value: The value to clamp.\n"
|
||||
" :type value: float\n"
|
||||
" :param min: The minimum value, defaults to 0.\n"
|
||||
" :type min: float\n"
|
||||
" :param max: The maximum value, defaults to 1.\n"
|
||||
" :type max: float\n"
|
||||
" :return: The clamped value.\n"
|
||||
" :rtype: float\n");
|
||||
static PyObject *py_bl_math_clamp(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
double x, minv = 0.0, maxv = 1.0;
|
||||
|
||||
if (PyTuple_Size(args) <= 1) {
|
||||
if (!PyArg_ParseTuple(args, "d:clamp", &x)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!PyArg_ParseTuple(args, "ddd:clamp", &x, &minv, &maxv)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
CLAMP(x, minv, maxv);
|
||||
|
||||
return PyFloat_FromDouble(x);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_bl_math_lerp_doc,
|
||||
".. function:: lerp(from_value, to_value, factor)\n"
|
||||
"\n"
|
||||
" Linearly interpolate between two float values based on factor.\n"
|
||||
"\n"
|
||||
" :param from_value: The value to return when factor is 0.\n"
|
||||
" :type from_value: float\n"
|
||||
" :param to_value: The value to return when factor is 1.\n"
|
||||
" :type to_value: float\n"
|
||||
" :param factor: The interpolation value, normally in [0.0, 1.0].\n"
|
||||
" :type factor: float\n"
|
||||
" :return: The interpolated value.\n"
|
||||
" :rtype: float\n");
|
||||
static PyObject *py_bl_math_lerp(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
double a, b, x;
|
||||
if (!PyArg_ParseTuple(args, "ddd:lerp", &a, &b, &x)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return PyFloat_FromDouble(a * (1.0 - x) + b * x);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_bl_math_smoothstep_doc,
|
||||
".. function:: smoothstep(from_value, to_value, value)\n"
|
||||
"\n"
|
||||
" Performs smooth interpolation between 0 and 1 as value changes between from and "
|
||||
"to values.\n"
|
||||
" Outside the range the function returns the same value as the nearest edge.\n"
|
||||
"\n"
|
||||
" :param from_value: The edge value where the result is 0.\n"
|
||||
" :type from_value: float\n"
|
||||
" :param to_value: The edge value where the result is 1.\n"
|
||||
" :type to_value: float\n"
|
||||
" :param value: The interpolation value.\n"
|
||||
" :type value: float\n"
|
||||
" :return: The interpolated value in [0.0, 1.0].\n"
|
||||
" :rtype: float\n");
|
||||
static PyObject *py_bl_math_smoothstep(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
double a, b, x;
|
||||
if (!PyArg_ParseTuple(args, "ddd:smoothstep", &a, &b, &x)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
double t = (x - a) / (b - a);
|
||||
|
||||
CLAMP(t, 0.0, 1.0);
|
||||
|
||||
return PyFloat_FromDouble(t * t * (3.0 - 2.0 * t));
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module Definition
|
||||
* \{ */
|
||||
|
||||
static PyMethodDef M_bl_math_methods[] = {
|
||||
{"clamp", static_cast<PyCFunction>(py_bl_math_clamp), METH_VARARGS, py_bl_math_clamp_doc},
|
||||
{"lerp", static_cast<PyCFunction>(py_bl_math_lerp), METH_VARARGS, py_bl_math_lerp_doc},
|
||||
{"smoothstep",
|
||||
static_cast<PyCFunction>(py_bl_math_smoothstep),
|
||||
METH_VARARGS,
|
||||
py_bl_math_smoothstep_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
M_bl_math_doc,
|
||||
"Miscellaneous math utilities module.");
|
||||
static PyModuleDef M_bl_math_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bl_math",
|
||||
/*m_doc*/ M_bl_math_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ M_bl_math_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC BPyInit_bl_math()
|
||||
{
|
||||
PyObject *submodule = PyModule_Create(&M_bl_math_module_def);
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,18 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyMODINIT_FUNC BPyInit_bl_math();
|
||||
|
||||
} // namespace blender
|
||||
852
blender-5.2.0/source/blender/python/generic/blf_py_api.cc
Normal file
852
blender-5.2.0/source/blender/python/generic/blf_py_api.cc
Normal file
@@ -0,0 +1,852 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*
|
||||
* This file defines the `blf` module, used for drawing text to the GPU or image buffers.
|
||||
*/
|
||||
|
||||
/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include "blf_py_api.hh"
|
||||
|
||||
#include "py_capi_utils.hh"
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../../blenfont/BLF_api.hh"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "../../imbuf/IMB_colormanagement.hh"
|
||||
#include "../../imbuf/IMB_imbuf.hh"
|
||||
#include "../../imbuf/IMB_imbuf_types.hh"
|
||||
|
||||
#include "python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "python_utildefines.hh"
|
||||
|
||||
#include "imbuf_py_api.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BPyBLFImBufContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
PyObject *py_imbuf;
|
||||
|
||||
int fontid;
|
||||
BLFBufferState *buffer_state;
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_position_doc,
|
||||
".. function:: position(fontid, x, y, z)\n"
|
||||
"\n"
|
||||
" Set the position for drawing text.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param x: X axis position to draw the text.\n"
|
||||
" :type x: float\n"
|
||||
" :param y: Y axis position to draw the text.\n"
|
||||
" :type y: float\n"
|
||||
" :param z: Z axis position to draw the text (typically 0).\n"
|
||||
" :type z: float\n");
|
||||
static PyObject *py_blf_position(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int fontid;
|
||||
float x, y, z;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ifff:blf.position", &fontid, &x, &y, &z)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_position(fontid, x, y, z);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_size_doc,
|
||||
".. function:: size(fontid, size)\n"
|
||||
"\n"
|
||||
" Set the size for drawing text.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param size: Point size of the font.\n"
|
||||
" :type size: float\n");
|
||||
static PyObject *py_blf_size(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int fontid;
|
||||
float size;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "if:blf.size", &fontid, &size)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_size(fontid, size);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_aspect_doc,
|
||||
".. function:: aspect(fontid, aspect)\n"
|
||||
"\n"
|
||||
" Set the aspect for drawing text.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param aspect: The aspect ratio for non-uniform scaling of text.\n"
|
||||
" :type aspect: float\n");
|
||||
static PyObject *py_blf_aspect(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
float aspect;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "if:blf.aspect", &fontid, &aspect)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_aspect(fontid, aspect, aspect, 1.0);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_color_doc,
|
||||
".. function:: color(fontid, r, g, b, a)\n"
|
||||
"\n"
|
||||
" Set the color for drawing text.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param r: Red channel 0.0 - 1.0.\n"
|
||||
" :type r: float\n"
|
||||
" :param g: Green channel 0.0 - 1.0.\n"
|
||||
" :type g: float\n"
|
||||
" :param b: Blue channel 0.0 - 1.0.\n"
|
||||
" :type b: float\n"
|
||||
" :param a: Alpha channel 0.0 - 1.0.\n"
|
||||
" :type a: float\n");
|
||||
static PyObject *py_blf_color(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int fontid;
|
||||
float rgba[4];
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iffff:blf.color", &fontid, &rgba[0], &rgba[1], &rgba[2], &rgba[3]))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_color4fv(fontid, rgba);
|
||||
|
||||
/* NOTE(@ideasman42): that storing these colors separately looks like something that could
|
||||
* be refactored away if the font's internal color format was changed from `uint8` to `float`. */
|
||||
BLF_buffer_col(fontid, rgba);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_draw_doc,
|
||||
".. function:: draw(fontid, text)\n"
|
||||
"\n"
|
||||
" Draw text in the current context.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param text: The text to draw.\n"
|
||||
" :type text: str\n");
|
||||
static PyObject *py_blf_draw(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *text;
|
||||
Py_ssize_t text_length;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "is#:blf.draw", &fontid, &text, &text_length)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_draw(fontid, text, uint(text_length));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_draw_buffer_doc,
|
||||
".. function:: draw_buffer(fontid, text)\n"
|
||||
"\n"
|
||||
" Draw text into the image buffer bound via :func:`blf.bind_imbuf`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param text: The text to draw into the bound image buffer.\n"
|
||||
" :type text: str\n");
|
||||
static PyObject *py_blf_draw_buffer(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *text;
|
||||
Py_ssize_t text_length;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "is#:blf.draw_buffer", &fontid, &text, &text_length)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_draw_buffer(fontid, text, uint(text_length));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_dimensions_doc,
|
||||
".. function:: dimensions(fontid, text)\n"
|
||||
"\n"
|
||||
" Return the width and height of the text.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param text: The text to measure.\n"
|
||||
" :type text: str\n"
|
||||
" :return: The width and height of the text.\n"
|
||||
" :rtype: tuple[float, float]\n");
|
||||
static PyObject *py_blf_dimensions(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
const char *text;
|
||||
float width, height;
|
||||
PyObject *ret;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "is:blf.dimensions", &fontid, &text)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_width_and_height(fontid, text, INT_MAX, &width, &height);
|
||||
|
||||
ret = PyTuple_New(2);
|
||||
PyTuple_SET_ITEMS(ret, PyFloat_FromDouble(width), PyFloat_FromDouble(height));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_clipping_doc,
|
||||
".. function:: clipping(fontid, xmin, ymin, xmax, ymax)\n"
|
||||
"\n"
|
||||
" Set the clipping, enable/disable using :data:`CLIPPING`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param xmin: Left edge of the clipping rectangle.\n"
|
||||
" :type xmin: float\n"
|
||||
" :param ymin: Bottom edge of the clipping rectangle.\n"
|
||||
" :type ymin: float\n"
|
||||
" :param xmax: Right edge of the clipping rectangle.\n"
|
||||
" :type xmax: float\n"
|
||||
" :param ymax: Top edge of the clipping rectangle.\n"
|
||||
" :type ymax: float\n");
|
||||
static PyObject *py_blf_clipping(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
float xmin, ymin, xmax, ymax;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iffff:blf.clipping", &fontid, &xmin, &ymin, &xmax, &ymax)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_clipping(fontid, xmin, ymin, xmax, ymax);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_word_wrap_doc,
|
||||
".. function:: word_wrap(fontid, wrap_width)\n"
|
||||
"\n"
|
||||
" Set the wrap width, enable/disable using :data:`WORD_WRAP`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param wrap_width: The width (in pixels) to wrap words at.\n"
|
||||
" :type wrap_width: int\n");
|
||||
static PyObject *py_blf_word_wrap(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int wrap_width;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii:blf.word_wrap", &fontid, &wrap_width)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_wordwrap(fontid, wrap_width);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_disable_doc,
|
||||
".. function:: disable(fontid, option)\n"
|
||||
"\n"
|
||||
" Disable a font drawing option.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param option: One of :data:`ROTATION`, :data:`CLIPPING`, "
|
||||
":data:`SHADOW`, :data:`MONOCHROME` or :data:`WORD_WRAP`.\n"
|
||||
" :type option: int\n");
|
||||
static PyObject *py_blf_disable(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int option, fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii:blf.disable", &fontid, &option)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_disable(fontid, FontFlags(option));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_enable_doc,
|
||||
".. function:: enable(fontid, option)\n"
|
||||
"\n"
|
||||
" Enable a font drawing option.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param option: One of :data:`ROTATION`, :data:`CLIPPING`, "
|
||||
":data:`SHADOW`, :data:`MONOCHROME` or :data:`WORD_WRAP`.\n"
|
||||
" :type option: int\n");
|
||||
static PyObject *py_blf_enable(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int option, fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ii:blf.enable", &fontid, &option)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_enable(fontid, FontFlags(option));
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_rotation_doc,
|
||||
".. function:: rotation(fontid, angle)\n"
|
||||
"\n"
|
||||
" Set the text rotation angle, enable/disable using :data:`ROTATION`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param angle: The angle for text drawing to use (in radians).\n"
|
||||
" :type angle: float\n");
|
||||
static PyObject *py_blf_rotation(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
float angle;
|
||||
int fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "if:blf.rotation", &fontid, &angle)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_rotation(fontid, angle);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_shadow_doc,
|
||||
".. function:: shadow(fontid, level, r, g, b, a)\n"
|
||||
"\n"
|
||||
" Shadow options, enable/disable using :data:`SHADOW`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param level: The shadow type: 0 for none, 3 for 3x3 blur, 5 for 5x5 blur "
|
||||
"or 6 for outline. Other values raise a :exc:`TypeError`.\n"
|
||||
" :type level: int\n"
|
||||
" :param r: Shadow color (red channel 0.0 - 1.0).\n"
|
||||
" :type r: float\n"
|
||||
" :param g: Shadow color (green channel 0.0 - 1.0).\n"
|
||||
" :type g: float\n"
|
||||
" :param b: Shadow color (blue channel 0.0 - 1.0).\n"
|
||||
" :type b: float\n"
|
||||
" :param a: Shadow color (alpha channel 0.0 - 1.0).\n"
|
||||
" :type a: float\n");
|
||||
static PyObject *py_blf_shadow(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int level, fontid;
|
||||
float rgba[4];
|
||||
|
||||
if (!PyArg_ParseTuple(
|
||||
args, "iiffff:blf.shadow", &fontid, &level, &rgba[0], &rgba[1], &rgba[2], &rgba[3]))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ELEM(level, 0, 3, 5, 6)) {
|
||||
PyErr_SetString(PyExc_TypeError, "blf.shadow expected arg to be in (0, 3, 5, 6)");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_shadow(fontid, FontShadowType(level), rgba);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_shadow_offset_doc,
|
||||
".. function:: shadow_offset(fontid, x, y)\n"
|
||||
"\n"
|
||||
" Set the offset for shadow text, enable/disable using :data:`SHADOW`.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param x: Horizontal shadow offset value in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Vertical shadow offset value in pixels.\n"
|
||||
" :type y: int\n");
|
||||
static PyObject *py_blf_shadow_offset(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
int x, y, fontid;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "iii:blf.shadow_offset", &fontid, &x, &y)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_shadow_offset(fontid, x, y);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_load_doc,
|
||||
".. function:: load(filepath)\n"
|
||||
"\n"
|
||||
" Load a new font.\n"
|
||||
"\n"
|
||||
" :param filepath: The filepath of the font.\n"
|
||||
" :type filepath: str | bytes\n"
|
||||
" :return: The new font's fontid or -1 if there was an error.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *py_blf_load(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
|
||||
if (!PyArg_ParseTuple(args,
|
||||
"O&" /* `filepath` */
|
||||
":blf.load",
|
||||
PyC_ParseUnicodeAsBytesAndSize,
|
||||
&filepath_data))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
const int font_id = BLF_load(filepath_data.value);
|
||||
Py_XDECREF(filepath_data.value_coerce);
|
||||
|
||||
return PyLong_FromLong(font_id);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_unload_doc,
|
||||
".. function:: unload(filepath)\n"
|
||||
"\n"
|
||||
" Unload an existing font.\n"
|
||||
"\n"
|
||||
" :param filepath: The filepath of the font.\n"
|
||||
" :type filepath: str | bytes\n");
|
||||
static PyObject *py_blf_unload(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
|
||||
if (!PyArg_ParseTuple(args,
|
||||
"O&" /* `filepath` */
|
||||
":blf.unload",
|
||||
PyC_ParseUnicodeAsBytesAndSize,
|
||||
&filepath_data))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLF_unload(filepath_data.value);
|
||||
Py_XDECREF(filepath_data.value_coerce);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Image Buffer Access
|
||||
*
|
||||
* Context manager for #ImBuf.
|
||||
* \{ */
|
||||
|
||||
static PyObject *py_blf_bind_imbuf_enter(BPyBLFImBufContext *self)
|
||||
{
|
||||
if (UNLIKELY(self->buffer_state)) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"BLFImBufContext.__enter__: unable to enter the same context more than once");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ImBuf *ibuf = BPy_ImBuf_FromPyObject(self->py_imbuf);
|
||||
if (ibuf == nullptr) {
|
||||
/* The error will have been set. */
|
||||
return nullptr;
|
||||
}
|
||||
BLFBufferState *buffer_state = BLF_buffer_state_push(self->fontid);
|
||||
if (buffer_state == nullptr) {
|
||||
PyErr_Format(PyExc_ValueError, "bind_imbuf: unknown fontid %d", self->fontid);
|
||||
return nullptr;
|
||||
}
|
||||
BLF_buffer(self->fontid,
|
||||
ibuf->float_data_for_write(),
|
||||
ibuf->byte_data_for_write(),
|
||||
ibuf->x,
|
||||
ibuf->y,
|
||||
4,
|
||||
ibuf->byte_buffer.colorspace);
|
||||
self->buffer_state = buffer_state;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *py_blf_bind_imbuf_exit(BPyBLFImBufContext *self, PyObject * /*args*/)
|
||||
{
|
||||
BLF_buffer_state_pop(self->buffer_state);
|
||||
self->buffer_state = nullptr;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static void py_blf_bind_imbuf_dealloc(BPyBLFImBufContext *self)
|
||||
{
|
||||
if (self->buffer_state) {
|
||||
/* This should practically never happen since it implies
|
||||
* `__enter__` is called without a matching `__exit__`.
|
||||
* Do this mainly for correctness:
|
||||
* if the process somehow exits before exiting the context manager. */
|
||||
BLF_buffer_state_free(self->buffer_state);
|
||||
}
|
||||
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->py_imbuf);
|
||||
PyObject_GC_Del(self);
|
||||
}
|
||||
|
||||
static int py_blf_bind_imbuf_traverse(BPyBLFImBufContext *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->py_imbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int py_blf_bind_imbuf_clear(BPyBLFImBufContext *self)
|
||||
{
|
||||
Py_CLEAR(self->py_imbuf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef py_blf_bind_imbuf_methods[] = {
|
||||
{"__enter__", reinterpret_cast<PyCFunction>(py_blf_bind_imbuf_enter), METH_NOARGS},
|
||||
{"__exit__", reinterpret_cast<PyCFunction>(py_blf_bind_imbuf_exit), METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BPyBLFImBufContext_Type_doc,
|
||||
"Context manager returned by :func:`blf.bind_imbuf` that binds an image buffer\n"
|
||||
"as the destination for text drawing.");
|
||||
|
||||
static PyTypeObject BPyBLFImBufContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "BLFImBufContext",
|
||||
/*tp_basicsize*/ sizeof(BPyBLFImBufContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(py_blf_bind_imbuf_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
/*tp_doc*/ BPyBLFImBufContext_Type_doc,
|
||||
/*tp_traverse*/ reinterpret_cast<traverseproc>(py_blf_bind_imbuf_traverse),
|
||||
/*tp_clear*/ reinterpret_cast<inquiry>(py_blf_bind_imbuf_clear),
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ py_blf_bind_imbuf_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
py_blf_bind_imbuf_doc,
|
||||
".. function:: bind_imbuf(fontid, imbuf, *, display_name=None)\n"
|
||||
"\n"
|
||||
" Context manager to draw text into an image buffer instead of the GPU's context.\n"
|
||||
"\n"
|
||||
" :param fontid: The id of the typeface as returned by :func:`blf.load`, for default "
|
||||
"font use 0.\n"
|
||||
" :type fontid: int\n"
|
||||
" :param imbuf: The image to draw into.\n"
|
||||
" :type imbuf: :class:`imbuf.types.ImBuf`\n"
|
||||
" :param display_name: Ignored (formerly a color-space transform name), "
|
||||
"kept for backwards compatibility.\n"
|
||||
" :type display_name: str | None\n"
|
||||
|
||||
" :return: The BLF ImBuf context manager.\n"
|
||||
" :rtype: :class:`blf.types.BLFImBufContext`\n");
|
||||
static PyObject *py_blf_bind_imbuf(PyObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
int fontid;
|
||||
PyObject *py_imbuf = nullptr;
|
||||
const char *display_name = nullptr;
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"",
|
||||
"",
|
||||
"display_name",
|
||||
nullptr,
|
||||
};
|
||||
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `fontid` */
|
||||
"O!" /* `image` */
|
||||
"|" /* Optional arguments. */
|
||||
"z" /* `display_name` */
|
||||
":bind_imbuf",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &fontid, &Py_ImBuf_Type, &py_imbuf, &display_name))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Display name is ignored, it is only kept for backwards compatibility. This should
|
||||
* always have been the image buffer byte colorspace rather than a display. */
|
||||
|
||||
BPyBLFImBufContext *ret = PyObject_GC_New(BPyBLFImBufContext, &BPyBLFImBufContext_Type);
|
||||
|
||||
ret->py_imbuf = Py_NewRef(py_imbuf);
|
||||
|
||||
ret->fontid = fontid;
|
||||
ret->buffer_state = nullptr;
|
||||
|
||||
PyObject_GC_Track(ret);
|
||||
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/*----------------------------MODULE INIT-------------------------*/
|
||||
static PyMethodDef BLF_methods[] = {
|
||||
{"aspect", static_cast<PyCFunction>(py_blf_aspect), METH_VARARGS, py_blf_aspect_doc},
|
||||
{"clipping", static_cast<PyCFunction>(py_blf_clipping), METH_VARARGS, py_blf_clipping_doc},
|
||||
{"word_wrap", static_cast<PyCFunction>(py_blf_word_wrap), METH_VARARGS, py_blf_word_wrap_doc},
|
||||
{"disable", static_cast<PyCFunction>(py_blf_disable), METH_VARARGS, py_blf_disable_doc},
|
||||
{"dimensions",
|
||||
static_cast<PyCFunction>(py_blf_dimensions),
|
||||
METH_VARARGS,
|
||||
py_blf_dimensions_doc},
|
||||
{"draw", static_cast<PyCFunction>(py_blf_draw), METH_VARARGS, py_blf_draw_doc},
|
||||
{"draw_buffer",
|
||||
static_cast<PyCFunction>(py_blf_draw_buffer),
|
||||
METH_VARARGS,
|
||||
py_blf_draw_buffer_doc},
|
||||
{"enable", static_cast<PyCFunction>(py_blf_enable), METH_VARARGS, py_blf_enable_doc},
|
||||
{"position", static_cast<PyCFunction>(py_blf_position), METH_VARARGS, py_blf_position_doc},
|
||||
{"rotation", static_cast<PyCFunction>(py_blf_rotation), METH_VARARGS, py_blf_rotation_doc},
|
||||
{"shadow", static_cast<PyCFunction>(py_blf_shadow), METH_VARARGS, py_blf_shadow_doc},
|
||||
{"shadow_offset",
|
||||
static_cast<PyCFunction>(py_blf_shadow_offset),
|
||||
METH_VARARGS,
|
||||
py_blf_shadow_offset_doc},
|
||||
{"size", static_cast<PyCFunction>(py_blf_size), METH_VARARGS, py_blf_size_doc},
|
||||
{"color", static_cast<PyCFunction>(py_blf_color), METH_VARARGS, py_blf_color_doc},
|
||||
{"load", static_cast<PyCFunction>(py_blf_load), METH_VARARGS, py_blf_load_doc},
|
||||
{"unload", static_cast<PyCFunction>(py_blf_unload), METH_VARARGS, py_blf_unload_doc},
|
||||
|
||||
{"bind_imbuf",
|
||||
reinterpret_cast<PyCFunction>(py_blf_bind_imbuf),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
py_blf_bind_imbuf_doc},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BLF_doc,
|
||||
"This module provides access to Blender's text drawing functions.");
|
||||
static PyModuleDef BLF_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "blf",
|
||||
/*m_doc*/ BLF_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ BLF_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module Definition (`blf.types`)
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
BLF_types_doc,
|
||||
"This module provides access to font drawing types.");
|
||||
static PyModuleDef BLF_types_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "blf.types",
|
||||
/*m_doc*/ BLF_types_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ nullptr,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
static PyObject *BPyInit_blf_types()
|
||||
{
|
||||
PyObject *submodule = PyModule_Create(&BLF_types_module_def);
|
||||
|
||||
if (PyType_Ready(&BPyBLFImBufContext_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyModule_AddType(submodule, &BPyBLFImBufContext_Type);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
PyObject *BPyInit_blf()
|
||||
{
|
||||
PyObject *mod;
|
||||
PyObject *submodule;
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
|
||||
mod = PyModule_Create(&BLF_module_def);
|
||||
|
||||
PyModule_AddIntConstant(mod, "ROTATION", BLF_ROTATION);
|
||||
PyModule_AddIntConstant(mod, "CLIPPING", BLF_CLIPPING);
|
||||
PyModule_AddIntConstant(mod, "SHADOW", BLF_SHADOW);
|
||||
PyModule_AddIntConstant(mod, "WORD_WRAP", BLF_WORD_WRAP);
|
||||
PyModule_AddIntConstant(mod, "MONOCHROME", BLF_MONOCHROME);
|
||||
PyModule_AddIntConstant(mod, "NO_FALLBACK", BLF_NO_FALLBACK);
|
||||
|
||||
/* `blf.types` */
|
||||
PyModule_AddObject(mod, "types", (submodule = BPyInit_blf_types()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/generic/blf_py_api.hh
Normal file
17
blender-5.2.0/source/blender/python/generic/blf_py_api.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_blf();
|
||||
|
||||
} // namespace blender
|
||||
67
blender-5.2.0/source/blender/python/generic/bpy_threads.cc
Normal file
67
blender-5.2.0/source/blender/python/generic/bpy_threads.cc
Normal file
@@ -0,0 +1,67 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*
|
||||
* This file contains wrapper functions related to global interpreter lock.
|
||||
* these functions are slightly different from the original Python API,
|
||||
* don't throw SIGABRT even if the thread state is nullptr. */
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "../BPY_extern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
BPy_ThreadStatePtr BPY_thread_save()
|
||||
{
|
||||
/* Use `_PyThreadState_UncheckedGet()` instead of `PyThreadState_Get()`, to avoid a fatal error
|
||||
* issued when a thread state is nullptr (the thread state can be nullptr when quitting Blender).
|
||||
*
|
||||
* `PyEval_SaveThread()` will release the GIL, so this thread has to have the GIL to begin with
|
||||
* or badness will ensue. */
|
||||
if (PyThreadState_GetUnchecked() && PyGILState_Check()) {
|
||||
return static_cast<BPy_ThreadStatePtr>(PyEval_SaveThread());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BPY_thread_restore(BPy_ThreadStatePtr tstate)
|
||||
{
|
||||
if (tstate) {
|
||||
PyEval_RestoreThread(static_cast<PyThreadState *>(tstate));
|
||||
}
|
||||
}
|
||||
|
||||
void BPY_thread_backtrace_print()
|
||||
{
|
||||
PyThreadState *tstate = PyGILState_GetThisThreadState();
|
||||
|
||||
if (tstate) {
|
||||
PyFrameObject *frame = PyThreadState_GetFrame(tstate);
|
||||
|
||||
printf(frame ? "Python stack trace:\n" : "No Python stack trace available.\n");
|
||||
|
||||
while (frame) {
|
||||
PyCodeObject *frame_co = PyFrame_GetCode(frame);
|
||||
int line = PyFrame_GetLineNumber(frame);
|
||||
const char *filename = PyUnicode_AsUTF8(frame_co->co_filename);
|
||||
const char *funcname = PyUnicode_AsUTF8(frame_co->co_name);
|
||||
printf(" %s:%d %s\n", filename, line, funcname);
|
||||
Py_DECREF(frame_co);
|
||||
PyFrameObject *frame_back = PyFrame_GetBack(frame);
|
||||
Py_DECREF(frame);
|
||||
frame = frame_back;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
else {
|
||||
printf("No Python thread state available.\n");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
2821
blender-5.2.0/source/blender/python/generic/idprop_py_api.cc
Normal file
2821
blender-5.2.0/source/blender/python/generic/idprop_py_api.cc
Normal file
File diff suppressed because it is too large
Load Diff
125
blender-5.2.0/source/blender/python/generic/idprop_py_api.hh
Normal file
125
blender-5.2.0/source/blender/python/generic/idprop_py_api.hh
Normal file
@@ -0,0 +1,125 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BPy_IDGroup_Iter;
|
||||
struct ID;
|
||||
struct IDProperty;
|
||||
|
||||
extern PyTypeObject BPy_IDArray_Type;
|
||||
extern PyTypeObject BPy_IDGroup_Type;
|
||||
|
||||
extern PyTypeObject BPy_IDGroup_ViewKeys_Type;
|
||||
extern PyTypeObject BPy_IDGroup_ViewValues_Type;
|
||||
extern PyTypeObject BPy_IDGroup_ViewItems_Type;
|
||||
|
||||
extern PyTypeObject BPy_IDGroup_IterKeys_Type;
|
||||
extern PyTypeObject BPy_IDGroup_IterValues_Type;
|
||||
extern PyTypeObject BPy_IDGroup_IterItems_Type;
|
||||
|
||||
#define BPy_IDArray_Check(v) (PyObject_TypeCheck(v, &BPy_IDArray_Type))
|
||||
#define BPy_IDArray_CheckExact(v) (Py_TYPE(v) == &BPy_IDArray_Type)
|
||||
#define BPy_IDGroup_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_Type))
|
||||
#define BPy_IDGroup_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_Type)
|
||||
|
||||
#define BPy_IDGroup_ViewKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewKeys_Type))
|
||||
#define BPy_IDGroup_ViewKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewKeys_Type)
|
||||
#define BPy_IDGroup_ViewValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewValues_Type))
|
||||
#define BPy_IDGroup_ViewValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewValues_Type)
|
||||
#define BPy_IDGroup_ViewItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_ViewItems_Type))
|
||||
#define BPy_IDGroup_ViewItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_ViewItems_Type)
|
||||
|
||||
#define BPy_IDGroup_IterKeys_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterKeys_Type))
|
||||
#define BPy_IDGroup_IterKeys_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterKeys_Type)
|
||||
#define BPy_IDGroup_IterValues_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterValues_Type))
|
||||
#define BPy_IDGroup_IterValues_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterValues_Type)
|
||||
#define BPy_IDGroup_IterItems_Check(v) (PyObject_TypeCheck(v, &BPy_IDGroup_IterItems_Type))
|
||||
#define BPy_IDGroup_IterItems_CheckExact(v) (Py_TYPE(v) == &BPy_IDGroup_IterItems_Type)
|
||||
|
||||
struct BPy_IDProperty {
|
||||
PyObject_HEAD
|
||||
struct ID *owner_id; /* can be NULL */
|
||||
struct IDProperty *prop; /* must be second member */
|
||||
struct IDProperty *parent;
|
||||
};
|
||||
|
||||
struct BPy_IDArray {
|
||||
PyObject_HEAD
|
||||
struct ID *owner_id; /* can be NULL */
|
||||
struct IDProperty *prop; /* must be second member */
|
||||
};
|
||||
|
||||
struct BPy_IDGroup_Iter {
|
||||
PyObject_HEAD
|
||||
BPy_IDProperty *group;
|
||||
struct IDProperty *cur;
|
||||
/** Use for detecting manipulation during iteration (which is not allowed). */
|
||||
int len_init;
|
||||
/** Iterate in the reverse direction. */
|
||||
bool reversed;
|
||||
};
|
||||
|
||||
/** Use to implement `IDPropertyGroup.keys/values/items` */
|
||||
struct BPy_IDGroup_View {
|
||||
PyObject_HEAD
|
||||
/** This will be NULL when accessing keys on data that has no ID properties. */
|
||||
BPy_IDProperty *group;
|
||||
bool reversed;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetKeys(IDProperty *prop);
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetValues(ID *id, IDProperty *prop);
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetItems(ID *id, IDProperty *prop);
|
||||
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetKeys_View_WithID(ID *id, IDProperty *prop);
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetValues_View_WithID(ID *id, IDProperty *prop);
|
||||
[[nodiscard]] PyObject *BPy_Wrap_GetItems_View_WithID(ID *id, IDProperty *prop);
|
||||
|
||||
[[nodiscard]] int BPy_Wrap_SetMapItem(IDProperty *prop, PyObject *key, PyObject *val);
|
||||
|
||||
/**
|
||||
* For simple, non nested types this is the same as #BPy_IDGroup_WrapData.
|
||||
*/
|
||||
[[nodiscard]] PyObject *BPy_IDGroup_MapDataToPy(IDProperty *prop);
|
||||
[[nodiscard]] PyObject *BPy_IDGroup_WrapData(ID *id, IDProperty *prop, IDProperty *parent);
|
||||
/**
|
||||
* \note group can be a pointer array or a group.
|
||||
* assume we already checked key is a string.
|
||||
*
|
||||
* \return success.
|
||||
*/
|
||||
[[nodiscard]] bool BPy_IDProperty_Map_ValidateAndCreate(PyObject *key,
|
||||
IDProperty *group,
|
||||
PyObject *ob);
|
||||
void IDProp_Init_Types();
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_idprop();
|
||||
|
||||
/**
|
||||
* Create an IDProperty from a Python object.
|
||||
*
|
||||
* \param prop_exist: pre-existing IDProperty to populate with the value. Can be `nullptr` to
|
||||
* allocate a new IDProperty.
|
||||
* \param name: the name of the IDProperty. Only used when creating a new IDProperty.
|
||||
* \param ob: the Python object to convert.
|
||||
* \param do_conversion: when there is a pre-existing IDProperty, whether the Python object's value
|
||||
* should be converted to its type (if not the same type already).
|
||||
* \param can_create: whether the function is allowed to create a new property. If this is `false`
|
||||
* and `prop_exists` is `nullptr`, this function is a no-op.
|
||||
*
|
||||
* \return the existing/created IDProperty if the value was set on it, and `nullptr` otherwise.
|
||||
*/
|
||||
IDProperty *BPy_IDProperty_FromPyObject(
|
||||
IDProperty *prop_exist, const char *name, PyObject *ob, bool do_conversion, bool can_create);
|
||||
|
||||
} // namespace blender
|
||||
1131
blender-5.2.0/source/blender/python/generic/idprop_py_ui_api.cc
Normal file
1131
blender-5.2.0/source/blender/python/generic/idprop_py_ui_api.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct IDProperty;
|
||||
|
||||
extern PyTypeObject BPy_IDPropertyUIManager_Type;
|
||||
|
||||
struct BPy_IDPropertyUIManager {
|
||||
PyObject_HEAD
|
||||
IDProperty *property;
|
||||
};
|
||||
|
||||
void IDPropertyUIData_Init_Types();
|
||||
|
||||
} // namespace blender
|
||||
1869
blender-5.2.0/source/blender/python/generic/imbuf_py_api.cc
Normal file
1869
blender-5.2.0/source/blender/python/generic/imbuf_py_api.cc
Normal file
File diff suppressed because it is too large
Load Diff
24
blender-5.2.0/source/blender/python/generic/imbuf_py_api.hh
Normal file
24
blender-5.2.0/source/blender/python/generic/imbuf_py_api.hh
Normal file
@@ -0,0 +1,24 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ImBuf;
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_imbuf();
|
||||
|
||||
extern PyTypeObject Py_ImBuf_Type;
|
||||
|
||||
/** Return the #ImBuf or null with an error set. */
|
||||
[[nodiscard]] ImBuf *BPy_ImBuf_FromPyObject(PyObject *py_imbuf);
|
||||
|
||||
} // namespace blender
|
||||
250
blender-5.2.0/source/blender/python/generic/py_capi_rna.cc
Normal file
250
blender-5.2.0/source/blender/python/generic/py_capi_rna.cc
Normal file
@@ -0,0 +1,250 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*
|
||||
* Python/RNA utilities.
|
||||
*
|
||||
* RNA functions that aren't part of the `bpy_rna.cc` API.
|
||||
*/
|
||||
|
||||
/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "py_capi_rna.hh"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_dynstr.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enum Utilities
|
||||
* \{ */
|
||||
|
||||
char *pyrna_enum_repr(const EnumPropertyItem *item)
|
||||
{
|
||||
DynStr *dynstr = BLI_dynstr_new();
|
||||
|
||||
/* We can't compare with the first element in the array
|
||||
* since it may be a category (without an identifier). */
|
||||
for (bool is_first = true; item->identifier; item++) {
|
||||
if (item->identifier[0]) {
|
||||
BLI_dynstr_appendf(dynstr, is_first ? "'%s'" : ", '%s'", item->identifier);
|
||||
is_first = false;
|
||||
}
|
||||
}
|
||||
|
||||
char *cstring = BLI_dynstr_get_cstring(dynstr);
|
||||
BLI_dynstr_free(dynstr);
|
||||
return cstring;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enum Conversion Utilities
|
||||
* \{ */
|
||||
|
||||
int pyrna_enum_value_from_id(const EnumPropertyItem *item,
|
||||
const char *identifier,
|
||||
int *r_value,
|
||||
const char *error_prefix)
|
||||
{
|
||||
if (RNA_enum_value_from_id(item, identifier, r_value) == 0) {
|
||||
const char *enum_str = pyrna_enum_repr(item);
|
||||
PyErr_Format(
|
||||
PyExc_ValueError, "%s: '%.200s' not found in (%s)", error_prefix, identifier, enum_str);
|
||||
MEM_delete(enum_str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
BLI_bitmap *pyrna_enum_bitmap_from_set(const EnumPropertyItem *items,
|
||||
PyObject *value,
|
||||
int type_size,
|
||||
bool type_convert_sign,
|
||||
int bitmap_size,
|
||||
const char *error_prefix)
|
||||
{
|
||||
BLI_assert(PySet_Check(value));
|
||||
BLI_bitmap *bitmap = BLI_BITMAP_NEW(bitmap_size, __func__);
|
||||
|
||||
if (PySet_GET_SIZE(value) > 0) {
|
||||
/* Set looping. */
|
||||
PyObject *it = PyObject_GetIter(value);
|
||||
PyObject *key;
|
||||
while ((key = PyIter_Next(it))) {
|
||||
/* Borrow from the set. */
|
||||
Py_DECREF(key);
|
||||
|
||||
const char *param = PyUnicode_AsUTF8(key);
|
||||
if (param == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s expected a string, not %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(key)->tp_name);
|
||||
break;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
int index = ret;
|
||||
|
||||
if (type_convert_sign) {
|
||||
if (type_size == 2) {
|
||||
union {
|
||||
signed short as_signed;
|
||||
ushort as_unsigned;
|
||||
} ret_convert;
|
||||
ret_convert.as_signed = short(ret);
|
||||
index = int(ret_convert.as_unsigned);
|
||||
}
|
||||
else if (type_size == 1) {
|
||||
union {
|
||||
signed char as_signed;
|
||||
uchar as_unsigned;
|
||||
} ret_convert;
|
||||
ret_convert.as_signed = static_cast<signed char>(ret);
|
||||
index = int(ret_convert.as_unsigned);
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
}
|
||||
BLI_assert(index < bitmap_size);
|
||||
BLI_BITMAP_ENABLE(bitmap, index);
|
||||
}
|
||||
Py_DECREF(it);
|
||||
|
||||
if (key) {
|
||||
MEM_delete(bitmap);
|
||||
bitmap = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
int pyrna_enum_bitfield_from_set(const EnumPropertyItem *items,
|
||||
PyObject *value,
|
||||
int *r_value,
|
||||
const char *error_prefix)
|
||||
{
|
||||
BLI_assert(PySet_Check(value));
|
||||
/* Set of enum items, concatenate all values with OR. */
|
||||
int flag = 0;
|
||||
|
||||
*r_value = 0;
|
||||
|
||||
PyObject *key = nullptr;
|
||||
if (PySet_GET_SIZE(value) > 0) {
|
||||
/* Set looping. */
|
||||
PyObject *it = PyObject_GetIter(value);
|
||||
while ((key = PyIter_Next(it))) {
|
||||
/* Borrow from the set. */
|
||||
Py_DECREF(key);
|
||||
|
||||
const char *param = PyUnicode_AsUTF8(key);
|
||||
if (param == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s expected a string, not %.200s",
|
||||
error_prefix,
|
||||
Py_TYPE(key)->tp_name);
|
||||
break;
|
||||
}
|
||||
|
||||
int ret;
|
||||
if (pyrna_enum_value_from_id(items, param, &ret, error_prefix) == -1) {
|
||||
break;
|
||||
}
|
||||
|
||||
flag |= ret;
|
||||
}
|
||||
Py_DECREF(it);
|
||||
if (key) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
*r_value = flag;
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyObject *pyrna_enum_bitfield_as_set(const EnumPropertyItem *items, int value)
|
||||
{
|
||||
PyObject *ret = PySet_New(nullptr);
|
||||
const char *identifier[RNA_ENUM_BITFLAG_SIZE + 1];
|
||||
|
||||
if (RNA_enum_bitflag_identifiers(items, value, identifier)) {
|
||||
PyObject *item;
|
||||
int index;
|
||||
for (index = 0; identifier[index]; index++) {
|
||||
item = PyUnicode_FromString(identifier[index]);
|
||||
PySet_Add(ret, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Argument Parsing Helpers
|
||||
* \{ */
|
||||
|
||||
int pyrna_enum_value_parse_string(PyObject *o, void *p)
|
||||
{
|
||||
const char *identifier = PyUnicode_AsUTF8(o);
|
||||
if (identifier == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError, "expected a string enum, not %.200s", Py_TYPE(o)->tp_name);
|
||||
return 0;
|
||||
}
|
||||
BPy_EnumProperty_Parse *parse_data = static_cast<BPy_EnumProperty_Parse *>(p);
|
||||
if (pyrna_enum_value_from_id(
|
||||
parse_data->items, identifier, &parse_data->value, "enum identifier") == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
parse_data->value_orig = o;
|
||||
parse_data->is_set = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pyrna_enum_bitfield_parse_set(PyObject *o, void *p)
|
||||
{
|
||||
if (!PySet_Check(o)) {
|
||||
PyErr_Format(PyExc_TypeError, "expected a set, not %.200s", Py_TYPE(o)->tp_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
BPy_EnumProperty_Parse *parse_data = static_cast<BPy_EnumProperty_Parse *>(p);
|
||||
if (pyrna_enum_bitfield_from_set(
|
||||
parse_data->items, o, &parse_data->value, "enum identifier set") == -1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
parse_data->value_orig = o;
|
||||
parse_data->is_set = true;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
82
blender-5.2.0/source/blender/python/generic/py_capi_rna.hh
Normal file
82
blender-5.2.0/source/blender/python/generic/py_capi_rna.hh
Normal file
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct EnumPropertyItem;
|
||||
|
||||
/**
|
||||
* Convert all items into a single comma separated string.
|
||||
* Use for creating useful error messages.
|
||||
*/
|
||||
[[nodiscard]] char *pyrna_enum_repr(const EnumPropertyItem *item);
|
||||
|
||||
/**
|
||||
* Same as #RNA_enum_value_from_id, but raises an exception.
|
||||
*/
|
||||
[[nodiscard]] int pyrna_enum_value_from_id(const EnumPropertyItem *item,
|
||||
const char *identifier,
|
||||
int *r_value,
|
||||
const char *error_prefix);
|
||||
|
||||
/**
|
||||
* Takes a set of strings and map it to and array of booleans.
|
||||
*
|
||||
* Useful when the values aren't flags.
|
||||
*
|
||||
* \param type_convert_sign: Maps signed to unsigned range,
|
||||
* needed when we want to use the full range of a signed short/char.
|
||||
*/
|
||||
[[nodiscard]] unsigned int *pyrna_enum_bitmap_from_set(const EnumPropertyItem *items,
|
||||
PyObject *value,
|
||||
int type_size,
|
||||
bool type_convert_sign,
|
||||
int bitmap_size,
|
||||
const char *error_prefix);
|
||||
|
||||
/**
|
||||
* 'value' _must_ be a set type, error check before calling.
|
||||
*/
|
||||
[[nodiscard]] int pyrna_enum_bitfield_from_set(const EnumPropertyItem *items,
|
||||
PyObject *value,
|
||||
int *r_value,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] PyObject *pyrna_enum_bitfield_as_set(const EnumPropertyItem *items, int value);
|
||||
|
||||
/**
|
||||
* Data for #pyrna_enum_value_parse_string & #pyrna_enum_bitfield_parse_set parsing utilities.
|
||||
* Use with #PyArg_ParseTuple's `O&` formatting.
|
||||
*/
|
||||
struct BPy_EnumProperty_Parse {
|
||||
const EnumPropertyItem *items;
|
||||
/**
|
||||
* Set when the value was successfully parsed.
|
||||
* Useful if the input ever needs to be included in an error message.
|
||||
* (if the value is not supported under certain conditions).
|
||||
*/
|
||||
PyObject *value_orig;
|
||||
|
||||
int value;
|
||||
bool is_set;
|
||||
};
|
||||
/**
|
||||
* Use with #PyArg_ParseTuple's `O&` formatting.
|
||||
*/
|
||||
[[nodiscard]] int pyrna_enum_value_parse_string(PyObject *o, void *p);
|
||||
/**
|
||||
* Use with #PyArg_ParseTuple's `O&` formatting.
|
||||
*/
|
||||
[[nodiscard]] int pyrna_enum_bitfield_parse_set(PyObject *o, void *p);
|
||||
|
||||
} // namespace blender
|
||||
2260
blender-5.2.0/source/blender/python/generic/py_capi_utils.cc
Normal file
2260
blender-5.2.0/source/blender/python/generic/py_capi_utils.cc
Normal file
File diff suppressed because it is too large
Load Diff
545
blender-5.2.0/source/blender/python/generic/py_capi_utils.hh
Normal file
545
blender-5.2.0/source/blender/python/generic/py_capi_utils.hh
Normal file
@@ -0,0 +1,545 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_span.hh"
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
#include "DNA_vec_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/** Useful to print Python objects while debugging. */
|
||||
void PyC_ObSpit(const char *name, PyObject *var);
|
||||
/**
|
||||
* A version of #PyC_ObSpit that writes into a string (and doesn't take a name argument).
|
||||
* Use for logging.
|
||||
*/
|
||||
void PyC_ObSpitStr(char *result, size_t result_maxncpy, PyObject *var);
|
||||
void PyC_LineSpit();
|
||||
void PyC_StackSpit();
|
||||
|
||||
/**
|
||||
* Return a string containing the full stack trace.
|
||||
*
|
||||
* - Only call when `PyErr_Occurred() != 0` .
|
||||
* - The exception is left in place without being manipulated,
|
||||
* although they will be normalized in order to display them (`PyErr_Print` also does this).
|
||||
* - `SystemExit` exceptions will exit (so `sys.exit(..)` works, matching `PyErr_Print` behavior).
|
||||
* - The always returns a Python string (unless exiting where the function doesn't return).
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_ExceptionBuffer() ATTR_RETURNS_NONNULL;
|
||||
/**
|
||||
* A version of #PyC_ExceptionBuffer that returns the last exception only.
|
||||
*
|
||||
* Useful for error messages from evaluating numeric expressions for example
|
||||
* where a full multi-line stack-trace isn't needed and doesn't format well in the status-bar.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_ExceptionBuffer_Simple() ATTR_RETURNS_NONNULL;
|
||||
|
||||
/**
|
||||
* Get exit code `sys.exit(..)` was called with.
|
||||
*/
|
||||
[[nodiscard]] std::optional<int> PyC_ExceptionSystemExitCode();
|
||||
|
||||
/**
|
||||
* Capture exit code from current python exception.
|
||||
*
|
||||
* If the current exception is `SystemExit`, capture the exit code and return true.
|
||||
* Otherwise return false;
|
||||
*/
|
||||
bool PyC_Err_CaptureSystemExitCode();
|
||||
|
||||
[[nodiscard]] PyObject *PyC_Object_GetAttrStringArgs(PyObject *o, Py_ssize_t n, ...);
|
||||
[[nodiscard]] PyObject *PyC_FrozenSetFromStrings(const char **strings);
|
||||
|
||||
/**
|
||||
* Similar to #PyErr_Format(),
|
||||
*
|
||||
* Implementation - we can't actually prepend the existing exception,
|
||||
* because it could have _any_ arguments given to it, so instead we get its
|
||||
* `__str__` output and raise our own exception including it.
|
||||
*/
|
||||
PyObject *PyC_Err_Format_Prefix(PyObject *exception_type_prefix, const char *format, ...);
|
||||
PyObject *PyC_Err_SetString_Prefix(PyObject *exception_type_prefix, const char *str);
|
||||
|
||||
/**
|
||||
* Use for Python callbacks run directly from C,
|
||||
* when we can't use normal methods of raising exceptions.
|
||||
*/
|
||||
void PyC_Err_PrintWithFunc(PyObject *py_func);
|
||||
|
||||
void PyC_FileAndNum(const char **r_filename, int *r_lineno);
|
||||
/**
|
||||
* The "safe" version checks Python is running first.
|
||||
* Typically the caller should know this but there are times it's impractical.
|
||||
*/
|
||||
void PyC_FileAndNum_Safe(const char **r_filename, int *r_lineno);
|
||||
[[nodiscard]] int PyC_AsArray_FAST(void *array,
|
||||
size_t array_item_size,
|
||||
PyObject *value_fast,
|
||||
Py_ssize_t length,
|
||||
const PyTypeObject *type,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] int PyC_AsArray(void *array,
|
||||
size_t array_item_size,
|
||||
PyObject *value,
|
||||
Py_ssize_t length,
|
||||
const PyTypeObject *type,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] int PyC_AsArray_Multi_FAST(void *array,
|
||||
size_t array_item_size,
|
||||
PyObject *value_fast,
|
||||
const int *dims,
|
||||
int dims_len,
|
||||
const PyTypeObject *type,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] int PyC_AsArray_Multi(void *array,
|
||||
size_t array_item_size,
|
||||
PyObject *value,
|
||||
const int *dims,
|
||||
int dims_len,
|
||||
const PyTypeObject *type,
|
||||
const char *error_prefix);
|
||||
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_F32(const float *array, uint len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_F64(const double *array, uint len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_I32(const int *array, uint len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_I32FromBool(const int *array, uint len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_Bool(const bool *array, uint len);
|
||||
|
||||
/**
|
||||
* \note Any errors converting strings will return null with the error left as-is.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_String(const char **array, uint len);
|
||||
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_Multi_F32(const float *array,
|
||||
const int dims[],
|
||||
int dims_len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_Multi_F64(const double *array,
|
||||
const int dims[],
|
||||
int dims_len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_Multi_I32(const int *array,
|
||||
const int dims[],
|
||||
int dims_len);
|
||||
[[nodiscard]] PyObject *PyC_Tuple_PackArray_Multi_Bool(const bool *array,
|
||||
const int dims[],
|
||||
int dims_len);
|
||||
|
||||
/**
|
||||
* Caller needs to ensure tuple is uninitialized.
|
||||
* Handy for filling a tuple with None for eg.
|
||||
*/
|
||||
void PyC_Tuple_Fill(PyObject *tuple, PyObject *value);
|
||||
void PyC_List_Fill(PyObject *list, PyObject *value);
|
||||
|
||||
/**
|
||||
* Create a `str` from bytes in a way which is compatible with non UTF8 encoded file-system paths,
|
||||
* see: #111033.
|
||||
* Follow http://www.python.org/dev/peps/pep-0383/
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_UnicodeFromBytes(const char *str);
|
||||
/**
|
||||
* \param size: The length of the string: `strlen(str)`.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_UnicodeFromBytesAndSize(const char *str, Py_ssize_t size);
|
||||
[[nodiscard]] const char *PyC_UnicodeAsBytes(PyObject *py_str,
|
||||
PyObject **r_coerce); /* coerce must be NULL */
|
||||
/**
|
||||
* String conversion, escape non-unicode chars
|
||||
* \param r_size: The string length (not including the null terminator).
|
||||
* \note By convention Blender API's use len/length however Python API's use the term size,
|
||||
* as this is an alternative to Python's #PyUnicode_AsUTF8AndSize, follow it's naming.
|
||||
* \param r_coerce: must reference a pointer set to NULL.
|
||||
*/
|
||||
[[nodiscard]] const char *PyC_UnicodeAsBytesAndSize(PyObject *py_str,
|
||||
Py_ssize_t *r_size,
|
||||
PyObject **r_coerce);
|
||||
|
||||
/**
|
||||
* Notes on using this structure:
|
||||
* - Always initialize to `{nullptr}`.
|
||||
* - Always `Py_XDECREF(value_coerce)` before returning,
|
||||
* after this `value` must not be accessed.
|
||||
*/
|
||||
struct PyC_UnicodeAsBytesAndSize_Data {
|
||||
PyObject *value_coerce;
|
||||
const char *value;
|
||||
Py_ssize_t value_len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* Expose #PyC_UnicodeAsBytes in a way which is useful to the argument parser.
|
||||
* \param o: An argument parsed to #PyC_UnicodeAsBytes.
|
||||
* \param p: Pointer to #PyC_UnicodeAsBytes_Data.
|
||||
*
|
||||
* \note The Python API docs reference `PyUnicode_FSConverter` however this does not support
|
||||
* paths which non UTF8 encoding, see: #111033.
|
||||
*/
|
||||
int PyC_ParseUnicodeAsBytesAndSize(PyObject *o, void *p);
|
||||
/** A version of #PyC_ParseUnicodeAsBytesAndSize that accepts None. */
|
||||
int PyC_ParseUnicodeAsBytesAndSize_OrNone(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Description: This function creates a new Python dictionary object.
|
||||
* NOTE: dict is owned by sys.modules["__main__"] module, reference is borrowed
|
||||
* NOTE: important we use the dict from __main__, this is what python expects
|
||||
* for 'pickle' to work as well as strings like this...
|
||||
* >> foo = 10
|
||||
* >> print(__import__("__main__").foo)
|
||||
*
|
||||
* NOTE: this overwrites __main__ which gives problems with nested calls.
|
||||
* be sure to run #PyC_MainModule_Backup & #PyC_MainModule_Restore if there is
|
||||
* any chance that python is in the call stack.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_DefaultNameSpace(const char *filename) ATTR_NONNULL(1);
|
||||
void PyC_RunQuicky(const char *filepath, int n, ...) ATTR_NONNULL(1);
|
||||
/**
|
||||
* Import `imports` into `py_dict`.
|
||||
*
|
||||
* \param py_dict: A Python dictionary, typically used as a name-space for script execution.
|
||||
* \param imports: A NULL terminated array of strings.
|
||||
* \return true when all modules import without errors, otherwise return false.
|
||||
* The caller is expected to handle the exception.
|
||||
*/
|
||||
[[nodiscard]] bool PyC_NameSpace_ImportArray(PyObject *py_dict, const char *imports[]);
|
||||
|
||||
/**
|
||||
* #PyC_MainModule_Restore MUST be called after #PyC_MainModule_Backup.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_MainModule_Backup();
|
||||
void PyC_MainModule_Restore(PyObject *main_mod);
|
||||
|
||||
/**
|
||||
* Add a module to `sys.modules` using the module's `__name__` as the key.
|
||||
*
|
||||
* Equivalent to: `sys.modules[module.__name__] = module`.
|
||||
*
|
||||
* \param sys_modules: The result of #PyImport_GetModuleDict().
|
||||
* \param module: The module to add.
|
||||
* \return 0 on success, -1 on error.
|
||||
*/
|
||||
int PyC_Module_AddToSysModules(PyObject *sys_modules, PyObject *module);
|
||||
|
||||
[[nodiscard]] bool PyC_IsInterpreterActive();
|
||||
|
||||
/**
|
||||
* Generic function to avoid depending on RNA.
|
||||
*/
|
||||
[[nodiscard]] void *PyC_RNA_AsPointer(PyObject *value, const char *type_name);
|
||||
|
||||
/* flag / set --- interchange */
|
||||
struct PyC_FlagSet {
|
||||
int value;
|
||||
const char *identifier;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *PyC_FlagSet_AsString(const PyC_FlagSet *item);
|
||||
[[nodiscard]] int PyC_FlagSet_ValueFromID_int(const PyC_FlagSet *item,
|
||||
const char *identifier,
|
||||
int *r_value);
|
||||
[[nodiscard]] int PyC_FlagSet_ValueFromID(const PyC_FlagSet *item,
|
||||
const char *identifier,
|
||||
int *r_value,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] int PyC_FlagSet_ToBitfield(const PyC_FlagSet *items,
|
||||
PyObject *value,
|
||||
int *r_value,
|
||||
const char *error_prefix);
|
||||
[[nodiscard]] PyObject *PyC_FlagSet_FromBitfield(PyC_FlagSet *items, int flag);
|
||||
|
||||
/**
|
||||
* \return success
|
||||
*
|
||||
* \note it is caller's responsibility to acquire & release GIL!
|
||||
*/
|
||||
[[nodiscard]] bool PyC_RunString_AsNumber(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
double *r_value) ATTR_NONNULL(2, 3, 4);
|
||||
[[nodiscard]] bool PyC_RunString_AsIntPtr(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
intptr_t *r_value) ATTR_NONNULL(2, 3, 4);
|
||||
/**
|
||||
* \param r_value_size: The length of the string assigned: `strlen(*r_value)`.
|
||||
*/
|
||||
[[nodiscard]] bool PyC_RunString_AsStringAndSize(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
char **r_value,
|
||||
size_t *r_value_size) ATTR_NONNULL(2, 3, 4, 5);
|
||||
[[nodiscard]] bool PyC_RunString_AsString(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
char **r_value) ATTR_NONNULL(2, 3, 4);
|
||||
|
||||
/**
|
||||
* \param r_value_size: The length of the string assigned: `strlen(*r_value)`.
|
||||
*/
|
||||
[[nodiscard]] bool PyC_RunString_AsStringAndSizeOrNone(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
char **r_value,
|
||||
size_t *r_value_size) ATTR_NONNULL(2, 3, 4);
|
||||
[[nodiscard]] bool PyC_RunString_AsStringOrNone(const char **imports,
|
||||
const char *expr,
|
||||
const char *filename,
|
||||
char **r_value) ATTR_NONNULL(2, 3, 4);
|
||||
|
||||
/**
|
||||
* Flush Python's `sys.stdout` and `sys.stderr`. Errors are ignored.
|
||||
*/
|
||||
void PyC_StdFilesFlush();
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* \see #PyC_Long_AsBool for a similar function to use outside of argument parsing.
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseBool(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of `O!` that also accepts None (setting the pointer to nullptr).
|
||||
*/
|
||||
struct PyC_TypeOrNone {
|
||||
PyTypeObject *type;
|
||||
PyObject **value_p;
|
||||
};
|
||||
[[nodiscard]] int PyC_ParseTypeOrNone(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of `i` that also accepts None (leaving the `std::optional<int>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalInt(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of `d` that also accepts None (leaving the `std::optional<double>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalDouble(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of `f` that also accepts None (leaving the `std::optional<float>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalFloat(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of `I` that also accepts None (leaving the `std::optional<uint>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalUInt(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* A version of #PyC_ParseBool that also accepts None
|
||||
* (leaving the `std::optional<bool>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalBool(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*
|
||||
* Parse `((x1, y1), (x2, y2))` into an `rcti`.
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseRectI(PyObject *o, void *p);
|
||||
/**
|
||||
* A version of #PyC_ParseRectI that accepts None
|
||||
* (leaving the `std::optional<rcti>` empty).
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseOptionalRectI(PyObject *o, void *p);
|
||||
|
||||
/**
|
||||
* Cast a pointer of a PyObject-derived type to `PyObject *`.
|
||||
*
|
||||
* A type-safe alternative to the C/Python API's `_PyObject_CAST` which is
|
||||
* a plain C-style cast without any validation. This verifies at compile time
|
||||
* that `T::ob_base` is `PyObject` or `PyVarObject` (from `PyObject_HEAD`
|
||||
* or `PyObject_VAR_HEAD`).
|
||||
*/
|
||||
template<typename T> inline PyObject *PyC_Object_CAST(T *value)
|
||||
{
|
||||
static_assert(std::is_same_v<decltype(T::ob_base), PyObject> ||
|
||||
std::is_same_v<decltype(T::ob_base), PyVarObject>,
|
||||
"Type must use PyObject_HEAD or PyObject_VAR_HEAD");
|
||||
return reinterpret_cast<PyObject *>(value);
|
||||
}
|
||||
|
||||
/** A version of #PyC_Object_CAST that casts `T**` to `PyObject **`. */
|
||||
template<typename T> inline PyObject **PyC_Object_ptr_CAST(T **value_p)
|
||||
{
|
||||
static_assert(std::is_same_v<decltype(T::ob_base), PyObject> ||
|
||||
std::is_same_v<decltype(T::ob_base), PyVarObject>,
|
||||
"Type must use PyObject_HEAD or PyObject_VAR_HEAD");
|
||||
return reinterpret_cast<PyObject **>(value_p);
|
||||
}
|
||||
|
||||
/** Initializer for #PyC_TypeOrNone, validates PyObject compatibility at compile time. */
|
||||
#define PyC_TYPE_OR_NONE_INIT(py_type, value_p) {(py_type), PyC_Object_ptr_CAST(value_p)}
|
||||
|
||||
struct PyC_StringEnumItems {
|
||||
int value;
|
||||
const char *id;
|
||||
};
|
||||
struct PyC_StringEnum {
|
||||
const struct PyC_StringEnumItems *items;
|
||||
int value_found;
|
||||
};
|
||||
|
||||
/**
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
*/
|
||||
[[nodiscard]] int PyC_ParseStringEnum(PyObject *o, void *p);
|
||||
[[nodiscard]] const char *PyC_StringEnum_FindIDFromValue(const struct PyC_StringEnumItems *items,
|
||||
int value);
|
||||
|
||||
/**
|
||||
* Silly function, we don't use arg. just check its compatible with `__deepcopy__`.
|
||||
*/
|
||||
[[nodiscard]] int PyC_CheckArgs_DeepCopy(PyObject *args);
|
||||
|
||||
/* Integer parsing (with overflow checks), -1 on error. */
|
||||
|
||||
/**
|
||||
* Comparison with #PyObject_IsTrue
|
||||
* ================================
|
||||
*
|
||||
* Even though Python provides a way to retrieve the boolean value for an object,
|
||||
* in many cases it's far too relaxed, with the following examples coercing values.
|
||||
*
|
||||
* \code{.py}
|
||||
* data.value = "Text" # True.
|
||||
* data.value = "" # False.
|
||||
* data.value = {1, 2} # True
|
||||
* data.value = {} # False.
|
||||
* data.value = None # False.
|
||||
* \endcode
|
||||
*
|
||||
* In practice this is often a mistake by the script author that doesn't behave as they expect.
|
||||
* So it's better to be more strict for attribute assignment and function arguments,
|
||||
* only accepting True/False 0/1.
|
||||
*
|
||||
* If coercing a value is desired, it can be done explicitly: `data.value = bool(value)`
|
||||
*
|
||||
* \see #PyC_ParseBool for use with #PyArg_ParseTuple and related functions.
|
||||
*
|
||||
* \note Don't use `bool` return type, so -1 can be used as an error value.
|
||||
*/
|
||||
[[nodiscard]] int PyC_Long_AsBool(PyObject *value);
|
||||
[[nodiscard]] int8_t PyC_Long_AsI8(PyObject *value);
|
||||
[[nodiscard]] int16_t PyC_Long_AsI16(PyObject *value);
|
||||
#if 0 /* inline */
|
||||
[[nodiscard]] int32_t PyC_Long_AsI32(PyObject *value);
|
||||
[[nodiscard]] int64_t PyC_Long_AsI64(PyObject *value);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Unlike Python's #PyLong_AsUnsignedLong and #PyLong_AsUnsignedLongLong, these unsigned integer
|
||||
* parsing functions fall back to calling #PyNumber_Index when their argument is not a
|
||||
* `PyLongObject`. This matches Python's signed integer parsing functions which also fall back to
|
||||
* calling #PyNumber_Index.
|
||||
*/
|
||||
[[nodiscard]] uint8_t PyC_Long_AsU8(PyObject *value);
|
||||
[[nodiscard]] uint16_t PyC_Long_AsU16(PyObject *value);
|
||||
[[nodiscard]] uint32_t PyC_Long_AsU32(PyObject *value);
|
||||
/**
|
||||
* #PyLong_AsUnsignedLongLong, unlike #PyLong_AsLongLong, does not fall back to calling
|
||||
* #PyNumber_Index when its argument is not a `PyLongObject` instance. To match parsing signed
|
||||
* integer types with #PyLong_AsLongLong, this function performs the #PyNumber_Index fallback, if
|
||||
* necessary, before calling #PyLong_AsUnsignedLongLong.
|
||||
*/
|
||||
[[nodiscard]] uint64_t PyC_Long_AsU64(PyObject *value);
|
||||
|
||||
/** Inline so type signatures match as expected. */
|
||||
[[nodiscard]] Py_LOCAL_INLINE(int32_t) PyC_Long_AsI32(PyObject *value)
|
||||
{
|
||||
return int32_t(PyLong_AsInt(value));
|
||||
}
|
||||
[[nodiscard]] Py_LOCAL_INLINE(int64_t) PyC_Long_AsI64(PyObject *value)
|
||||
{
|
||||
return int64_t(PyLong_AsLongLong(value));
|
||||
}
|
||||
|
||||
/* Utils for format string in `struct` module style syntax. */
|
||||
|
||||
[[nodiscard]] char PyC_StructFmt_type_from_str(const char *typestr);
|
||||
[[nodiscard]] bool PyC_StructFmt_type_is_float_any(char format);
|
||||
[[nodiscard]] bool PyC_StructFmt_type_is_int_any(char format);
|
||||
[[nodiscard]] bool PyC_StructFmt_type_is_byte(char format);
|
||||
[[nodiscard]] bool PyC_StructFmt_type_is_bool(char format);
|
||||
|
||||
/**
|
||||
* Create a `str` from `std::string`, wraps #PyC_UnicodeFromBytesAndSize.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_UnicodeFromStdStr(const std::string &str);
|
||||
|
||||
[[nodiscard]] inline PyObject *PyC_Tuple_Pack_F32(const Span<float> values)
|
||||
{
|
||||
return PyC_Tuple_PackArray_F32(values.data(), values.size());
|
||||
}
|
||||
[[nodiscard]] inline PyObject *PyC_Tuple_Pack_F64(const Span<double> values)
|
||||
{
|
||||
return PyC_Tuple_PackArray_F64(values.data(), values.size());
|
||||
}
|
||||
[[nodiscard]] inline PyObject *PyC_Tuple_Pack_I32(const Span<int> values)
|
||||
{
|
||||
return PyC_Tuple_PackArray_I32(values.data(), values.size());
|
||||
}
|
||||
[[nodiscard]] inline PyObject *PyC_Tuple_Pack_I32FromBool(const Span<int> values)
|
||||
{
|
||||
return PyC_Tuple_PackArray_I32FromBool(values.data(), values.size());
|
||||
}
|
||||
[[nodiscard]] inline PyObject *PyC_Tuple_Pack_Bool(const Span<bool> values)
|
||||
{
|
||||
return PyC_Tuple_PackArray_Bool(values.data(), values.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that all keys in `dict` are Python strings.
|
||||
*
|
||||
* Use this to validate keyword arguments from `tp_call` which,
|
||||
* unlike regular Python function calls, does not enforce string keys.
|
||||
*/
|
||||
[[nodiscard]] bool PyC_Dict_CheckKeysAreStrings(PyObject *dict);
|
||||
|
||||
/**
|
||||
* Create a `memoryview` from the contents of `info`,
|
||||
* similar to #PyMemoryView_FromBuffer.
|
||||
*
|
||||
* Unlike #PyMemoryView_FromBuffer the returned `memoryview` takes ownership of `info->buf`:
|
||||
* when the last reference to the `memoryview`
|
||||
* (or any `memoryview` derived from it via `cast()` / slicing) is released,
|
||||
* the buffer is freed with #MEM_delete_void.
|
||||
*
|
||||
* \return A new `memoryview` reference, or null with an exception set on failure.
|
||||
* `info->buf` is freed even when the function returns null.
|
||||
*/
|
||||
[[nodiscard]] PyObject *PyC_MemoryView_FromBufferOwned(const Py_buffer *info);
|
||||
|
||||
} // namespace blender
|
||||
70
blender-5.2.0/source/blender/python/generic/python_compat.cc
Normal file
70
blender-5.2.0/source/blender/python/generic/python_compat.cc
Normal file
@@ -0,0 +1,70 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
*
|
||||
* Functions relating to compatibility across Python versions.
|
||||
*/
|
||||
|
||||
#include <Python.h> /* IWYU pragma: keep. */
|
||||
|
||||
#include "BLI_utildefines.h" /* IWYU pragma: keep. */
|
||||
#include "python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t min, Py_ssize_t max)
|
||||
{
|
||||
BLI_assert(min >= 0);
|
||||
BLI_assert(min <= max);
|
||||
|
||||
if (nargs < min) {
|
||||
if (name != nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s expected %s%zd argument%s, got %zd",
|
||||
name,
|
||||
(min == max ? "" : "at least "),
|
||||
min,
|
||||
min == 1 ? "" : "s",
|
||||
nargs);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"unpacked tuple should have %s%zd element%s,"
|
||||
" but has %zd",
|
||||
(min == max ? "" : "at least "),
|
||||
min,
|
||||
min == 1 ? "" : "s",
|
||||
nargs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (nargs == 0) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (nargs > max) {
|
||||
if (name != nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%.200s expected %s%zd argument%s, got %zd",
|
||||
name,
|
||||
(min == max ? "" : "at most "),
|
||||
max,
|
||||
max == 1 ? "" : "s",
|
||||
nargs);
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"unpacked tuple should have %s%zd element%s,"
|
||||
" but has %zd",
|
||||
(min == max ? "" : "at most "),
|
||||
max,
|
||||
max == 1 ? "" : "s",
|
||||
nargs);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
28
blender-5.2.0/source/blender/python/generic/python_compat.hh
Normal file
28
blender-5.2.0/source/blender/python/generic/python_compat.hh
Normal file
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
* \brief header-only compatibility defines.
|
||||
*
|
||||
* \note this header should not be removed/cleaned where Python is used.
|
||||
* Because its required for Blender to build against different versions of Python.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
/* This code is not placed in the blender namespace, as it is meant to replace Python functions
|
||||
* in the global namespace. */
|
||||
|
||||
/* Python 3.14 made some changes, use the "new" names. */
|
||||
#if PY_VERSION_HEX < 0x030e0000
|
||||
# define Py_HashPointer _Py_HashPointer
|
||||
# define PyThreadState_GetUnchecked _PyThreadState_UncheckedGet
|
||||
/* TODO: Support: `PyDict_Pop`, it has different arguments. */
|
||||
#endif
|
||||
|
||||
/** Removed in Python 3.13. */
|
||||
int _PyArg_CheckPositional(const char *name, Py_ssize_t nargs, Py_ssize_t min, Py_ssize_t max);
|
||||
@@ -0,0 +1,38 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pygen
|
||||
* \brief header-only utilities
|
||||
* \note light addition to Python.h, use py_capi_utils.hh for larger features.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define PyTuple_SET_ITEMS(op_arg, ...) \
|
||||
{ \
|
||||
PyTupleObject *op = (PyTupleObject *)op_arg; \
|
||||
PyObject **ob_items = op->ob_item; \
|
||||
CHECK_TYPE_ANY(op_arg, PyObject *, PyTupleObject *); \
|
||||
BLI_assert(VA_NARGS_COUNT(__VA_ARGS__) == PyTuple_GET_SIZE(op)); \
|
||||
ARRAY_SET_ITEMS(ob_items, __VA_ARGS__); \
|
||||
} \
|
||||
(void)0
|
||||
|
||||
/**
|
||||
* Append & transfer ownership to the list,
|
||||
* avoids inline #Py_DECREF all over (which is quite a large macro).
|
||||
*/
|
||||
Py_LOCAL_INLINE(int) PyList_APPEND(PyObject *op, PyObject *v)
|
||||
{
|
||||
int ret = PyList_Append(op, v);
|
||||
Py_DecRef(v);
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
74
blender-5.2.0/source/blender/python/gpu/CMakeLists.txt
Normal file
74
blender-5.2.0/source/blender/python/gpu/CMakeLists.txt
Normal file
@@ -0,0 +1,74 @@
|
||||
# SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
.
|
||||
../../editors/include
|
||||
../../makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
gpu_py.cc
|
||||
gpu_py_api.cc
|
||||
gpu_py_batch.cc
|
||||
gpu_py_buffer.cc
|
||||
gpu_py_capabilities.cc
|
||||
gpu_py_compute.cc
|
||||
gpu_py_element.cc
|
||||
gpu_py_framebuffer.cc
|
||||
gpu_py_matrix.cc
|
||||
gpu_py_offscreen.cc
|
||||
gpu_py_platform.cc
|
||||
gpu_py_select.cc
|
||||
gpu_py_shader.cc
|
||||
gpu_py_shader_create_info.cc
|
||||
gpu_py_state.cc
|
||||
gpu_py_texture.cc
|
||||
gpu_py_types.cc
|
||||
gpu_py_uniformbuffer.cc
|
||||
gpu_py_vertex_buffer.cc
|
||||
gpu_py_vertex_format.cc
|
||||
|
||||
gpu_py.hh
|
||||
gpu_py_api.hh
|
||||
gpu_py_batch.hh
|
||||
gpu_py_buffer.hh
|
||||
gpu_py_capabilities.hh
|
||||
gpu_py_compute.hh
|
||||
gpu_py_element.hh
|
||||
gpu_py_framebuffer.hh
|
||||
gpu_py_matrix.hh
|
||||
gpu_py_offscreen.hh
|
||||
gpu_py_platform.hh
|
||||
gpu_py_select.hh
|
||||
gpu_py_shader.hh
|
||||
gpu_py_state.hh
|
||||
gpu_py_texture.hh
|
||||
gpu_py_types.hh
|
||||
gpu_py_uniformbuffer.hh
|
||||
gpu_py_vertex_buffer.hh
|
||||
gpu_py_vertex_format.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::windowmanager
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
)
|
||||
|
||||
if(WITH_OPENGL_BACKEND)
|
||||
list(APPEND LIB
|
||||
PRIVATE bf::dependencies::epoxy
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python_gpu "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
72
blender-5.2.0/source/blender/python/gpu/gpu_py.cc
Normal file
72
blender-5.2.0/source/blender/python/gpu/gpu_py.cc
Normal file
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "GPU_primitive.hh"
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Enums
|
||||
* \{ */
|
||||
|
||||
PyC_StringEnumItems bpygpu_primtype_items[] = {
|
||||
{GPU_PRIM_POINTS, "POINTS"},
|
||||
{GPU_PRIM_LINES, "LINES"},
|
||||
{GPU_PRIM_TRIS, "TRIS"},
|
||||
{GPU_PRIM_LINE_STRIP, "LINE_STRIP"},
|
||||
{GPU_PRIM_LINE_LOOP, "LINE_LOOP"},
|
||||
{GPU_PRIM_TRI_STRIP, "TRI_STRIP"},
|
||||
{GPU_PRIM_TRI_FAN, "TRI_FAN"},
|
||||
{GPU_PRIM_LINES_ADJ, "LINES_ADJ"},
|
||||
{GPU_PRIM_TRIS_ADJ, "TRIS_ADJ"},
|
||||
{GPU_PRIM_LINE_STRIP_ADJ, "LINE_STRIP_ADJ"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
PyC_StringEnumItems bpygpu_dataformat_items[] = {
|
||||
{GPU_DATA_FLOAT, "FLOAT"},
|
||||
{GPU_DATA_INT, "INT"},
|
||||
{GPU_DATA_UINT, "UINT"},
|
||||
{GPU_DATA_UBYTE, "UBYTE"},
|
||||
{GPU_DATA_UINT_24_8_DEPRECATED, "UINT_24_8"},
|
||||
{GPU_DATA_10_11_11_REV, "10_11_11_REV"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utilities
|
||||
* \{ */
|
||||
|
||||
bool bpygpu_is_init_or_error()
|
||||
{
|
||||
if (!GPU_is_init()) {
|
||||
PyErr_SetString(
|
||||
PyExc_SystemError,
|
||||
"GPU functions for drawing requires the gpu module to be initialized. See gpu.init.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
56
blender-5.2.0/source/blender/python/gpu/gpu_py.hh
Normal file
56
blender-5.2.0/source/blender/python/gpu/gpu_py.hh
Normal file
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern struct PyC_StringEnumItems bpygpu_primtype_items[];
|
||||
extern struct PyC_StringEnumItems bpygpu_dataformat_items[];
|
||||
|
||||
/* Docstring Literal types for shared enums. */
|
||||
|
||||
#define PYDOC_PRIMTYPE_LITERAL \
|
||||
"Literal[" \
|
||||
"'POINTS', " \
|
||||
"'LINES', " \
|
||||
"'TRIS', " \
|
||||
"'LINE_STRIP', " \
|
||||
"'LINE_LOOP', " \
|
||||
"'TRI_STRIP', " \
|
||||
"'TRI_FAN', " \
|
||||
"'LINES_ADJ', " \
|
||||
"'TRIS_ADJ', " \
|
||||
"'LINE_STRIP_ADJ']"
|
||||
#define PYDOC_DATAFORMAT_LITERAL \
|
||||
"Literal[" \
|
||||
"'FLOAT', " \
|
||||
"'INT', " \
|
||||
"'UINT', " \
|
||||
"'UBYTE', " \
|
||||
"'UINT_24_8', " \
|
||||
"'10_11_11_REV']"
|
||||
|
||||
[[nodiscard]] bool bpygpu_is_init_or_error();
|
||||
|
||||
#define BPYGPU_IS_INIT_OR_ERROR_OBJ \
|
||||
if (UNLIKELY(!bpygpu_is_init_or_error())) { \
|
||||
return NULL; \
|
||||
} \
|
||||
((void)0)
|
||||
#define BPYGPU_IS_INIT_OR_ERROR_INT \
|
||||
if (UNLIKELY(!bpygpu_is_init_or_error())) { \
|
||||
return -1; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
} // namespace blender
|
||||
151
blender-5.2.0/source/blender/python/gpu/gpu_py_api.cc
Normal file
151
blender-5.2.0/source/blender/python/gpu/gpu_py_api.cc
Normal file
@@ -0,0 +1,151 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* Experimental Python API, not considered public yet (called '_gpu'),
|
||||
* we may re-expose as public later.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py_capabilities.hh"
|
||||
#include "gpu_py_compute.hh"
|
||||
#include "gpu_py_matrix.hh"
|
||||
#include "gpu_py_platform.hh"
|
||||
#include "gpu_py_select.hh"
|
||||
#include "gpu_py_state.hh"
|
||||
#include "gpu_py_types.hh"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "WM_api.hh"
|
||||
#include "gpu_py_api.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Module
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_init_doc,
|
||||
".. function:: init()\n"
|
||||
"\n"
|
||||
" Initializes the GPU module for background use.\n"
|
||||
" If the initialization fails, a SystemError will be raised.\n");
|
||||
|
||||
static PyObject *pygpu_init(PyObject * /*self*/)
|
||||
{
|
||||
if (!G.background || GPU_is_init()) {
|
||||
/* GPU is already initialized. */
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
if (!GPU_backend_supported()) {
|
||||
PyErr_SetString(PyExc_SystemError, "Failed to initialize GPU. GPU backend not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Cannot use GPU_init() as it requires a GPU context to have been created.
|
||||
* See WM_init_gpu implementation. */
|
||||
WM_init_gpu();
|
||||
|
||||
if (!GPU_is_init()) {
|
||||
PyErr_SetString(PyExc_SystemError, "Failed to initialize GPU. Unexpected Error");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_tp_methods[] = {
|
||||
{"init", reinterpret_cast<PyCFunction>(pygpu_init), METH_NOARGS, pygpu_init_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_doc,
|
||||
"This module provides Python wrappers for the GPU implementation in Blender.\n"
|
||||
"Some higher level functions can be found in the :mod:`gpu_extras` module.\n");
|
||||
static PyModuleDef pygpu_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu",
|
||||
/*m_doc*/ pygpu_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPyInit_gpu()
|
||||
{
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
PyObject *submodule;
|
||||
PyObject *mod;
|
||||
|
||||
mod = PyModule_Create(&pygpu_module_def);
|
||||
|
||||
PyModule_AddObject(mod, "types", (submodule = bpygpu_types_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "capabilities", (submodule = bpygpu_capabilities_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "matrix", (submodule = bpygpu_matrix_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "platform", (submodule = bpygpu_platform_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "select", (submodule = bpygpu_select_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "shader", (submodule = bpygpu_shader_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "state", (submodule = bpygpu_state_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "texture", (submodule = bpygpu_texture_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
PyModule_AddObject(mod, "compute", (submodule = bpygpu_compute_init()));
|
||||
PyC_Module_AddToSysModules(sys_modules, submodule);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
21
blender-5.2.0/source/blender/python/gpu/gpu_py_api.hh
Normal file
21
blender-5.2.0/source/blender/python/gpu/gpu_py_api.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Each type object could have a method for free GPU resources.
|
||||
* However, it is currently of little use. */
|
||||
// #define BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
|
||||
[[nodiscard]] PyObject *BPyInit_gpu();
|
||||
|
||||
} // namespace blender
|
||||
708
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.cc
Normal file
708
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.cc
Normal file
@@ -0,0 +1,708 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the off-screen functionalities of the 'gpu' module
|
||||
* used for off-screen OpenGL rendering.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "GPU_batch.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_element.hh"
|
||||
#include "gpu_py_shader.hh"
|
||||
#include "gpu_py_vertex_buffer.hh"
|
||||
|
||||
#include "gpu_py_batch.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
static bool pygpu_batch_is_program_or_error(BPyGPUBatch *self)
|
||||
{
|
||||
if (!self->batch->shader) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "batch does not have any program assigned to it");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUBatch Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_batch__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *exc_str_missing_arg = "GPUBatch.__new__() missing required argument '%s' (pos %d)";
|
||||
|
||||
PyC_StringEnum prim_type = {bpygpu_primtype_items, GPU_PRIM_NONE};
|
||||
BPyGPUVertBuf *py_vertbuf = nullptr;
|
||||
BPyGPUIndexBuf *py_indexbuf = nullptr;
|
||||
PyC_TypeOrNone py_indexbuf_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPUIndexBuf_Type, &py_indexbuf);
|
||||
|
||||
static const char *_keywords[] = {"type", "buf", "elem", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `type` */
|
||||
"O!" /* `buf` */
|
||||
"O&" /* `elem` */
|
||||
":GPUBatch.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
PyC_ParseStringEnum,
|
||||
&prim_type,
|
||||
&BPyGPUVertBuf_Type,
|
||||
&py_vertbuf,
|
||||
PyC_ParseTypeOrNone,
|
||||
&py_indexbuf_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(prim_type.value_found != GPU_PRIM_NONE);
|
||||
if (prim_type.value_found == GPU_PRIM_LINE_LOOP) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"'LINE_LOOP' is deprecated. Please use 'LINE_STRIP' and close the segment.",
|
||||
1);
|
||||
}
|
||||
else if (prim_type.value_found == GPU_PRIM_TRI_FAN) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"'TRI_FAN' is deprecated. Please use 'TRI_STRIP' or 'TRIS' and try modifying your "
|
||||
"vertices or indices to match the topology.",
|
||||
1);
|
||||
}
|
||||
|
||||
if (py_vertbuf == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError, exc_str_missing_arg, _keywords[1], 2);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Batch *batch = GPU_batch_create(GPUPrimType(prim_type.value_found),
|
||||
py_vertbuf->buf,
|
||||
py_indexbuf ? py_indexbuf->elem : nullptr);
|
||||
|
||||
BPyGPUBatch *ret = reinterpret_cast<BPyGPUBatch *>(BPyGPUBatch_CreatePyObject(batch));
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
ret->references = PyList_New(py_indexbuf ? 2 : 1);
|
||||
PyList_SET_ITEM(ret->references, 0, (PyObject *)py_vertbuf);
|
||||
Py_INCREF(py_vertbuf);
|
||||
|
||||
if (py_indexbuf != nullptr) {
|
||||
PyList_SET_ITEM(ret->references, 1, (PyObject *)py_indexbuf);
|
||||
Py_INCREF(py_indexbuf);
|
||||
}
|
||||
|
||||
BLI_assert(!PyObject_GC_IsTracked((PyObject *)ret));
|
||||
PyObject_GC_Track(ret);
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_vertbuf_add_doc, ".. method:: vertbuf_add(buf)\n"
|
||||
"\n"
|
||||
" Add another vertex buffer to the Batch.\n"
|
||||
" It is not possible to add more vertices to the batch using this method.\n"
|
||||
" Instead it can be used to add more attributes to the existing vertices.\n"
|
||||
" A good use case would be when you have a separate\n"
|
||||
" vertex buffer for vertex positions and vertex normals.\n"
|
||||
" Current a batch can have at most " STRINGIFY(GPU_BATCH_VBO_MAX_LEN) " vertex buffers.\n"
|
||||
"\n"
|
||||
" :param buf: The vertex buffer that will be added to the batch.\n"
|
||||
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
|
||||
);
|
||||
static PyObject *pygpu_batch_vertbuf_add(BPyGPUBatch *self, BPyGPUVertBuf *py_buf)
|
||||
{
|
||||
if (!BPyGPUVertBuf_Check(py_buf)) {
|
||||
PyErr_Format(PyExc_TypeError, "Expected a GPUVertBuf, got %s", Py_TYPE(py_buf)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (GPU_vertbuf_get_vertex_len(self->batch->verts[0]) != GPU_vertbuf_get_vertex_len(py_buf->buf))
|
||||
{
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"Expected %d length, got %d",
|
||||
GPU_vertbuf_get_vertex_len(self->batch->verts[0]),
|
||||
GPU_vertbuf_get_vertex_len(py_buf->buf));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->batch->verts[GPU_BATCH_VBO_MAX_LEN - 1] != nullptr) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum number of vertex buffers exceeded: " STRINGIFY(GPU_BATCH_VBO_MAX_LEN));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Hold user */
|
||||
PyList_Append(self->references, reinterpret_cast<PyObject *>(py_buf));
|
||||
#endif
|
||||
|
||||
GPU_batch_vertbuf_add(self->batch, py_buf->buf, false);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_program_set_doc,
|
||||
".. method:: program_set(program)\n"
|
||||
"\n"
|
||||
" Assign a shader to this batch that will be used for drawing when not overwritten later.\n"
|
||||
" Note: This method has to be called in the draw context that the batch will be drawn in.\n"
|
||||
" This function does not need to be called when you always\n"
|
||||
" set the shader when calling :meth:`gpu.types.GPUBatch.draw`.\n"
|
||||
"\n"
|
||||
" :param program: The program/shader the batch will use in future draw calls.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n");
|
||||
static PyObject *pygpu_batch_program_set(BPyGPUBatch *self, BPyGPUShader *py_shader)
|
||||
{
|
||||
static bool deprecation_warning_issued = false;
|
||||
|
||||
/* Deprecation warning raised when calling `gpu.types.GPUBatch.program_set`. */
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calls to GPUBatch.program_set are deprecated."
|
||||
"Please set the shader via the 'program' parameter when calling "
|
||||
"GPUBatch.draw/draw_instanced/draw_range.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
if (!BPyGPUShader_Check(py_shader)) {
|
||||
PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
GPU_batch_set_shader(self->batch, shader);
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Remove existing user (if any), hold new user. */
|
||||
int i = PyList_GET_SIZE(self->references);
|
||||
while (--i != -1) {
|
||||
PyObject *py_shader_test = PyList_GET_ITEM(self->references, i);
|
||||
if (BPyGPUShader_Check(py_shader_test)) {
|
||||
PyList_SET_ITEM(self->references, i, (PyObject *)py_shader);
|
||||
Py_INCREF(py_shader);
|
||||
Py_DECREF(py_shader_test);
|
||||
/* Only ever reference one shader. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == -1) {
|
||||
/* No references set in the loop, so add it here. */
|
||||
PyList_Append(self->references, reinterpret_cast<PyObject *>(py_shader));
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify if the Shader is compatible with the batch and can be used for rendering.
|
||||
* Derived from `polyline_draw_workaround` in `gpu_immediate.cc`.
|
||||
*/
|
||||
static const char *pygpu_shader_check_compatibility(gpu::Batch *batch)
|
||||
{
|
||||
if (!batch->shader) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Currently only POLYLINE shaders are checked. */
|
||||
if (!bpygpu_shader_is_polyline(batch->shader)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check batch compatibility with shader. */
|
||||
for (auto *vert : Span(batch->verts, ARRAY_SIZE(batch->verts))) {
|
||||
if (!vert) {
|
||||
continue;
|
||||
}
|
||||
GPUVertFormat &format = vert->format;
|
||||
if ((format.stride % 4) != 0) {
|
||||
return "For POLYLINE shaders, only 4-byte aligned formats are supported";
|
||||
}
|
||||
|
||||
int pos_attr_id = -1;
|
||||
int col_attr_id = -1;
|
||||
for (uint a_idx = 0; a_idx < format.attr_len; a_idx++) {
|
||||
const GPUVertAttr *a = &format.attrs[a_idx];
|
||||
if ((a->offset % 4) != 0) {
|
||||
return "For POLYLINE shaders, only 4-byte aligned attributes are supported";
|
||||
}
|
||||
const StringRefNull name = GPU_vertformat_attr_name_get(&format, a, 0);
|
||||
if (pos_attr_id == -1 && name == "pos") {
|
||||
if (!ELEM(a->type.comp_type(), GPU_COMP_F32)) {
|
||||
return "For POLYLINE shaders, the 'pos' attribute needs to be 'F32'";
|
||||
}
|
||||
if (!ELEM(a->type.fetch_mode(), GPU_FETCH_FLOAT)) {
|
||||
return "For POLYLINE shaders, the 'pos' attribute must use the 'FLOAT' fetch type";
|
||||
}
|
||||
pos_attr_id = a_idx;
|
||||
}
|
||||
else if (col_attr_id == -1 && name == "color") {
|
||||
if (!ELEM(a->type.comp_type(), GPU_COMP_F32, GPU_COMP_U8)) {
|
||||
return "For POLYLINE shaders, the 'color' attribute needs to be 'F32' or 'U8'";
|
||||
}
|
||||
col_attr_id = a_idx;
|
||||
}
|
||||
if (pos_attr_id != -1 && col_attr_id != -1) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_doc,
|
||||
".. method:: draw(shader=None)\n"
|
||||
"\n"
|
||||
" Run the drawing shader with the parameters assigned to the batch.\n"
|
||||
"\n"
|
||||
" :param shader: Shader that performs the drawing operations.\n"
|
||||
" If ``None`` is passed, the last shader set to this batch will run.\n"
|
||||
" :type shader: :class:`gpu.types.GPUShader` | None\n");
|
||||
static PyObject *pygpu_batch_draw(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
static bool deprecation_warning_issued = false;
|
||||
|
||||
BPyGPUShader *py_shader = nullptr;
|
||||
PyC_TypeOrNone py_shader_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPUShader_Type, &py_shader);
|
||||
|
||||
static const char *_keywords[] = {"shader", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|" /* Optional arguments. */
|
||||
"O&" /* `shader` */
|
||||
":GPUBatch.draw",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, PyC_ParseTypeOrNone, &py_shader_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (py_shader == nullptr) {
|
||||
if (!deprecation_warning_issued) {
|
||||
/* Deprecation warning raised when calling gpu.types.GPUBatch.draw without a valid GPUShader.
|
||||
*/
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw without specifying a shader is deprecated. "
|
||||
"Please provide a valid GPUShader as the 'shader' parameter.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else if (self->batch->shader != py_shader->shader) {
|
||||
GPU_batch_set_shader(self->batch, py_shader->shader);
|
||||
}
|
||||
|
||||
/* Emit a warning when trying to draw wide lines as it is too late to automatically switch to a
|
||||
* polyline shader. */
|
||||
if (py_shader && py_shader->is_builtin &&
|
||||
ELEM(self->batch->prim_type, GPU_PRIM_LINES, GPU_PRIM_LINE_STRIP, GPU_PRIM_LINE_LOOP))
|
||||
{
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
const float line_width = GPU_line_width_get();
|
||||
const bool use_linesmooth = GPU_line_smooth_get();
|
||||
if (line_width > 1.0f || use_linesmooth) {
|
||||
if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_FLAT_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_SMOOTH_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_SMOOTH_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw wide or smooth lines with "
|
||||
"GPU_SHADER_3D_UNIFORM_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Emit a warning when trying to draw points with a regular shader as it is too late to
|
||||
* automatically switch to a point shader. */
|
||||
if (py_shader && py_shader->is_builtin && self->batch->prim_type == GPU_PRIM_POINTS) {
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_FLAT_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_FLAT_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_SMOOTH_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_SMOOTH_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_FLAT_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
else if (shader == GPU_shader_get_builtin_shader(GPU_SHADER_3D_UNIFORM_COLOR)) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Calling GPUBatch.draw to draw points with "
|
||||
"GPU_SHADER_3D_UNIFORM_COLOR is deprecated. "
|
||||
"Use GPU_SHADER_3D_POINT_SMOOTH_COLOR instead.",
|
||||
1);
|
||||
}
|
||||
}
|
||||
|
||||
if (const char *error = pygpu_shader_check_compatibility(self->batch)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_draw(self->batch);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_instanced_doc,
|
||||
".. method:: draw_instanced(program, *, instance_start=0, instance_count=0)\n"
|
||||
"\n"
|
||||
" Draw multiple instances of the drawing program with the parameters assigned\n"
|
||||
" to the batch. In the vertex shader, ``gl_InstanceID`` will contain the instance\n"
|
||||
" number being drawn.\n"
|
||||
"\n"
|
||||
" :param program: Program that performs the drawing operations.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n"
|
||||
" :param instance_start: Number of the first instance to draw.\n"
|
||||
" :type instance_start: int\n"
|
||||
" :param instance_count: Number of instances to draw. When not provided or set to 0\n"
|
||||
" the number of instances will be determined by the number of rows in the first\n"
|
||||
" vertex buffer.\n"
|
||||
" :type instance_count: int\n");
|
||||
static PyObject *pygpu_batch_draw_instanced(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
BPyGPUShader *py_program = nullptr;
|
||||
int instance_start = 0;
|
||||
int instance_count = 0;
|
||||
|
||||
static const char *_keywords[] = {"program", "instance_start", "instance_count", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `program` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"i" /* `instance_start` */
|
||||
"i" /* `instance_count` */
|
||||
":GPUBatch.draw_instanced",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &BPyGPUShader_Type, &py_program, &instance_start, &instance_count))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_set_shader(self->batch, py_program->shader);
|
||||
GPU_batch_draw_instance_range(self->batch, instance_start, instance_count);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch_draw_range_doc,
|
||||
".. method:: draw_range(program, *, elem_start=0, elem_count=0)\n"
|
||||
"\n"
|
||||
" Run the drawing program with the parameters assigned to the batch. "
|
||||
"Only draw the ``elem_count`` elements of the index buffer starting at ``elem_start``.\n"
|
||||
"\n"
|
||||
" :param program: Program that performs the drawing operations.\n"
|
||||
" :type program: :class:`gpu.types.GPUShader`\n"
|
||||
" :param elem_start: First index to draw. When not provided or set to 0 drawing\n"
|
||||
" will start from the first element of the index buffer.\n"
|
||||
" :type elem_start: int\n"
|
||||
" :param elem_count: Number of elements of the index buffer to draw. When not\n"
|
||||
" provided or set to 0 all elements from ``elem_start`` to the end of the\n"
|
||||
" index buffer will be drawn.\n"
|
||||
" :type elem_count: int\n");
|
||||
static PyObject *pygpu_batch_draw_range(BPyGPUBatch *self, PyObject *args, PyObject *kw)
|
||||
{
|
||||
BPyGPUShader *py_program = nullptr;
|
||||
int elem_start = 0;
|
||||
int elem_count = 0;
|
||||
|
||||
static const char *_keywords[] = {"program", "elem_start", "elem_count", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `program` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"i" /* `elem_start` */
|
||||
"i" /* `elem_count` */
|
||||
":GPUBatch.draw_range",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &BPyGPUShader_Type, &py_program, &elem_start, &elem_count))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_batch_set_shader(self->batch, py_program->shader);
|
||||
GPU_batch_draw_range(self->batch, elem_start, elem_count);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_batch_program_use_begin(BPyGPUBatch *self)
|
||||
{
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_shader_bind(self->batch->shader);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_batch_program_use_end(BPyGPUBatch *self)
|
||||
{
|
||||
if (!pygpu_batch_is_program_or_error(self)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_shader_unbind();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_batch__tp_methods[] = {
|
||||
{"vertbuf_add",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_vertbuf_add),
|
||||
METH_O,
|
||||
pygpu_batch_vertbuf_add_doc},
|
||||
{"program_set",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_set),
|
||||
METH_O,
|
||||
pygpu_batch_program_set_doc},
|
||||
{"draw",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_doc},
|
||||
{"draw_instanced",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw_instanced),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_instanced_doc},
|
||||
{"draw_range",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_draw_range),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_batch_draw_range_doc},
|
||||
{"_program_use_begin",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_use_begin),
|
||||
METH_NOARGS,
|
||||
""},
|
||||
{"_program_use_end",
|
||||
reinterpret_cast<PyCFunction>(pygpu_batch_program_use_end),
|
||||
METH_NOARGS,
|
||||
""},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
|
||||
static int pygpu_batch__tp_traverse(BPyGPUBatch *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->references);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_batch__tp_clear(BPyGPUBatch *self)
|
||||
{
|
||||
Py_CLEAR(self->references);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_batch__tp_is_gc(BPyGPUBatch *self)
|
||||
{
|
||||
return self->references != nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static void pygpu_batch__tp_dealloc(BPyGPUBatch *self)
|
||||
{
|
||||
GPU_batch_discard(self->batch);
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
PyObject_GC_UnTrack(self);
|
||||
if (self->references) {
|
||||
pygpu_batch__tp_clear(self);
|
||||
Py_XDECREF(self->references);
|
||||
}
|
||||
#endif
|
||||
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_batch__tp_doc,
|
||||
".. class:: GPUBatch\n"
|
||||
"\n"
|
||||
" Reusable container for drawable geometry.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(type, buf, elem=None)\n"
|
||||
"\n"
|
||||
" :param type: The primitive type of geometry to be drawn.\n"
|
||||
" :type type: " PYDOC_PRIMTYPE_LITERAL
|
||||
"\n"
|
||||
" :param buf: Vertex buffer containing all or some of the attributes required for "
|
||||
"drawing.\n"
|
||||
" :type buf: :class:`gpu.types.GPUVertBuf`\n"
|
||||
" :param elem: An optional index buffer.\n"
|
||||
" :type elem: :class:`gpu.types.GPUIndexBuf` | None\n");
|
||||
PyTypeObject BPyGPUBatch_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUBatch",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUBatch),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_batch__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
#else
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
#endif
|
||||
/*tp_doc*/ pygpu_batch__tp_doc,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_traverse*/ reinterpret_cast<traverseproc>(pygpu_batch__tp_traverse),
|
||||
#else
|
||||
/*tp_traverse*/ nullptr,
|
||||
#endif
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_clear*/ reinterpret_cast<inquiry>(pygpu_batch__tp_clear),
|
||||
#else
|
||||
/*tp_clear*/ nullptr,
|
||||
#endif
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_batch__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_batch__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/*tp_is_gc*/ reinterpret_cast<inquiry>(pygpu_batch__tp_is_gc),
|
||||
#else
|
||||
/*tp_is_gc*/ nullptr,
|
||||
#endif
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUBatch_CreatePyObject(gpu::Batch *batch)
|
||||
{
|
||||
BPyGPUBatch *self;
|
||||
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
self = reinterpret_cast<BPyGPUBatch *>(_PyObject_GC_New(&BPyGPUBatch_Type));
|
||||
self->references = nullptr;
|
||||
#else
|
||||
self = PyObject_New(BPyGPUBatch, &BPyGPUBatch_Type);
|
||||
#endif
|
||||
|
||||
self->batch = batch;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPY_GPU_BATCH_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
39
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.hh
Normal file
39
blender-5.2.0/source/blender/python/gpu/gpu_py_batch.hh
Normal file
@@ -0,0 +1,39 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Batch;
|
||||
}
|
||||
|
||||
#define USE_GPU_PY_REFERENCES
|
||||
|
||||
extern PyTypeObject BPyGPUBatch_Type;
|
||||
|
||||
#define BPyGPUBatch_Check(v) (Py_TYPE(v) == &BPyGPUBatch_Type)
|
||||
|
||||
struct BPyGPUBatch {
|
||||
PyObject_HEAD
|
||||
/* The batch is owned, we may support thin wrapped batches later. */
|
||||
gpu::Batch *batch;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *references;
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUBatch_CreatePyObject(gpu::Batch *batch) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
796
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.cc
Normal file
796
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.cc
Normal file
@@ -0,0 +1,796 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.types.Buffer API.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "GPU_texture.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
|
||||
#include "gpu_py_buffer.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define PYGPU_BUFFER_PROTOCOL
|
||||
#define MAX_DIMENSIONS 64
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
static Py_ssize_t pygpu_buffer_dimensions_tot_elem(const Py_ssize_t *shape, Py_ssize_t shape_len)
|
||||
{
|
||||
Py_ssize_t tot = shape[0];
|
||||
for (int i = 1; i < shape_len; i++) {
|
||||
tot *= shape[i];
|
||||
}
|
||||
|
||||
return tot;
|
||||
}
|
||||
|
||||
static bool pygpu_buffer_dimensions_tot_len_compare(const Py_ssize_t *shape_a,
|
||||
const Py_ssize_t shape_a_len,
|
||||
const Py_ssize_t *shape_b,
|
||||
const Py_ssize_t shape_b_len)
|
||||
{
|
||||
if (pygpu_buffer_dimensions_tot_elem(shape_a, shape_a_len) !=
|
||||
pygpu_buffer_dimensions_tot_elem(shape_b, shape_b_len))
|
||||
{
|
||||
PyErr_SetString(PyExc_BufferError, "array size does not match");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_buffer_pyobj_as_shape(PyObject *shape_obj,
|
||||
Py_ssize_t r_shape[MAX_DIMENSIONS],
|
||||
Py_ssize_t *r_shape_len)
|
||||
{
|
||||
Py_ssize_t shape_len = 0;
|
||||
if (PyLong_Check(shape_obj)) {
|
||||
shape_len = 1;
|
||||
if ((r_shape[0] = PyLong_AsSsize_t(shape_obj)) < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (PySequence_Check(shape_obj)) {
|
||||
shape_len = PySequence_Size(shape_obj);
|
||||
if (shape_len > MAX_DIMENSIONS) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"too many dimensions, max is " STRINGIFY(MAX_DIMENSIONS));
|
||||
return false;
|
||||
}
|
||||
if (shape_len < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "sequence must have at least one dimension");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < shape_len; i++) {
|
||||
PyObject *ob = PySequence_GetItem(shape_obj, i);
|
||||
if (!PyLong_Check(ob)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid dimension %i, expected an int, not a %.200s",
|
||||
i,
|
||||
Py_TYPE(ob)->tp_name);
|
||||
Py_DECREF(ob);
|
||||
return false;
|
||||
}
|
||||
|
||||
r_shape[i] = PyLong_AsSsize_t(ob);
|
||||
Py_DECREF(ob);
|
||||
|
||||
if (r_shape[i] < 1) {
|
||||
PyErr_SetString(PyExc_AttributeError, "dimension must be greater than or equal to 1");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"invalid second argument expected a sequence "
|
||||
"or an int, not a %.200s",
|
||||
Py_TYPE(shape_obj)->tp_name);
|
||||
}
|
||||
|
||||
*r_shape_len = shape_len;
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char *pygpu_buffer_formatstr(eGPUDataFormat data_format)
|
||||
{
|
||||
switch (data_format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return "f";
|
||||
case GPU_DATA_INT:
|
||||
return "i";
|
||||
case GPU_DATA_UINT:
|
||||
return "I";
|
||||
case GPU_DATA_UBYTE:
|
||||
return "B";
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return "I";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name BPyGPUBuffer API
|
||||
* \{ */
|
||||
|
||||
static BPyGPUBuffer *pygpu_buffer_make_from_data(PyObject *parent,
|
||||
const eGPUDataFormat format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape,
|
||||
void *buf)
|
||||
{
|
||||
BPyGPUBuffer *buffer = reinterpret_cast<BPyGPUBuffer *>(_PyObject_GC_New(&BPyGPU_BufferType));
|
||||
|
||||
buffer->parent = nullptr;
|
||||
buffer->format = format;
|
||||
buffer->shape_len = shape_len;
|
||||
buffer->shape = MEM_new_array_uninitialized<Py_ssize_t>(size_t(shape_len), "BPyGPUBuffer shape");
|
||||
memcpy(buffer->shape, shape, sizeof(*buffer->shape) * size_t(shape_len));
|
||||
buffer->buf.as_void = buf;
|
||||
|
||||
if (parent) {
|
||||
Py_INCREF(parent);
|
||||
buffer->parent = parent;
|
||||
BLI_assert(!PyObject_GC_IsTracked((PyObject *)buffer));
|
||||
PyObject_GC_Track(buffer);
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__sq_item(BPyGPUBuffer *self, Py_ssize_t i)
|
||||
{
|
||||
if (i >= self->shape[0] || i < 0) {
|
||||
PyErr_SetString(PyExc_IndexError, "array index out of range");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const char *formatstr = pygpu_buffer_formatstr(eGPUDataFormat(self->format));
|
||||
|
||||
if (self->shape_len == 1) {
|
||||
switch (self->format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return Py_BuildValue(formatstr, self->buf.as_float[i]);
|
||||
case GPU_DATA_INT:
|
||||
return Py_BuildValue(formatstr, self->buf.as_int[i]);
|
||||
case GPU_DATA_UBYTE:
|
||||
return Py_BuildValue(formatstr, self->buf.as_byte[i]);
|
||||
case GPU_DATA_UINT:
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return Py_BuildValue(formatstr, self->buf.as_uint[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
int offset = i * GPU_texture_dataformat_size(eGPUDataFormat(self->format));
|
||||
for (int j = 1; j < self->shape_len; j++) {
|
||||
offset *= self->shape[j];
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(
|
||||
pygpu_buffer_make_from_data(reinterpret_cast<PyObject *>(self),
|
||||
eGPUDataFormat(self->format),
|
||||
self->shape_len - 1,
|
||||
self->shape + 1,
|
||||
self->buf.as_byte + offset));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer_to_list(BPyGPUBuffer *self)
|
||||
{
|
||||
const Py_ssize_t len = self->shape[0];
|
||||
PyObject *list = PyList_New(len);
|
||||
|
||||
for (Py_ssize_t i = 0; i < len; i++) {
|
||||
PyList_SET_ITEM(list, i, pygpu_buffer__sq_item(self, i));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer_to_list_doc,
|
||||
".. method:: to_list()\n"
|
||||
"\n"
|
||||
" Return the buffer as a list.\n"
|
||||
"\n"
|
||||
" :return: The buffer as a list.\n"
|
||||
" :rtype: list\n");
|
||||
static PyObject *pygpu_buffer_to_list_recursive(BPyGPUBuffer *self)
|
||||
{
|
||||
PyObject *list;
|
||||
|
||||
if (self->shape_len > 1) {
|
||||
int i, len = self->shape[0];
|
||||
list = PyList_New(len);
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
/* "BPyGPUBuffer *sub_tmp" is a temporary object created just to be read for nested lists.
|
||||
* That is why it is decremented/freed soon after.
|
||||
* TODO: For efficiency, avoid creating #BPyGPUBuffer when creating nested lists. */
|
||||
BPyGPUBuffer *sub_tmp = reinterpret_cast<BPyGPUBuffer *>(pygpu_buffer__sq_item(self, i));
|
||||
PyList_SET_ITEM(list, i, pygpu_buffer_to_list_recursive(sub_tmp));
|
||||
Py_DECREF(sub_tmp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
list = pygpu_buffer_to_list(self);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer_dimensions_doc,
|
||||
"The size of the buffer for each dimension.\n"
|
||||
"\n"
|
||||
"Setting the dimensions is supported when the total number of elements is unchanged.\n"
|
||||
"\n"
|
||||
":type: list[int]\n");
|
||||
static PyObject *pygpu_buffer_dimensions_get(BPyGPUBuffer *self, void * /*arg*/)
|
||||
{
|
||||
PyObject *list = PyList_New(self->shape_len);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < self->shape_len; i++) {
|
||||
PyList_SET_ITEM(list, i, PyLong_FromLong(self->shape[i]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
static int pygpu_buffer_dimensions_set(BPyGPUBuffer *self, PyObject *value, void * /*type*/)
|
||||
{
|
||||
Py_ssize_t shape[MAX_DIMENSIONS];
|
||||
Py_ssize_t shape_len = 0;
|
||||
|
||||
if (!pygpu_buffer_pyobj_as_shape(value, shape, &shape_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, self->shape, self->shape_len)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (shape_len != self->shape_len) {
|
||||
MEM_delete(self->shape);
|
||||
self->shape = MEM_new_array_uninitialized<Py_ssize_t>(size_t(shape_len), __func__);
|
||||
}
|
||||
|
||||
self->shape_len = shape_len;
|
||||
memcpy(self->shape, shape, sizeof(*self->shape) * size_t(shape_len));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_traverse(BPyGPUBuffer *self, visitproc visit, void *arg)
|
||||
{
|
||||
Py_VISIT(self->parent);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_clear(BPyGPUBuffer *self)
|
||||
{
|
||||
if (self->parent) {
|
||||
Py_CLEAR(self->parent);
|
||||
self->buf.as_void = nullptr;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pygpu_buffer__tp_dealloc(BPyGPUBuffer *self)
|
||||
{
|
||||
if (self->parent) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->parent);
|
||||
}
|
||||
else if (self->buf.as_void) {
|
||||
MEM_delete_void(self->buf.as_void);
|
||||
}
|
||||
|
||||
MEM_delete(self->shape);
|
||||
|
||||
PyObject_GC_Del(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__tp_repr(BPyGPUBuffer *self)
|
||||
{
|
||||
PyObject *repr;
|
||||
|
||||
PyObject *list = pygpu_buffer_to_list_recursive(self);
|
||||
const char *typestr = PyC_StringEnum_FindIDFromValue(bpygpu_dataformat_items, self->format);
|
||||
|
||||
repr = PyUnicode_FromFormat("Buffer(%s, %R)", typestr, list);
|
||||
Py_DECREF(list);
|
||||
|
||||
return repr;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, Py_ssize_t i, PyObject *v);
|
||||
|
||||
static int pygpu_buffer_ass_slice(BPyGPUBuffer *self,
|
||||
Py_ssize_t begin,
|
||||
Py_ssize_t end,
|
||||
PyObject *seq)
|
||||
{
|
||||
PyObject *item;
|
||||
int count, err = 0;
|
||||
|
||||
begin = std::max<Py_ssize_t>(begin, 0);
|
||||
end = std::min(end, self->shape[0]);
|
||||
begin = std::min(begin, end);
|
||||
|
||||
if (!PySequence_Check(seq)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"buffer[:] = value, invalid assignment. "
|
||||
"Expected a sequence, not an %.200s type",
|
||||
Py_TYPE(seq)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* re-use count var */
|
||||
if ((count = PySequence_Size(seq)) != (end - begin)) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"buffer[:] = value, size mismatch in assignment. "
|
||||
"Expected: %d (given: %d)",
|
||||
count,
|
||||
end - begin);
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (count = begin; count < end; count++) {
|
||||
item = PySequence_GetItem(seq, count - begin);
|
||||
if (item) {
|
||||
err = pygpu_buffer__sq_ass_item(self, count, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
else {
|
||||
err = -1;
|
||||
}
|
||||
if (err) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyObject *length_ob, *init = nullptr;
|
||||
BPyGPUBuffer *buffer = nullptr;
|
||||
Py_ssize_t shape[MAX_DIMENSIONS];
|
||||
|
||||
Py_ssize_t shape_len = 0;
|
||||
|
||||
if (kwds && PyDict_Size(kwds)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Buffer(): takes no keyword args");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items, GPU_DATA_FLOAT};
|
||||
if (!PyArg_ParseTuple(
|
||||
args, "O&O|O: Buffer", PyC_ParseStringEnum, &pygpu_dataformat, &length_ob, &init))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (pygpu_dataformat.value_found == GPU_DATA_UINT_24_8_DEPRECATED) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning, "`UINT_24_8` is deprecated, use `FLOAT` instead", 1);
|
||||
}
|
||||
|
||||
if (!pygpu_buffer_pyobj_as_shape(length_ob, shape, &shape_len)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (init && PyObject_CheckBuffer(init)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(init, &pybuffer, PyBUF_ND | PyBUF_FORMAT) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_ssize_t *pybuffer_shape = pybuffer.shape;
|
||||
Py_ssize_t pybuffer_ndim = pybuffer.ndim;
|
||||
if (!pybuffer_shape) {
|
||||
pybuffer_shape = &pybuffer.len;
|
||||
pybuffer_ndim = 1;
|
||||
}
|
||||
|
||||
if (pygpu_buffer_dimensions_tot_len_compare(shape, shape_len, pybuffer_shape, pybuffer_ndim)) {
|
||||
buffer = pygpu_buffer_make_from_data(
|
||||
init, eGPUDataFormat(pygpu_dataformat.value_found), shape_len, shape, pybuffer.buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, shape_len, nullptr);
|
||||
if (init && pygpu_buffer_ass_slice(buffer, 0, shape[0], init)) {
|
||||
Py_DECREF(buffer);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(buffer);
|
||||
}
|
||||
|
||||
static int pygpu_buffer__tp_is_gc(BPyGPUBuffer *self)
|
||||
{
|
||||
return self->parent != nullptr;
|
||||
}
|
||||
|
||||
/* BPyGPUBuffer sequence methods */
|
||||
|
||||
static Py_ssize_t pygpu_buffer__sq_length(BPyGPUBuffer *self)
|
||||
{
|
||||
return self->shape[0];
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer_slice(BPyGPUBuffer *self, Py_ssize_t begin, Py_ssize_t end)
|
||||
{
|
||||
PyObject *list;
|
||||
Py_ssize_t count;
|
||||
|
||||
begin = std::max<Py_ssize_t>(begin, 0);
|
||||
end = std::min(end, self->shape[0]);
|
||||
begin = std::min(begin, end);
|
||||
|
||||
list = PyList_New(end - begin);
|
||||
|
||||
for (count = begin; count < end; count++) {
|
||||
PyList_SET_ITEM(list, count - begin, pygpu_buffer__sq_item(self, count));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__sq_ass_item(BPyGPUBuffer *self, Py_ssize_t i, PyObject *v)
|
||||
{
|
||||
if (i >= self->shape[0] || i < 0) {
|
||||
PyErr_SetString(PyExc_IndexError, "array assignment index out of range");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (self->shape_len != 1) {
|
||||
BPyGPUBuffer *row = reinterpret_cast<BPyGPUBuffer *>(pygpu_buffer__sq_item(self, i));
|
||||
|
||||
if (row) {
|
||||
const int ret = pygpu_buffer_ass_slice(row, 0, self->shape[1], v);
|
||||
Py_DECREF(row);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (self->format) {
|
||||
case GPU_DATA_FLOAT:
|
||||
return PyArg_Parse(v, "f:Expected floats", &self->buf.as_float[i]) ? 0 : -1;
|
||||
case GPU_DATA_INT:
|
||||
return PyArg_Parse(v, "i:Expected ints", &self->buf.as_int[i]) ? 0 : -1;
|
||||
case GPU_DATA_UBYTE:
|
||||
return PyArg_Parse(v, "b:Expected ints", &self->buf.as_byte[i]) ? 0 : -1;
|
||||
case GPU_DATA_UINT:
|
||||
case GPU_DATA_UINT_24_8_DEPRECATED:
|
||||
case GPU_DATA_10_11_11_REV:
|
||||
return PyArg_Parse(v, "I:Expected unsigned ints", &self->buf.as_uint[i]) ? 0 : -1;
|
||||
default:
|
||||
return 0; /* should never happen */
|
||||
}
|
||||
}
|
||||
|
||||
static PyObject *pygpu_buffer__mp_subscript(BPyGPUBuffer *self, PyObject *item)
|
||||
{
|
||||
if (PyIndex_Check(item)) {
|
||||
Py_ssize_t i;
|
||||
i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
if (i < 0) {
|
||||
i += self->shape[0];
|
||||
}
|
||||
return pygpu_buffer__sq_item(self, i);
|
||||
}
|
||||
if (PySlice_Check(item)) {
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx(item, self->shape[0], &start, &stop, &step, &slicelength) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (slicelength <= 0) {
|
||||
return PyTuple_New(0);
|
||||
}
|
||||
if (step == 1) {
|
||||
return pygpu_buffer_slice(self, start, stop);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "buffer indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static int pygpu_buffer__mp_ass_subscript(BPyGPUBuffer *self, PyObject *item, PyObject *value)
|
||||
{
|
||||
if (PyIndex_Check(item)) {
|
||||
Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
|
||||
if (i == -1 && PyErr_Occurred()) {
|
||||
return -1;
|
||||
}
|
||||
if (i < 0) {
|
||||
i += self->shape[0];
|
||||
}
|
||||
return pygpu_buffer__sq_ass_item(self, i, value);
|
||||
}
|
||||
if (PySlice_Check(item)) {
|
||||
Py_ssize_t start, stop, step, slicelength;
|
||||
|
||||
if (PySlice_GetIndicesEx(item, self->shape[0], &start, &stop, &step, &slicelength) < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (step == 1) {
|
||||
return pygpu_buffer_ass_slice(self, start, stop, value);
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_IndexError, "slice steps not supported with vectors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "buffer indices must be integers, not %.200s", Py_TYPE(item)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_buffer__tp_methods[] = {
|
||||
{"to_list",
|
||||
reinterpret_cast<PyCFunction>(pygpu_buffer_to_list_recursive),
|
||||
METH_NOARGS,
|
||||
pygpu_buffer_to_list_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyGetSetDef pygpu_buffer_getseters[] = {
|
||||
{"dimensions",
|
||||
reinterpret_cast<getter>(pygpu_buffer_dimensions_get),
|
||||
reinterpret_cast<setter>(pygpu_buffer_dimensions_set),
|
||||
pygpu_buffer_dimensions_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
};
|
||||
|
||||
static PySequenceMethods pygpu_buffer__tp_as_sequence = {
|
||||
/*sq_length*/ reinterpret_cast<lenfunc>(pygpu_buffer__sq_length),
|
||||
/*sq_concat*/ nullptr,
|
||||
/*sq_repeat*/ nullptr,
|
||||
/*sq_item*/ reinterpret_cast<ssizeargfunc>(pygpu_buffer__sq_item),
|
||||
/*was_sq_slice*/ nullptr, /* DEPRECATED. Handled by #pygpu_buffer__sq_item. */
|
||||
/*sq_ass_item*/ reinterpret_cast<ssizeobjargproc>(pygpu_buffer__sq_ass_item),
|
||||
/*was_sq_ass_slice*/ nullptr, /* DEPRECATED. Handled by #pygpu_buffer__sq_ass_item. */
|
||||
/*sq_contains*/ nullptr,
|
||||
/*sq_inplace_concat*/ nullptr,
|
||||
/*sq_inplace_repeat*/ nullptr,
|
||||
};
|
||||
|
||||
static PyMappingMethods pygpu_buffer__tp_as_mapping = {
|
||||
/*mp_length*/ reinterpret_cast<lenfunc>(pygpu_buffer__sq_length),
|
||||
/*mp_subscript*/ reinterpret_cast<binaryfunc>(pygpu_buffer__mp_subscript),
|
||||
/*mp_ass_subscript*/ reinterpret_cast<objobjargproc>(pygpu_buffer__mp_ass_subscript),
|
||||
};
|
||||
|
||||
#ifdef PYGPU_BUFFER_PROTOCOL
|
||||
static void pygpu_buffer_strides_calc(const eGPUDataFormat format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape,
|
||||
Py_ssize_t *r_strides)
|
||||
{
|
||||
Py_ssize_t stride = GPU_texture_dataformat_size(format);
|
||||
for (int i = shape_len; i-- > 0;) {
|
||||
r_strides[i] = stride;
|
||||
stride *= shape[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* Here is the buffer interface function */
|
||||
static int pygpu_buffer__bf_getbuffer(BPyGPUBuffer *self, Py_buffer *view, int flags)
|
||||
{
|
||||
if (UNLIKELY(view == nullptr)) {
|
||||
PyErr_SetString(PyExc_ValueError, "null view in get-buffer is obsolete");
|
||||
return -1;
|
||||
}
|
||||
|
||||
memset(view, 0, sizeof(*view));
|
||||
|
||||
view->obj = reinterpret_cast<PyObject *>(self);
|
||||
view->buf = self->buf.as_void;
|
||||
view->len = bpygpu_Buffer_size(self);
|
||||
view->readonly = 0;
|
||||
view->itemsize = GPU_texture_dataformat_size(eGPUDataFormat(self->format));
|
||||
if (flags & PyBUF_FORMAT) {
|
||||
view->format = const_cast<char *>(pygpu_buffer_formatstr(eGPUDataFormat(self->format)));
|
||||
}
|
||||
if (flags & PyBUF_ND) {
|
||||
view->ndim = self->shape_len;
|
||||
view->shape = self->shape;
|
||||
}
|
||||
if (flags & PyBUF_STRIDES) {
|
||||
view->strides = MEM_new_array_uninitialized<Py_ssize_t>(size_t(view->ndim),
|
||||
"BPyGPUBuffer strides");
|
||||
pygpu_buffer_strides_calc(
|
||||
eGPUDataFormat(self->format), view->ndim, view->shape, view->strides);
|
||||
}
|
||||
view->suboffsets = nullptr;
|
||||
view->internal = nullptr;
|
||||
|
||||
Py_INCREF(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pygpu_buffer__bf_releasebuffer(PyObject * /*exporter*/, Py_buffer *view)
|
||||
{
|
||||
MEM_SAFE_DELETE(view->strides);
|
||||
}
|
||||
|
||||
static PyBufferProcs pygpu_buffer__tp_as_buffer = {
|
||||
/*bf_getbuffer*/ reinterpret_cast<getbufferproc>(pygpu_buffer__bf_getbuffer),
|
||||
/*bf_releasebuffer*/ static_cast<releasebufferproc>(pygpu_buffer__bf_releasebuffer),
|
||||
};
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_buffer__tp_doc,
|
||||
".. class:: Buffer\n"
|
||||
"\n"
|
||||
" For Python access to GPU functions requiring a pointer.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(format, dimensions, data)\n"
|
||||
"\n"
|
||||
" :param format: Format type to interpret the buffer.\n"
|
||||
" ``UINT_24_8`` is deprecated, use ``FLOAT`` instead.\n"
|
||||
" :type format: " PYDOC_DATAFORMAT_LITERAL
|
||||
"\n"
|
||||
" :param dimensions: Array describing the dimensions.\n"
|
||||
" :type dimensions: int | Sequence[int]\n"
|
||||
" :param data: Optional data array.\n"
|
||||
" :type data: Buffer | Sequence[float] | Sequence[int]\n");
|
||||
PyTypeObject BPyGPU_BufferType = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "Buffer",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUBuffer),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_buffer__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(pygpu_buffer__tp_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ &pygpu_buffer__tp_as_sequence,
|
||||
/*tp_as_mapping*/ &pygpu_buffer__tp_as_mapping,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
#ifdef PYGPU_BUFFER_PROTOCOL
|
||||
/*tp_as_buffer*/ &pygpu_buffer__tp_as_buffer,
|
||||
#else
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
#endif
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
||||
/*tp_doc*/ pygpu_buffer__tp_doc,
|
||||
/*tp_traverse*/ reinterpret_cast<traverseproc>(pygpu_buffer__tp_traverse),
|
||||
/*tp_clear*/ reinterpret_cast<inquiry>(pygpu_buffer__tp_clear),
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_buffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_buffer_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_buffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ reinterpret_cast<inquiry>(pygpu_buffer__tp_is_gc),
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static size_t pygpu_buffer_calc_size(const int format,
|
||||
const int shape_len,
|
||||
const Py_ssize_t *shape)
|
||||
{
|
||||
return pygpu_buffer_dimensions_tot_elem(shape, shape_len) *
|
||||
GPU_texture_dataformat_size(eGPUDataFormat(format));
|
||||
}
|
||||
|
||||
size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer)
|
||||
{
|
||||
return pygpu_buffer_calc_size(buffer->format, buffer->shape_len, buffer->shape);
|
||||
}
|
||||
|
||||
BPyGPUBuffer *BPyGPU_Buffer_CreatePyObject(const int format,
|
||||
const Py_ssize_t *shape,
|
||||
const int shape_len,
|
||||
void *buffer)
|
||||
{
|
||||
if (buffer == nullptr) {
|
||||
size_t size = pygpu_buffer_calc_size(format, shape_len, shape);
|
||||
buffer = MEM_new_zeroed(size, "BPyGPUBuffer buffer");
|
||||
}
|
||||
|
||||
return pygpu_buffer_make_from_data(nullptr, eGPUDataFormat(format), shape_len, shape, buffer);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
57
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.hh
Normal file
57
blender-5.2.0/source/blender/python/gpu/gpu_py_buffer.hh
Normal file
@@ -0,0 +1,57 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_sys_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPyGPU_BufferType;
|
||||
|
||||
#define BPyGPU_Buffer_Check(v) (Py_TYPE(v) == &BPyGPU_BufferType)
|
||||
|
||||
/**
|
||||
* Buffer Object
|
||||
*
|
||||
* For Python access to GPU functions requiring a pointer.
|
||||
*/
|
||||
struct BPyGPUBuffer {
|
||||
PyObject_HEAD
|
||||
PyObject *parent;
|
||||
|
||||
int format;
|
||||
int shape_len;
|
||||
Py_ssize_t *shape;
|
||||
|
||||
union {
|
||||
char *as_byte;
|
||||
int *as_int;
|
||||
uint *as_uint;
|
||||
float *as_float;
|
||||
|
||||
void *as_void;
|
||||
} buf;
|
||||
};
|
||||
|
||||
[[nodiscard]] size_t bpygpu_Buffer_size(BPyGPUBuffer *buffer);
|
||||
/**
|
||||
* Create a buffer object
|
||||
*
|
||||
* \param shape: An array of `shape_len` integers representing the size of each dimension.
|
||||
* \param buffer: When not NULL holds a contiguous buffer
|
||||
* with the correct format from which the buffer will be initialized
|
||||
*/
|
||||
[[nodiscard]] BPyGPUBuffer *BPyGPU_Buffer_CreatePyObject(int format,
|
||||
const Py_ssize_t *shape,
|
||||
int shape_len,
|
||||
void *buffer);
|
||||
|
||||
} // namespace blender
|
||||
518
blender-5.2.0/source/blender/python/gpu/gpu_py_capabilities.cc
Normal file
518
blender-5.2.0/source/blender/python/gpu/gpu_py_capabilities.cc
Normal file
@@ -0,0 +1,518 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_capabilities.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Functions
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_texture_size_get_doc,
|
||||
".. function:: max_texture_size_get()\n"
|
||||
"\n"
|
||||
" Get estimated maximum texture size to be able to handle.\n"
|
||||
"\n"
|
||||
" :return: Texture size.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_texture_size_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_texture_size());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_texture_layers_get_doc,
|
||||
".. function:: max_texture_layers_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of layers in texture.\n"
|
||||
"\n"
|
||||
" :return: Number of layers.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_texture_layers_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_texture_layers());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_get_doc,
|
||||
".. function:: max_textures_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the vertex shader and the\n"
|
||||
" fragment processor.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_vert_get_doc,
|
||||
".. function:: max_textures_vert_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_vert_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_geom_get_doc,
|
||||
".. function:: max_textures_geom_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the geometry shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_geom_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_textures_frag_get_doc,
|
||||
".. function:: max_textures_frag_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported texture image units used for\n"
|
||||
" accessing texture maps from the fragment shader.\n"
|
||||
"\n"
|
||||
" :return: Texture image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_textures_frag_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_textures());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_images_get_doc,
|
||||
".. function:: max_images_get()\n"
|
||||
"\n"
|
||||
" Get maximum supported number of image units.\n"
|
||||
"\n"
|
||||
" :return: Number of image units.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_images_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_images());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_uniforms_vert_get_doc,
|
||||
".. function:: max_uniforms_vert_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of values held in uniform variable\n"
|
||||
" storage for a vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Number of values.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_uniforms_vert_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_uniforms_vert());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_uniforms_frag_get_doc,
|
||||
".. function:: max_uniforms_frag_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of values held in uniform variable\n"
|
||||
" storage for a fragment shader.\n"
|
||||
"\n"
|
||||
" :return: Number of values.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_uniforms_frag_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_uniforms_frag());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_batch_indices_get_doc,
|
||||
".. function:: max_batch_indices_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex array indices.\n"
|
||||
"\n"
|
||||
" :return: Number of indices.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_batch_indices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_batch_indices());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_batch_vertices_get_doc,
|
||||
".. function:: max_batch_vertices_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex array vertices.\n"
|
||||
"\n"
|
||||
" :return: Number of vertices.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_batch_vertices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_batch_vertices());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_vertex_attribs_get_doc,
|
||||
".. function:: max_vertex_attribs_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of vertex attributes accessible to\n"
|
||||
" a vertex shader.\n"
|
||||
"\n"
|
||||
" :return: Number of attributes.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_vertex_attribs_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_vertex_attribs());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_varying_floats_get_doc,
|
||||
".. function:: max_varying_floats_get()\n"
|
||||
"\n"
|
||||
" Get maximum number of varying variables used by\n"
|
||||
" vertex and fragment shaders.\n"
|
||||
"\n"
|
||||
" :return: Number of variables.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_varying_floats_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyLong_FromLong(GPU_max_varying_floats());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_extensions_get_doc,
|
||||
".. function:: extensions_get()\n"
|
||||
"\n"
|
||||
" Get supported extensions in the current context.\n"
|
||||
"\n"
|
||||
" :return: Extensions.\n"
|
||||
" :rtype: tuple[str, ...]\n");
|
||||
static PyObject *pygpu_extensions_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int extensions_len = GPU_extensions_len();
|
||||
PyObject *ret = PyTuple_New(extensions_len);
|
||||
PyObject **ob_items = (reinterpret_cast<PyTupleObject *>(ret))->ob_item;
|
||||
for (int i = 0; i < extensions_len; i++) {
|
||||
ob_items[i] = PyUnicode_FromString(GPU_extension_get(i));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute_shader_support_get_doc,
|
||||
".. function:: compute_shader_support_get()\n"
|
||||
"\n"
|
||||
" Are compute shaders supported.\n"
|
||||
"\n"
|
||||
" :return: True when supported, False when not supported.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_compute_shader_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
static bool deprecation_warning_issued = false;
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"compute_shader_support_get is deprecated. All platforms have support for "
|
||||
"compute shaders.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
|
||||
return PyBool_FromLong(true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_shader_image_load_store_support_get_doc,
|
||||
".. function:: shader_image_load_store_support_get()\n"
|
||||
"\n"
|
||||
" Is image load/store supported.\n"
|
||||
"\n"
|
||||
" :return: True when supported, False when not supported.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_shader_image_load_store_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
static bool deprecation_warning_issued = false;
|
||||
if (!deprecation_warning_issued) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"shader_image_load_store_support_get is deprecated. All platforms have support for "
|
||||
"image load store.",
|
||||
1);
|
||||
deprecation_warning_issued = true;
|
||||
}
|
||||
/* Now required to start Blender. */
|
||||
return PyBool_FromLong(true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_hdr_support_get_doc,
|
||||
".. function:: hdr_support_get()\n"
|
||||
"\n"
|
||||
" Return whether GPU backend supports High Dynamic range for viewport.\n"
|
||||
"\n"
|
||||
" :return: HDR support available.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_hdr_support_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyBool_FromLong(GPU_hdr_support());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_work_group_count_get_doc,
|
||||
".. function:: max_work_group_count_get(index)\n"
|
||||
"\n"
|
||||
" Get maximum number of work groups that may be dispatched to a compute shader.\n"
|
||||
"\n"
|
||||
" :param index: Index of the dimension.\n"
|
||||
" :type index: int\n"
|
||||
" :return: Maximum number of work groups for the queried dimension.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_work_group_count_get(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int index;
|
||||
if (!PyArg_ParseTuple(args, "i", &index)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int max_work_group_count = GPU_max_work_group_count(index);
|
||||
|
||||
return PyLong_FromLong(max_work_group_count);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_max_work_group_size_get_doc,
|
||||
".. function:: max_work_group_size_get(index)\n"
|
||||
"\n"
|
||||
" Get maximum size of a work group that may be dispatched to a compute shader.\n"
|
||||
"\n"
|
||||
" :param index: Index of the dimension.\n"
|
||||
" :type index: int\n"
|
||||
" :return: Maximum size of a work group for the queried dimension.\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *pygpu_max_work_group_size_get(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int index;
|
||||
if (!PyArg_ParseTuple(args, "i", &index)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int max_work_group_size = GPU_max_work_group_size(index);
|
||||
|
||||
return PyLong_FromLong(max_work_group_size);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_capabilities__tp_methods[] = {
|
||||
{"max_texture_size_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_texture_size_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_texture_size_get_doc},
|
||||
{"max_texture_layers_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_texture_layers_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_texture_layers_get_doc},
|
||||
{"max_textures_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_get_doc},
|
||||
{"max_textures_vert_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_vert_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_vert_get_doc},
|
||||
{"max_textures_geom_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_geom_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_geom_get_doc},
|
||||
{"max_textures_frag_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_textures_frag_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_textures_frag_get_doc},
|
||||
{"max_images_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_images_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_images_get_doc},
|
||||
{"max_uniforms_vert_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_uniforms_vert_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_uniforms_vert_get_doc},
|
||||
{"max_uniforms_frag_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_uniforms_frag_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_uniforms_frag_get_doc},
|
||||
{"max_batch_indices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_batch_indices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_batch_indices_get_doc},
|
||||
{"max_batch_vertices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_batch_vertices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_batch_vertices_get_doc},
|
||||
{"max_vertex_attribs_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_vertex_attribs_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_vertex_attribs_get_doc},
|
||||
{"max_varying_floats_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_max_varying_floats_get),
|
||||
METH_NOARGS,
|
||||
pygpu_max_varying_floats_get_doc},
|
||||
{"extensions_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_extensions_get),
|
||||
METH_NOARGS,
|
||||
pygpu_extensions_get_doc},
|
||||
|
||||
{"compute_shader_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_compute_shader_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_compute_shader_support_get_doc},
|
||||
{"shader_image_load_store_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_shader_image_load_store_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_shader_image_load_store_support_get_doc},
|
||||
{"hdr_support_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_hdr_support_get),
|
||||
METH_NOARGS,
|
||||
pygpu_hdr_support_get_doc},
|
||||
{
|
||||
"max_work_group_count_get",
|
||||
static_cast<PyCFunction>(pygpu_max_work_group_count_get),
|
||||
METH_VARARGS,
|
||||
pygpu_max_work_group_count_get_doc,
|
||||
},
|
||||
{
|
||||
"max_work_group_size_get",
|
||||
static_cast<PyCFunction>(pygpu_max_work_group_size_get),
|
||||
METH_VARARGS,
|
||||
pygpu_max_work_group_size_get_doc,
|
||||
},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_capabilities__tp_doc,
|
||||
"This module provides access to the GPU capabilities.");
|
||||
static PyModuleDef pygpu_capabilities_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.capabilities",
|
||||
/*m_doc*/ pygpu_capabilities__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_capabilities__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_capabilities_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_capabilities_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_capabilities_init();
|
||||
|
||||
} // namespace blender
|
||||
161
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.cc
Normal file
161
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.cc
Normal file
@@ -0,0 +1,161 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_capabilities.hh"
|
||||
#include "GPU_compute.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_compute.hh" /* own include */
|
||||
#include "gpu_py_shader.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute_dispatch_doc,
|
||||
".. function:: dispatch(shader, groups_x_len, groups_y_len, groups_z_len)\n"
|
||||
"\n"
|
||||
" Dispatches GPU compute.\n"
|
||||
"\n"
|
||||
" :param shader: The shader that you want to dispatch.\n"
|
||||
" :type shader: :class:`gpu.types.GPUShader`\n"
|
||||
" :param groups_x_len: Int for group x length:\n"
|
||||
" :type groups_x_len: int\n"
|
||||
" :param groups_y_len: Int for group y length:\n"
|
||||
" :type groups_y_len: int\n"
|
||||
" :param groups_z_len: Int for group z length:\n"
|
||||
" :type groups_z_len: int\n");
|
||||
static PyObject *pygpu_compute_dispatch(PyObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPyGPUShader *py_shader;
|
||||
int groups_x_len;
|
||||
int groups_y_len;
|
||||
int groups_z_len;
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"shader", "groups_x_len", "groups_y_len", "groups_z_len", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `shader` */
|
||||
"i" /* `groups_x_len` */
|
||||
"i" /* `groups_y_len` */
|
||||
"i" /* `groups_z_len` */
|
||||
":dispatch",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &py_shader, &groups_x_len, &groups_y_len, &groups_z_len))
|
||||
{
|
||||
if (!BPyGPUShader_Check(py_shader)) {
|
||||
PyErr_Format(PyExc_TypeError, "Expected a GPUShader, got %s", Py_TYPE(py_shader)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Check that groups do not exceed #GPU_max_work_group_count(). */
|
||||
const int max_work_group_count_x = GPU_max_work_group_count(0);
|
||||
const int max_work_group_count_y = GPU_max_work_group_count(1);
|
||||
const int max_work_group_count_z = GPU_max_work_group_count(2);
|
||||
|
||||
/* Report back to the user both the requested and the maximum supported value. */
|
||||
if (groups_x_len > GPU_max_work_group_count(0)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_x_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_x_len,
|
||||
max_work_group_count_x);
|
||||
return nullptr;
|
||||
}
|
||||
if (groups_y_len > GPU_max_work_group_count(1)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_y_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_y_len,
|
||||
max_work_group_count_y);
|
||||
return nullptr;
|
||||
}
|
||||
if (groups_z_len > GPU_max_work_group_count(2)) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"groups_z_len (%d) exceeds maximum supported value (max work group count: %d)",
|
||||
groups_z_len,
|
||||
max_work_group_count_z);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
gpu::Shader *shader = py_shader->shader;
|
||||
GPU_compute_dispatch(shader, groups_x_len, groups_y_len, groups_z_len);
|
||||
GPU_memory_barrier(GPU_BARRIER_TEXTURE_FETCH | GPU_BARRIER_SHADER_IMAGE_ACCESS);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_compute__tp_methods[] = {
|
||||
{"dispatch",
|
||||
reinterpret_cast<PyCFunction>(pygpu_compute_dispatch),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_compute_dispatch_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_compute__tp_doc,
|
||||
"This module provides access to the global GPU compute functions.");
|
||||
static PyModuleDef pygpu_compute_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.compute",
|
||||
/*m_doc*/ pygpu_compute__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_compute__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_compute_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_compute_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_compute.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_compute_init();
|
||||
|
||||
} // namespace blender
|
||||
266
blender-5.2.0/source/blender/python/gpu/gpu_py_element.cc
Normal file
266
blender-5.2.0/source/blender/python/gpu/gpu_py_element.cc
Normal file
@@ -0,0 +1,266 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_index_buffer.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_element.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name IndexBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_IndexBuf__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *error_prefix = "IndexBuf.__new__";
|
||||
bool ok = true;
|
||||
|
||||
PyC_StringEnum prim_type = {bpygpu_primtype_items, GPU_PRIM_NONE};
|
||||
PyObject *seq;
|
||||
|
||||
uint verts_per_prim;
|
||||
uint index_len;
|
||||
GPUIndexBufBuilder builder;
|
||||
|
||||
static const char *_keywords[] = {"type", "seq", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"$" /* Keyword only arguments. */
|
||||
"O&" /* `type` */
|
||||
"O" /* `seq` */
|
||||
":IndexBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, PyC_ParseStringEnum, &prim_type, &seq))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
verts_per_prim = GPU_indexbuf_primitive_len(GPUPrimType(prim_type.value_found));
|
||||
if (verts_per_prim == -1) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"The argument 'type' must be "
|
||||
"'POINTS', 'LINES', 'TRIS', 'LINES_ADJ' or 'TRIS_ADJ'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyObject_CheckBuffer(seq)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(seq, &pybuffer, PyBUF_FORMAT | PyBUF_ND) == -1) {
|
||||
/* PyObject_GetBuffer already handles error messages. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pybuffer.ndim != 1 && pybuffer.shape[1] != verts_per_prim) {
|
||||
PyErr_Format(PyExc_ValueError, "Each primitive must exactly %d indices", verts_per_prim);
|
||||
PyBuffer_Release(&pybuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pybuffer.itemsize != 4 ||
|
||||
PyC_StructFmt_type_is_float_any(PyC_StructFmt_type_from_str(pybuffer.format)))
|
||||
{
|
||||
PyErr_SetString(PyExc_ValueError, "Each index must be an 4-bytes integer value");
|
||||
PyBuffer_Release(&pybuffer);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
index_len = pybuffer.shape[0];
|
||||
if (pybuffer.ndim != 1) {
|
||||
index_len *= pybuffer.shape[1];
|
||||
}
|
||||
|
||||
/* The `vertex_len` parameter is only used for asserts in the Debug build. */
|
||||
/* Not very useful in python since scripts are often tested in Release build. */
|
||||
/* Use `INT_MAX` instead of the actual number of vertices. */
|
||||
GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
|
||||
|
||||
uint *buf = static_cast<uint *>(pybuffer.buf);
|
||||
for (uint i = index_len; i--; buf++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, *buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
PyObject *seq_fast = PySequence_Fast(seq, error_prefix);
|
||||
|
||||
if (seq_fast == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
|
||||
|
||||
PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
|
||||
|
||||
index_len = seq_len * verts_per_prim;
|
||||
|
||||
/* The `vertex_len` parameter is only used for asserts in the Debug build. */
|
||||
/* Not very useful in python since scripts are often tested in Release build. */
|
||||
/* Use `INT_MAX` instead of the actual number of vertices. */
|
||||
GPU_indexbuf_init(&builder, GPUPrimType(prim_type.value_found), index_len, INT_MAX);
|
||||
|
||||
if (verts_per_prim == 1) {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, PyC_Long_AsU32(seq_items[i]));
|
||||
}
|
||||
}
|
||||
else {
|
||||
int values[4];
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
|
||||
if (seq_fast_item == nullptr) {
|
||||
PyErr_Format(PyExc_TypeError,
|
||||
"%s: expected a sequence, got %s",
|
||||
error_prefix,
|
||||
Py_TYPE(seq_items[i])->tp_name);
|
||||
ok = false;
|
||||
goto finally;
|
||||
}
|
||||
|
||||
ok = PyC_AsArray_FAST(values,
|
||||
sizeof(*values),
|
||||
seq_fast_item,
|
||||
verts_per_prim,
|
||||
&PyLong_Type,
|
||||
error_prefix) == 0;
|
||||
|
||||
if (ok) {
|
||||
for (uint j = 0; j < verts_per_prim; j++) {
|
||||
GPU_indexbuf_add_generic_vert(&builder, values[j]);
|
||||
}
|
||||
}
|
||||
Py_DECREF(seq_fast_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
finally:
|
||||
|
||||
Py_DECREF(seq_fast);
|
||||
}
|
||||
|
||||
if (ok == false) {
|
||||
MEM_delete(builder.data);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUIndexBuf_CreatePyObject(GPU_indexbuf_build(&builder));
|
||||
}
|
||||
|
||||
static void pygpu_IndexBuf__tp_dealloc(BPyGPUIndexBuf *self)
|
||||
{
|
||||
GPU_indexbuf_discard(self->elem);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_IndexBuf__tp_doc,
|
||||
".. class:: GPUIndexBuf\n"
|
||||
"\n"
|
||||
" Contains an index buffer.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(type, seq)\n"
|
||||
"\n"
|
||||
" :param type: The primitive type this index buffer is composed of.\n"
|
||||
" :type type: Literal['POINTS', 'LINES', 'TRIS', 'LINES_ADJ', 'TRIS_ADJ']\n"
|
||||
" :param seq: Indices this index buffer will contain.\n"
|
||||
" Whether a 1D or 2D sequence is required depends on the type.\n"
|
||||
" Optionally the sequence can support the buffer protocol.\n"
|
||||
" :type seq: Buffer | Sequence[int] | Sequence[Sequence[int]]\n");
|
||||
PyTypeObject BPyGPUIndexBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUIndexBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUIndexBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_IndexBuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_IndexBuf__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_IndexBuf__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUIndexBuf_CreatePyObject(gpu::IndexBuf *elem)
|
||||
{
|
||||
BPyGPUIndexBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUIndexBuf, &BPyGPUIndexBuf_Type);
|
||||
self->elem = elem;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
30
blender-5.2.0/source/blender/python/gpu/gpu_py_element.hh
Normal file
30
blender-5.2.0/source/blender/python/gpu/gpu_py_element.hh
Normal file
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class IndexBuf;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUIndexBuf_Type;
|
||||
|
||||
#define BPyGPUIndexBuf_Check(v) (Py_TYPE(v) == &BPyGPUIndexBuf_Type)
|
||||
|
||||
struct BPyGPUIndexBuf {
|
||||
PyObject_HEAD
|
||||
gpu::IndexBuf *elem;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUIndexBuf_CreatePyObject(gpu::IndexBuf *elem);
|
||||
|
||||
} // namespace blender
|
||||
958
blender-5.2.0/source/blender/python/gpu/gpu_py_framebuffer.cc
Normal file
958
blender-5.2.0/source/blender/python/gpu/gpu_py_framebuffer.cc
Normal file
@@ -0,0 +1,958 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the framebuffer functionalities of the 'gpu' module
|
||||
* used for off-screen OpenGL rendering.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_init_exit.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_buffer.hh"
|
||||
#include "gpu_py_framebuffer.hh" /* own include */
|
||||
#include "gpu_py_texture.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::FrameBuffer Common Utilities
|
||||
* \{ */
|
||||
|
||||
static int pygpu_framebuffer_valid_check(BPyGPUFrameBuffer *bpygpu_fb)
|
||||
{
|
||||
if (UNLIKELY(bpygpu_fb->fb == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError, "GPU framebuffer was freed, no further access is valid");
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define PYGPU_FRAMEBUFFER_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_framebuffer_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
static void pygpu_framebuffer_free_if_possible(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_is_init()) {
|
||||
GPU_framebuffer_free(fb);
|
||||
}
|
||||
else {
|
||||
printf("PyFramebuffer freed after the context has been destroyed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void pygpu_framebuffer_free_safe(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
if (self->fb) {
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
GPU_framebuffer_py_reference_set(self->fb, nullptr);
|
||||
if (!self->shared_reference)
|
||||
#endif
|
||||
{
|
||||
pygpu_framebuffer_free_if_possible(self->fb);
|
||||
}
|
||||
|
||||
self->fb = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* Keep less than or equal to #FRAMEBUFFER_STACK_DEPTH */
|
||||
#define GPU_PY_FRAMEBUFFER_STACK_LEN 16
|
||||
|
||||
static bool pygpu_framebuffer_stack_push_and_bind_or_error(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_framebuffer_stack_level_get() >= GPU_PY_FRAMEBUFFER_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum framebuffer stack depth " STRINGIFY(GPU_PY_FRAMEBUFFER_STACK_LEN) " reached");
|
||||
return false;
|
||||
}
|
||||
GPU_framebuffer_push(GPU_framebuffer_active_get());
|
||||
GPU_framebuffer_bind(fb);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_framebuffer_stack_pop_and_restore_or_error(gpu::FrameBuffer *fb)
|
||||
{
|
||||
if (GPU_framebuffer_stack_level_get() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum framebuffer stack depth reached");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fb && !GPU_framebuffer_bound(fb)) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Framebuffer is not bound");
|
||||
return false;
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *fb_prev = GPU_framebuffer_pop();
|
||||
GPU_framebuffer_bind(fb_prev);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct PyFrameBufferStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
BPyGPUFrameBuffer *py_fb;
|
||||
int level;
|
||||
};
|
||||
|
||||
static void pygpu_framebuffer_stack_context__tp_dealloc(PyFrameBufferStackContext *self)
|
||||
{
|
||||
Py_DECREF(self->py_fb);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer_stack_context_enter(PyFrameBufferStackContext *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self->py_fb);
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!pygpu_framebuffer_stack_push_and_bind_or_error(self->py_fb->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
self->level = GPU_framebuffer_stack_level_get();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer_stack_context_exit(PyFrameBufferStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self->py_fb);
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level == -1) {
|
||||
fprintf(stderr, "Not yet in use\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int level = GPU_framebuffer_stack_level_get();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
|
||||
if (!pygpu_framebuffer_stack_pop_and_restore_or_error(self->py_fb->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_framebuffer_stack_context__tp_methods[] = {
|
||||
{"__enter__",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_stack_context_enter),
|
||||
METH_NOARGS},
|
||||
{"__exit__",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_stack_context_exit),
|
||||
METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyTypeObject FramebufferStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUFrameBufferStackContext",
|
||||
/*tp_basicsize*/ sizeof(PyFrameBufferStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_framebuffer_stack_context__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ nullptr,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_framebuffer_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_bind_doc,
|
||||
".. method:: bind()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced bind calls, even in the case of an error.\n");
|
||||
static PyObject *pygpu_framebuffer_bind(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PyFrameBufferStackContext *ret = PyObject_New(PyFrameBufferStackContext,
|
||||
&FramebufferStackContext_Type);
|
||||
ret->py_fb = self;
|
||||
ret->level = -1;
|
||||
Py_INCREF(self);
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUFramebuffer Type
|
||||
* \{ */
|
||||
|
||||
/* Fill in the GPUAttachment according to the PyObject parameter.
|
||||
* PyObject *o can be nullptr, Py_None, BPyGPUTexture or a dictionary containing the keyword
|
||||
* "texture" and the optional keywords "layer" and "mip". Returns false on error. In this case, a
|
||||
* python message will be raised and GPUAttachment will not be touched. */
|
||||
static bool pygpu_framebuffer_new_parse_arg(PyObject *o, GPUAttachment *r_attach)
|
||||
{
|
||||
GPUAttachment tmp_attach = GPU_ATTACHMENT_NONE;
|
||||
|
||||
if (!o || o == Py_None) {
|
||||
/* Pass. */;
|
||||
}
|
||||
else if (BPyGPUTexture_Check(o)) {
|
||||
if (!bpygpu_ParseTexture(o, &tmp_attach.tex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
const char *c_texture = "texture";
|
||||
const char *c_layer = "layer";
|
||||
const char *c_mip = "mip";
|
||||
PyObject *key, *value;
|
||||
Py_ssize_t pos = 0;
|
||||
while (PyDict_Next(o, &pos, &key, &value)) {
|
||||
if (!PyUnicode_Check(key)) {
|
||||
PyErr_SetString(PyExc_TypeError, "keywords must be strings");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c_texture && PyUnicode_CompareWithASCIIString(key, c_texture) == 0) {
|
||||
/* Compare only once. */
|
||||
c_texture = nullptr;
|
||||
if (!bpygpu_ParseTexture(value, &tmp_attach.tex)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c_layer && PyUnicode_CompareWithASCIIString(key, c_layer) == 0) {
|
||||
/* Compare only once. */
|
||||
c_layer = nullptr;
|
||||
tmp_attach.layer = PyLong_AsLong(value);
|
||||
if (tmp_attach.layer == -1 && PyErr_Occurred()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (c_mip && PyUnicode_CompareWithASCIIString(key, c_mip) == 0) {
|
||||
/* Compare only once. */
|
||||
c_mip = nullptr;
|
||||
tmp_attach.mip = PyLong_AsLong(value);
|
||||
if (tmp_attach.mip == -1 && PyErr_Occurred()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_Format(
|
||||
PyExc_TypeError, "'%U' is an invalid keyword argument for this attribute", key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*r_attach = tmp_attach;
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_framebuffer__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!GPU_context_active_get()) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "No active GPU context found");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject *depth_attachment = nullptr;
|
||||
PyObject *color_attachements = nullptr;
|
||||
static const char *_keywords[] = {"depth_slot", "color_slots", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O" /* `depth_slot` */
|
||||
"O" /* `color_slots` */
|
||||
":GPUFrameBuffer.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &depth_attachment, &color_attachements))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Keep in sync with #GPU_FB_MAX_COLOR_ATTACHMENT.
|
||||
* TODO: share the define. */
|
||||
#define BPYGPU_FB_MAX_COLOR_ATTACHMENT 6
|
||||
|
||||
GPUAttachment config[BPYGPU_FB_MAX_COLOR_ATTACHMENT + 1];
|
||||
|
||||
if (!pygpu_framebuffer_new_parse_arg(depth_attachment, &config[0])) {
|
||||
return nullptr;
|
||||
}
|
||||
if (config[0].tex && !GPU_texture_has_depth_format(config[0].tex)) {
|
||||
PyErr_SetString(PyExc_ValueError, "Depth texture with incompatible format");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int color_attachements_len = 0;
|
||||
if (color_attachements && color_attachements != Py_None) {
|
||||
if (PySequence_Check(color_attachements)) {
|
||||
color_attachements_len = PySequence_Size(color_attachements);
|
||||
if (color_attachements_len > BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"too many attachments, max is " STRINGIFY(BPYGPU_FB_MAX_COLOR_ATTACHMENT));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < color_attachements_len; i++) {
|
||||
PyObject *o = PySequence_GetItem(color_attachements, i);
|
||||
bool ok = pygpu_framebuffer_new_parse_arg(o, &config[i + 1]);
|
||||
Py_DECREF(o);
|
||||
if (!ok) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!pygpu_framebuffer_new_parse_arg(color_attachements, &config[1])) {
|
||||
return nullptr;
|
||||
}
|
||||
color_attachements_len = 1;
|
||||
}
|
||||
}
|
||||
|
||||
gpu::FrameBuffer *fb_python = GPU_framebuffer_create("fb_python");
|
||||
GPU_framebuffer_config_array(fb_python, config, color_attachements_len + 1);
|
||||
|
||||
return BPyGPUFrameBuffer_CreatePyObject(fb_python, false);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_is_bound_doc,
|
||||
"Checks if this is the active frame-buffer in the context.\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
static PyObject *pygpu_framebuffer_is_bound(BPyGPUFrameBuffer *self, void * /*type*/)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
return PyBool_FromLong(GPU_framebuffer_bound(self->fb));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_clear_doc,
|
||||
".. method:: clear(*, color=None, depth=None, stencil=None)\n"
|
||||
"\n"
|
||||
" Fill color, depth and stencil textures with specific value.\n"
|
||||
" Common values: color=(0.0, 0.0, 0.0, 1.0), depth=1.0, stencil=0.\n"
|
||||
"\n"
|
||||
" :param color: Sequence of 3 or 4 floats representing ``(r, g, b, a)``.\n"
|
||||
" :type color: Sequence[float] | None\n"
|
||||
" :param depth: depth value.\n"
|
||||
" :type depth: float | None\n"
|
||||
" :param stencil: stencil value.\n"
|
||||
" :type stencil: int | None\n");
|
||||
static PyObject *pygpu_framebuffer_clear(BPyGPUFrameBuffer *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
|
||||
if (!GPU_framebuffer_bound(self->fb)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyObject *py_col = nullptr;
|
||||
PyObject *py_depth = nullptr;
|
||||
PyObject *py_stencil = nullptr;
|
||||
|
||||
static const char *_keywords[] = {"color", "depth", "stencil", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O" /* `color` */
|
||||
"O" /* `depth` */
|
||||
"O" /* `stencil` */
|
||||
":clear",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &py_col, &py_depth, &py_stencil)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUFrameBufferBits buffers = GPUFrameBufferBits(0);
|
||||
float col[4] = {0.0f, 0.0f, 0.0f, 1.0f};
|
||||
float depth = 1.0f;
|
||||
uint stencil = 0;
|
||||
|
||||
if (py_col && py_col != Py_None) {
|
||||
if (mathutils_array_parse(
|
||||
col, 3, 4, py_col, "gpu::FrameBuffer.clear(), invalid 'color' arg") == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_COLOR_BIT;
|
||||
}
|
||||
|
||||
if (py_depth && py_depth != Py_None) {
|
||||
depth = PyFloat_AsDouble(py_depth);
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_DEPTH_BIT;
|
||||
}
|
||||
|
||||
if (py_stencil && py_stencil != Py_None) {
|
||||
if ((stencil = PyC_Long_AsU32(py_stencil)) == uint(-1)) {
|
||||
return nullptr;
|
||||
}
|
||||
buffers |= GPU_STENCIL_BIT;
|
||||
}
|
||||
|
||||
GPU_framebuffer_clear(self->fb, buffers, double4(UNPACK4(col)), depth, stencil);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_viewport_set_doc,
|
||||
".. method:: viewport_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Set the viewport for this framebuffer object.\n"
|
||||
" Note: The viewport state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of the viewport rectangle, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of the viewport rectangle, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the viewport.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the viewport.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_framebuffer_viewport_set(BPyGPUFrameBuffer *self, PyObject *args)
|
||||
{
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_framebuffer_viewport_set(self->fb, x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_viewport_get_doc,
|
||||
".. method:: viewport_get()\n"
|
||||
"\n"
|
||||
" Returns position and dimension to current viewport.\n"
|
||||
"\n"
|
||||
" :return: The viewport as ``(x, y, width, height)``.\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_framebuffer_viewport_get(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int viewport[4];
|
||||
GPU_framebuffer_viewport_get(self->fb, viewport);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(viewport[0]),
|
||||
PyLong_FromLong(viewport[1]),
|
||||
PyLong_FromLong(viewport[2]),
|
||||
PyLong_FromLong(viewport[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_read_color_doc,
|
||||
".. method:: read_color(x, y, xsize, ysize, channels, slot, format, *, data=None)\n"
|
||||
"\n"
|
||||
" Read a block of pixels from the frame buffer.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of a rectangular block of pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of a rectangular block of pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the pixel rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the pixel rectangle.\n"
|
||||
" :type ysize: int\n"
|
||||
" :param channels: Number of components to read.\n"
|
||||
" :type channels: int\n"
|
||||
" :param slot: The framebuffer slot to read data from.\n"
|
||||
" :type slot: int\n"
|
||||
" :param format: The format that describes the content of a single channel.\n"
|
||||
" ``UINT_24_8`` is deprecated, use ``FLOAT`` instead.\n"
|
||||
" :type format: " PYDOC_DATAFORMAT_LITERAL
|
||||
"\n"
|
||||
" :param data: Optional Buffer object to fill with the pixels values.\n"
|
||||
" :type data: :class:`gpu.types.Buffer` | None\n"
|
||||
" :return: The Buffer with the read pixels.\n"
|
||||
" :rtype: :class:`gpu.types.Buffer`\n");
|
||||
static PyObject *pygpu_framebuffer_read_color(BPyGPUFrameBuffer *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int x, y, w, h, channels;
|
||||
uint slot;
|
||||
PyC_StringEnum pygpu_dataformat = {bpygpu_dataformat_items,
|
||||
int(gpu::TextureFormat::UNORM_8_8_8_8)};
|
||||
BPyGPUBuffer *py_buffer = nullptr;
|
||||
PyC_TypeOrNone py_buffer_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPU_BufferType, &py_buffer);
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"x", "y", "xsize", "ysize", "channels", "slot", "format", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `x` */
|
||||
"i" /* `y` */
|
||||
"i" /* `xsize` */
|
||||
"i" /* `ysize` */
|
||||
"i" /* `channels` */
|
||||
"I" /* `slot` */
|
||||
"O&" /* `format` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `data` */
|
||||
":read_color",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&x,
|
||||
&y,
|
||||
&w,
|
||||
&h,
|
||||
&channels,
|
||||
&slot,
|
||||
PyC_ParseStringEnum,
|
||||
&pygpu_dataformat,
|
||||
PyC_ParseTypeOrNone,
|
||||
&py_buffer_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (pygpu_dataformat.value_found == GPU_DATA_UINT_24_8_DEPRECATED) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning, "`UINT_24_8` is deprecated, use `FLOAT` instead", 1);
|
||||
}
|
||||
|
||||
if (!IN_RANGE_INCL(channels, 1, 4)) {
|
||||
PyErr_SetString(PyExc_AttributeError, "Color channels must be 1, 2, 3 or 4");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (slot >= BPYGPU_FB_MAX_COLOR_ATTACHMENT) {
|
||||
PyErr_SetString(PyExc_ValueError, "slot overflow");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int2 extent = GPU_framebuffer_extent_get(self->fb);
|
||||
if (x < 0 || w < 0 || x + w > extent.x || y < 0 || h < 0 || y + h > extent.y) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Trying to read color outside the extent of the framebuffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (py_buffer) {
|
||||
if (pygpu_dataformat.value_found != py_buffer->format) {
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"the format of the buffer is different from that specified");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t size_curr = bpygpu_Buffer_size(py_buffer);
|
||||
size_t size_expected = w * h * channels *
|
||||
GPU_texture_dataformat_size(
|
||||
eGPUDataFormat(pygpu_dataformat.value_found));
|
||||
if (size_curr < size_expected) {
|
||||
PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
|
||||
return nullptr;
|
||||
}
|
||||
Py_INCREF(py_buffer);
|
||||
}
|
||||
else {
|
||||
const Py_ssize_t shape[3] = {h, w, channels};
|
||||
py_buffer = BPyGPU_Buffer_CreatePyObject(pygpu_dataformat.value_found, shape, 3, nullptr);
|
||||
BLI_assert(bpygpu_Buffer_size(py_buffer) ==
|
||||
size_t(w) * size_t(h) * size_t(channels) *
|
||||
GPU_texture_dataformat_size(eGPUDataFormat(pygpu_dataformat.value_found)));
|
||||
}
|
||||
|
||||
GPU_framebuffer_read_color(self->fb,
|
||||
x,
|
||||
y,
|
||||
w,
|
||||
h,
|
||||
channels,
|
||||
int(slot),
|
||||
eGPUDataFormat(pygpu_dataformat.value_found),
|
||||
py_buffer->buf.as_void);
|
||||
|
||||
return reinterpret_cast<PyObject *>(py_buffer);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_read_depth_doc,
|
||||
".. method:: read_depth(x, y, xsize, ysize, *, data=None)\n"
|
||||
"\n"
|
||||
" Read a pixel depth block from the frame buffer.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x of a rectangular block of pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y of a rectangular block of pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the pixel rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the pixel rectangle.\n"
|
||||
" :type ysize: int\n"
|
||||
" :param data: Optional Buffer object to fill with the pixels values.\n"
|
||||
" :type data: :class:`gpu.types.Buffer` | None\n"
|
||||
" :return: The Buffer with the read pixels.\n"
|
||||
" :rtype: :class:`gpu.types.Buffer`\n");
|
||||
static PyObject *pygpu_framebuffer_read_depth(BPyGPUFrameBuffer *self,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
int x, y, w, h;
|
||||
BPyGPUBuffer *py_buffer = nullptr;
|
||||
PyC_TypeOrNone py_buffer_or_none = PyC_TYPE_OR_NONE_INIT(&BPyGPU_BufferType, &py_buffer);
|
||||
|
||||
static const char *_keywords[] = {"x", "y", "xsize", "ysize", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `x` */
|
||||
"i" /* `y` */
|
||||
"i" /* `xsize` */
|
||||
"i" /* `ysize` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `data` */
|
||||
":read_depth",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &x, &y, &w, &h, PyC_ParseTypeOrNone, &py_buffer_or_none))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int2 extent = GPU_framebuffer_extent_get(self->fb);
|
||||
if (x < 0 || w < 0 || x + w > extent.x || y < 0 || h < 0 || y + h > extent.y) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"Trying to read depth outside the extent of the framebuffer");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (py_buffer) {
|
||||
if (py_buffer->format != GPU_DATA_FLOAT) {
|
||||
PyErr_SetString(PyExc_AttributeError, "the format of the buffer must be 'GPU_DATA_FLOAT'");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
size_t size_curr = bpygpu_Buffer_size(py_buffer);
|
||||
size_t size_expected = w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT);
|
||||
if (size_curr < size_expected) {
|
||||
PyErr_SetString(PyExc_BufferError, "the buffer size is smaller than expected");
|
||||
return nullptr;
|
||||
}
|
||||
Py_INCREF(py_buffer);
|
||||
}
|
||||
else {
|
||||
const Py_ssize_t shape[2] = {h, w};
|
||||
py_buffer = BPyGPU_Buffer_CreatePyObject(GPU_DATA_FLOAT, shape, 2, nullptr);
|
||||
BLI_assert(bpygpu_Buffer_size(py_buffer) ==
|
||||
w * h * GPU_texture_dataformat_size(GPU_DATA_FLOAT));
|
||||
}
|
||||
|
||||
GPU_framebuffer_read_depth(self->fb, x, y, w, h, GPU_DATA_FLOAT, py_buffer->buf.as_void);
|
||||
|
||||
return reinterpret_cast<PyObject *>(py_buffer);
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer_free_doc,
|
||||
".. method:: free()\n"
|
||||
"\n"
|
||||
" Free the framebuffer object.\n"
|
||||
" The framebuffer will no longer be accessible.\n");
|
||||
static PyObject *pygpu_framebuffer_free(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
PYGPU_FRAMEBUFFER_CHECK_OBJ(self);
|
||||
pygpu_framebuffer_free_safe(self);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUFrameBuffer__tp_dealloc(BPyGPUFrameBuffer *self)
|
||||
{
|
||||
pygpu_framebuffer_free_safe(self);
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_framebuffer__tp_getseters[] = {
|
||||
{"is_bound",
|
||||
reinterpret_cast<getter>(pygpu_framebuffer_is_bound),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_framebuffer_is_bound_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_framebuffer__tp_methods[] = {
|
||||
{"bind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_bind),
|
||||
METH_NOARGS,
|
||||
pygpu_framebuffer_bind_doc},
|
||||
{"clear",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_clear),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_clear_doc},
|
||||
{"viewport_set",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_viewport_set),
|
||||
METH_VARARGS,
|
||||
pygpu_framebuffer_viewport_set_doc},
|
||||
{"viewport_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_viewport_get),
|
||||
METH_NOARGS,
|
||||
pygpu_framebuffer_viewport_get_doc},
|
||||
{"read_color",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_read_color),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_read_color_doc},
|
||||
{"read_depth",
|
||||
reinterpret_cast<PyCFunction>(pygpu_framebuffer_read_depth),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_framebuffer_read_depth_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free", (PyCFunction)pygpu_framebuffer_free, METH_NOARGS, pygpu_framebuffer_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* Ideally type aliases would de-duplicate:
|
||||
* `GPUTexture | dict[str, int | GPUTexture]` in this docstring. */
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_framebuffer__tp_doc,
|
||||
".. class:: GPUFrameBuffer\n"
|
||||
"\n"
|
||||
" This object gives access to framebuffer functionalities.\n"
|
||||
" When a 'layer' is specified in a argument, a single layer of a 3D or array "
|
||||
"texture is attached to the frame-buffer.\n"
|
||||
" For cube map textures, layer is translated into a cube map face.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(*, depth_slot=None, color_slots=None)\n"
|
||||
"\n"
|
||||
" :param depth_slot: GPUTexture to attach or a ``dict`` containing keywords: "
|
||||
"'texture', 'layer' and 'mip'.\n"
|
||||
" :type depth_slot: :class:`gpu.types.GPUTexture` | dict[str, int | "
|
||||
":class:`gpu.types.GPUTexture`] | None\n"
|
||||
" :param color_slots: Tuple where each item can be a GPUTexture or a ``dict`` "
|
||||
"containing keywords: 'texture', 'layer' and 'mip'.\n"
|
||||
" :type color_slots: :class:`gpu.types.GPUTexture` | "
|
||||
"dict[str, int | :class:`gpu.types.GPUTexture`] | "
|
||||
"Sequence[:class:`gpu.types.GPUTexture` | dict[str, int | "
|
||||
":class:`gpu.types.GPUTexture`]] | "
|
||||
"None\n");
|
||||
PyTypeObject BPyGPUFrameBuffer_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUFrameBuffer",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUFrameBuffer),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUFrameBuffer__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_framebuffer__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_framebuffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_framebuffer__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_framebuffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUFrameBuffer_CreatePyObject(gpu::FrameBuffer *fb, bool shared_reference)
|
||||
{
|
||||
BPyGPUFrameBuffer *self;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
if (shared_reference) {
|
||||
void **ref = GPU_framebuffer_py_reference_get(fb);
|
||||
if (ref) {
|
||||
/* Retrieve BPyGPUFrameBuffer reference. */
|
||||
self = reinterpret_cast<BPyGPUFrameBuffer *> POINTER_OFFSET(
|
||||
ref, -offsetof(BPyGPUFrameBuffer, fb));
|
||||
BLI_assert(self->fb == fb);
|
||||
Py_INCREF(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
}
|
||||
#else
|
||||
UNUSED_VARS(shared_reference);
|
||||
#endif
|
||||
|
||||
self = PyObject_New(BPyGPUFrameBuffer, &BPyGPUFrameBuffer_Type);
|
||||
self->fb = fb;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
self->shared_reference = shared_reference;
|
||||
|
||||
BLI_assert(GPU_framebuffer_py_reference_get(fb) == nullptr);
|
||||
GPU_framebuffer_py_reference_set(fb, reinterpret_cast<void **>(&self->fb));
|
||||
#endif
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef PYGPU_FRAMEBUFFER_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,37 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class FrameBuffer;
|
||||
} // namespace gpu
|
||||
|
||||
extern PyTypeObject BPyGPUFrameBuffer_Type;
|
||||
|
||||
#define BPyGPUFrameBuffer_Check(v) (Py_TYPE(v) == &BPyGPUFrameBuffer_Type)
|
||||
|
||||
struct BPyGPUFrameBuffer {
|
||||
PyObject_HEAD
|
||||
gpu::FrameBuffer *fb;
|
||||
|
||||
#ifndef GPU_NO_USE_PY_REFERENCES
|
||||
bool shared_reference;
|
||||
#endif
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUFrameBuffer_CreatePyObject(gpu::FrameBuffer *fb,
|
||||
bool shared_reference) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
732
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.cc
Normal file
732
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.cc
Normal file
@@ -0,0 +1,732 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.matrix stack API.
|
||||
*
|
||||
* \warning While these functions attempt to ensure correct stack usage.
|
||||
* Mixing Python and C functions may still crash on invalid use.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#define USE_GPU_PY_MATRIX_API
|
||||
#include "GPU_matrix.hh"
|
||||
#undef USE_GPU_PY_MATRIX_API
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_matrix.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Helper Functions
|
||||
* \{ */
|
||||
|
||||
static bool pygpu_stack_is_push_model_view_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_model_view() >= GPU_PY_MATRIX_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum model-view stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_push_projection_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_projection() >= GPU_PY_MATRIX_STACK_LEN) {
|
||||
PyErr_SetString(
|
||||
PyExc_RuntimeError,
|
||||
"Maximum projection stack depth " STRINGIFY(GPU_PY_MATRIX_STACK_DEPTH) " reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_pop_model_view_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_model_view() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum model-view stack depth reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool pygpu_stack_is_pop_projection_ok_or_error()
|
||||
{
|
||||
if (GPU_matrix_stack_level_get_projection() == 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Minimum projection stack depth reached");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manage Stack
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_doc,
|
||||
".. function:: push()\n"
|
||||
"\n"
|
||||
" Add to the model-view matrix stack.\n");
|
||||
static PyObject *pygpu_matrix_push(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_pop_doc,
|
||||
".. function:: pop()\n"
|
||||
"\n"
|
||||
" Remove the last model-view matrix from the stack.\n");
|
||||
static PyObject *pygpu_matrix_pop(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_pop_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_pop();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_projection_doc,
|
||||
".. function:: push_projection()\n"
|
||||
"\n"
|
||||
" Add to the projection matrix stack.\n");
|
||||
static PyObject *pygpu_matrix_push_projection(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_push_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push_projection();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_pop_projection_doc,
|
||||
".. function:: pop_projection()\n"
|
||||
"\n"
|
||||
" Remove the last projection matrix from the stack.\n");
|
||||
static PyObject *pygpu_matrix_pop_projection(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (!pygpu_stack_is_pop_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_pop_projection();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct BPyGPU_MatrixStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
int type;
|
||||
int level;
|
||||
};
|
||||
|
||||
enum {
|
||||
PYGPU_MATRIX_TYPE_MODEL_VIEW = 1,
|
||||
PYGPU_MATRIX_TYPE_PROJECTION = 2,
|
||||
};
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self);
|
||||
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self, PyObject *args);
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_matrix_stack_context__tp_methods[] = {
|
||||
{"__enter__", reinterpret_cast<PyCFunction>(pygpu_matrix_stack_context_enter), METH_NOARGS},
|
||||
{"__exit__", reinterpret_cast<PyCFunction>(pygpu_matrix_stack_context_exit), METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_stack_context__tp_doc,
|
||||
"Context manager for matrix stack push/pop.");
|
||||
|
||||
PyTypeObject PyGPUMatrixStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "MatrixStackContext",
|
||||
/*tp_basicsize*/ sizeof(BPyGPU_MatrixStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_matrix_stack_context__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_matrix_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_enter(BPyGPU_MatrixStackContext *self)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
|
||||
if (!pygpu_stack_is_push_model_view_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push();
|
||||
self->level = GPU_matrix_stack_level_get_model_view();
|
||||
}
|
||||
else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
|
||||
if (!pygpu_stack_is_push_projection_ok_or_error()) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_push_projection();
|
||||
self->level = GPU_matrix_stack_level_get_projection();
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_matrix_stack_context_exit(BPyGPU_MatrixStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
/* sanity - should never happen */
|
||||
if (self->level == -1) {
|
||||
fprintf(stderr, "Not yet in use\n");
|
||||
goto finally;
|
||||
}
|
||||
|
||||
if (self->type == PYGPU_MATRIX_TYPE_MODEL_VIEW) {
|
||||
const int level = GPU_matrix_stack_level_get_model_view();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level push/pop mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
if (level != 0) {
|
||||
GPU_matrix_pop();
|
||||
}
|
||||
}
|
||||
else if (self->type == PYGPU_MATRIX_TYPE_PROJECTION) {
|
||||
const int level = GPU_matrix_stack_level_get_projection();
|
||||
if (level != self->level) {
|
||||
fprintf(stderr, "Level push/pop mismatch, expected %d, got %d", self->level, level);
|
||||
}
|
||||
if (level != 0) {
|
||||
GPU_matrix_pop_projection();
|
||||
}
|
||||
}
|
||||
else {
|
||||
BLI_assert_unreachable();
|
||||
}
|
||||
finally:
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_matrix_push_pop_impl(int type)
|
||||
{
|
||||
BPyGPU_MatrixStackContext *ret = PyObject_New(BPyGPU_MatrixStackContext,
|
||||
&PyGPUMatrixStackContext_Type);
|
||||
ret->type = type;
|
||||
ret->level = -1;
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_pop_doc,
|
||||
".. function:: push_pop()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: The context manager.\n"
|
||||
" :rtype: :class:`gpu.types.MatrixStackContext`\n");
|
||||
static PyObject *pygpu_matrix_push_pop(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_MODEL_VIEW);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_push_pop_projection_doc,
|
||||
".. function:: push_pop_projection()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced push/pop calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: The context manager.\n"
|
||||
" :rtype: :class:`gpu.types.MatrixStackContext`\n");
|
||||
static PyObject *pygpu_matrix_push_pop_projection(PyObject * /*self*/)
|
||||
{
|
||||
return pygpu_matrix_push_pop_impl(PYGPU_MATRIX_TYPE_PROJECTION);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manipulate State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_multiply_matrix_doc,
|
||||
".. function:: multiply_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Multiply the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_multiply_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_mul(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_scale_doc,
|
||||
".. function:: scale(scale)\n"
|
||||
"\n"
|
||||
" Scale the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param scale: Scale the current stack matrix with 2 or 3 floats.\n"
|
||||
" :type scale: Sequence[float]\n");
|
||||
static PyObject *pygpu_matrix_scale(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float scale[3];
|
||||
int len;
|
||||
if ((len = mathutils_array_parse(
|
||||
scale, 2, 3, value, "gpu.matrix.scale(): invalid vector arg")) == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (len == 2) {
|
||||
GPU_matrix_scale_2fv(scale);
|
||||
}
|
||||
else {
|
||||
GPU_matrix_scale_3fv(scale);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_scale_uniform_doc,
|
||||
".. function:: scale_uniform(scale)\n"
|
||||
"\n"
|
||||
" Scale the current stack matrix uniformly.\n"
|
||||
"\n"
|
||||
" :param scale: Uniform scale factor.\n"
|
||||
" :type scale: float\n");
|
||||
static PyObject *pygpu_matrix_scale_uniform(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float scalar;
|
||||
if ((scalar = PyFloat_AsDouble(value)) == -1.0f && PyErr_Occurred()) {
|
||||
PyErr_Format(PyExc_TypeError, "expected a number, not %.200s", Py_TYPE(value)->tp_name);
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_scale_1f(scalar);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_translate_doc,
|
||||
".. function:: translate(offset)\n"
|
||||
"\n"
|
||||
" Translate the current stack matrix.\n"
|
||||
"\n"
|
||||
" :param offset: Translate the current stack matrix with 2 or 3 floats.\n"
|
||||
" :type offset: Sequence[float]\n");
|
||||
static PyObject *pygpu_matrix_translate(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
float offset[3];
|
||||
int len;
|
||||
if ((len = mathutils_array_parse(
|
||||
offset, 2, 3, value, "gpu.matrix.translate(): invalid vector arg")) == -1)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if (len == 2) {
|
||||
GPU_matrix_translate_2fv(offset);
|
||||
}
|
||||
else {
|
||||
GPU_matrix_translate_3fv(offset);
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Write State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_reset_doc,
|
||||
".. function:: reset()\n"
|
||||
"\n"
|
||||
" Empty stack and set to identity.\n");
|
||||
static PyObject *pygpu_matrix_reset(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPU_matrix_reset();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_identity_doc,
|
||||
".. function:: load_identity()\n"
|
||||
"\n"
|
||||
" Load an identity matrix into the stack.\n");
|
||||
static PyObject *pygpu_matrix_load_identity(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPU_matrix_identity_set();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_matrix_doc,
|
||||
".. function:: load_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Load a matrix into the stack.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_load_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_set(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_load_projection_matrix_doc,
|
||||
".. function:: load_projection_matrix(matrix)\n"
|
||||
"\n"
|
||||
" Load a projection matrix into the stack.\n"
|
||||
"\n"
|
||||
" :param matrix: A 4x4 matrix.\n"
|
||||
" :type matrix: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_load_projection_matrix(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
MatrixObject *pymat;
|
||||
if (!Matrix_Parse4x4(value, &pymat)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_matrix_projection_set(pymat->matrix);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Read State
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_projection_matrix_doc,
|
||||
".. function:: get_projection_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the projection matrix.\n"
|
||||
"\n"
|
||||
" :return: A 4x4 projection matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_projection_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[4][4];
|
||||
GPU_matrix_projection_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_model_view_matrix_doc,
|
||||
".. function:: get_model_view_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the model-view matrix.\n"
|
||||
"\n"
|
||||
" :return: A 4x4 view matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_model_view_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[4][4];
|
||||
GPU_matrix_model_view_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 4, 4, nullptr);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix_get_normal_matrix_doc,
|
||||
".. function:: get_normal_matrix()\n"
|
||||
"\n"
|
||||
" Return a copy of the normal matrix.\n"
|
||||
"\n"
|
||||
" :return: A 3x3 normal matrix.\n"
|
||||
" :rtype: :class:`mathutils.Matrix`\n");
|
||||
static PyObject *pygpu_matrix_get_normal_matrix(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float matrix[3][3];
|
||||
GPU_matrix_normal_get(matrix);
|
||||
return Matrix_CreatePyObject(&matrix[0][0], 3, 3, nullptr);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_matrix__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"push", reinterpret_cast<PyCFunction>(pygpu_matrix_push), METH_NOARGS, pygpu_matrix_push_doc},
|
||||
{"pop", reinterpret_cast<PyCFunction>(pygpu_matrix_pop), METH_NOARGS, pygpu_matrix_pop_doc},
|
||||
|
||||
{"push_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_projection_doc},
|
||||
{"pop_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_pop_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_pop_projection_doc},
|
||||
|
||||
/* Stack (Context Manager) */
|
||||
{"push_pop",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_pop),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_pop_doc},
|
||||
{"push_pop_projection",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_push_pop_projection),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_push_pop_projection_doc},
|
||||
|
||||
/* Manipulate State */
|
||||
{"multiply_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_multiply_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_multiply_matrix_doc},
|
||||
{"scale", static_cast<PyCFunction>(pygpu_matrix_scale), METH_O, pygpu_matrix_scale_doc},
|
||||
{"scale_uniform",
|
||||
static_cast<PyCFunction>(pygpu_matrix_scale_uniform),
|
||||
METH_O,
|
||||
pygpu_matrix_scale_uniform_doc},
|
||||
{"translate",
|
||||
static_cast<PyCFunction>(pygpu_matrix_translate),
|
||||
METH_O,
|
||||
pygpu_matrix_translate_doc},
|
||||
|
||||
/* TODO */
|
||||
#if 0
|
||||
{"rotate", (PyCFunction)pygpu_matrix_rotate, METH_O, pygpu_matrix_rotate_doc},
|
||||
{"rotate_axis", (PyCFunction)pygpu_matrix_rotate_axis, METH_O, pygpu_matrix_rotate_axis_doc},
|
||||
{"look_at", (PyCFunction)pygpu_matrix_look_at, METH_O, pygpu_matrix_look_at_doc},
|
||||
#endif
|
||||
|
||||
/* Write State */
|
||||
{"reset",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_reset),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_reset_doc},
|
||||
{"load_identity",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_load_identity),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_load_identity_doc},
|
||||
{"load_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_load_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_load_matrix_doc},
|
||||
{"load_projection_matrix",
|
||||
static_cast<PyCFunction>(pygpu_matrix_load_projection_matrix),
|
||||
METH_O,
|
||||
pygpu_matrix_load_projection_matrix_doc},
|
||||
|
||||
/* Read State */
|
||||
{"get_projection_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_projection_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_projection_matrix_doc},
|
||||
{"get_model_view_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_model_view_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_model_view_matrix_doc},
|
||||
{"get_normal_matrix",
|
||||
reinterpret_cast<PyCFunction>(pygpu_matrix_get_normal_matrix),
|
||||
METH_NOARGS,
|
||||
pygpu_matrix_get_normal_matrix_doc},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_matrix__tp_doc,
|
||||
"This module provides access to the matrix stack.");
|
||||
static PyModuleDef pygpu_matrix_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.matrix",
|
||||
/*m_doc*/ pygpu_matrix__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_matrix__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_matrix_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_matrix_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
19
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.hh
Normal file
19
blender-5.2.0/source/blender/python/gpu/gpu_py_matrix.hh
Normal file
@@ -0,0 +1,19 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject PyGPUMatrixStackContext_Type;
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_matrix_init();
|
||||
|
||||
} // namespace blender
|
||||
689
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.cc
Normal file
689
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.cc
Normal file
@@ -0,0 +1,689 @@
|
||||
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the offscreen functionalities of the 'gpu' module
|
||||
* used for off-screen OpenGL rendering.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_lib_id.hh" /* For #BKE_id_is_in_global_main. */
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DNA_view3d_types.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_state.hh"
|
||||
#include "GPU_texture.hh"
|
||||
#include "GPU_viewport.hh"
|
||||
|
||||
#include "ED_view3d_offscreen.hh"
|
||||
|
||||
#include "../mathutils/mathutils.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_texture.hh"
|
||||
|
||||
#include "gpu_py_offscreen.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Define the free method to avoid breakage. */
|
||||
#define BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUOffScreen Common Utilities
|
||||
* \{ */
|
||||
|
||||
static const PyC_StringEnumItems pygpu_framebuffer_color_texture_formats[] = {
|
||||
{int(gpu::TextureFormat::UNORM_8_8_8_8), "RGBA8"},
|
||||
{int(gpu::TextureFormat::UNORM_16_16_16_16), "RGBA16"},
|
||||
{int(gpu::TextureFormat::SFLOAT_16_16_16_16), "RGBA16F"},
|
||||
{int(gpu::TextureFormat::SFLOAT_32_32_32_32), "RGBA32F"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static int pygpu_offscreen_valid_check(BPyGPUOffScreen *py_ofs)
|
||||
{
|
||||
if (UNLIKELY(py_ofs->ofs == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError,
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
"GPU offscreen was freed, no further access is valid"
|
||||
#else
|
||||
"GPU offscreen: internal error"
|
||||
#endif
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BPY_GPU_OFFSCREEN_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_offscreen_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Stack (Context Manager)
|
||||
*
|
||||
* Safer alternative to ensure balanced push/pop calls.
|
||||
*
|
||||
* \{ */
|
||||
|
||||
struct OffScreenStackContext {
|
||||
PyObject_HEAD /* Required Python macro. */
|
||||
BPyGPUOffScreen *py_offscreen;
|
||||
int level;
|
||||
bool is_explicitly_bound; /* Bound by "bind" method. */
|
||||
};
|
||||
|
||||
static void pygpu_offscreen_stack_context__tp_dealloc(OffScreenStackContext *self)
|
||||
{
|
||||
Py_DECREF(self->py_offscreen);
|
||||
PyObject_DEL(self);
|
||||
}
|
||||
|
||||
static PyObject *pygpu_offscreen_stack_context_enter(OffScreenStackContext *self)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
|
||||
|
||||
if (!self->is_explicitly_bound) {
|
||||
if (self->level != -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Already in use");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_bind(self->py_offscreen->ofs, true);
|
||||
self->level = GPU_framebuffer_stack_level_get();
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject *pygpu_offscreen_stack_context_exit(OffScreenStackContext *self,
|
||||
PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self->py_offscreen);
|
||||
|
||||
if (self->level == -1) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "Not yet in use\n");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int level = GPU_framebuffer_stack_level_get();
|
||||
if (level != self->level) {
|
||||
PyErr_Format(
|
||||
PyExc_RuntimeError, "Level of bind mismatch, expected %d, got %d\n", self->level, level);
|
||||
}
|
||||
|
||||
GPU_offscreen_unbind(self->py_offscreen->ofs, true);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_offscreen_stack_context__tp_methods[] = {
|
||||
{"__enter__", reinterpret_cast<PyCFunction>(pygpu_offscreen_stack_context_enter), METH_NOARGS},
|
||||
{"__exit__", reinterpret_cast<PyCFunction>(pygpu_offscreen_stack_context_exit), METH_VARARGS},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_stack_context__tp_doc,
|
||||
"Context manager for off-screen framebuffer binding.");
|
||||
PyTypeObject PyGPUOffscreenStackContext_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "OffScreenStackContext",
|
||||
/*tp_basicsize*/ sizeof(OffScreenStackContext),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_offscreen_stack_context__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_offscreen_stack_context__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_offscreen_stack_context__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_bind_doc,
|
||||
".. method:: bind()\n"
|
||||
"\n"
|
||||
" Context manager to ensure balanced bind calls, even in the case of an error.\n"
|
||||
"\n"
|
||||
" :return: A context manager for the off-screen binding.\n"
|
||||
" :rtype: :class:`gpu.types.OffScreenStackContext`\n");
|
||||
static PyObject *pygpu_offscreen_bind(BPyGPUOffScreen *self)
|
||||
{
|
||||
OffScreenStackContext *ret = PyObject_New(OffScreenStackContext,
|
||||
&PyGPUOffscreenStackContext_Type);
|
||||
ret->py_offscreen = self;
|
||||
ret->level = -1;
|
||||
ret->is_explicitly_bound = false;
|
||||
Py_INCREF(self);
|
||||
|
||||
pygpu_offscreen_stack_context_enter(ret);
|
||||
ret->is_explicitly_bound = true;
|
||||
|
||||
return reinterpret_cast<PyObject *>(ret);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_unbind_doc,
|
||||
".. method:: unbind(*, restore=True)\n"
|
||||
"\n"
|
||||
" Unbind the offscreen object.\n"
|
||||
"\n"
|
||||
" :param restore: Restore the OpenGL state, can only be used when the state has been "
|
||||
"saved before.\n"
|
||||
" :type restore: bool\n");
|
||||
static PyObject *pygpu_offscreen_unbind(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
bool restore = true;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
static const char *_keywords[] = {"restore", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `restore` */
|
||||
":unbind",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &restore)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_unbind(self->ofs, restore);
|
||||
GPU_apply_state();
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPUOffscreen Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_offscreen__tp_new(PyTypeObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUOffScreen *ofs = nullptr;
|
||||
int width, height;
|
||||
PyC_StringEnum pygpu_textureformat = {pygpu_framebuffer_color_texture_formats,
|
||||
int(gpu::TextureFormat::UNORM_8_8_8_8)};
|
||||
char err_out[256];
|
||||
|
||||
static const char *_keywords[] = {"width", "height", "format", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `width` */
|
||||
"i" /* `height` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `format` */
|
||||
":GPUOffScreen.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &width, &height, PyC_ParseStringEnum, &pygpu_textureformat))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (GPU_context_active_get()) {
|
||||
ofs = GPU_offscreen_create(width,
|
||||
height,
|
||||
true,
|
||||
gpu::TextureFormat(pygpu_textureformat.value_found),
|
||||
GPU_TEXTURE_USAGE_SHADER_READ | GPU_TEXTURE_USAGE_HOST_READ,
|
||||
false,
|
||||
err_out);
|
||||
}
|
||||
else {
|
||||
STRNCPY_UTF8(err_out, "No active GPU context found");
|
||||
}
|
||||
|
||||
if (ofs == nullptr) {
|
||||
PyErr_Format(PyExc_RuntimeError,
|
||||
"gpu.offscreen.new(...) failed with '%s'",
|
||||
err_out[0] ? err_out : "unknown error");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUOffScreen_CreatePyObject(ofs);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_width_doc,
|
||||
"Width of the texture.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_offscreen_width_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
return PyLong_FromLong(GPU_offscreen_width(self->ofs));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_height_doc,
|
||||
"Height of the texture.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_offscreen_height_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
return PyLong_FromLong(GPU_offscreen_height(self->ofs));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_texture_color_doc,
|
||||
"The color texture attached.\n"
|
||||
"\n"
|
||||
":type: :class:`gpu.types.GPUTexture`\n");
|
||||
static PyObject *pygpu_offscreen_texture_color_get(BPyGPUOffScreen *self, void * /*type*/)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
gpu::Texture *texture = GPU_offscreen_color_texture(self->ofs);
|
||||
return BPyGPUTexture_CreatePyObject(texture, true);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_draw_view3d_doc,
|
||||
".. method:: draw_view3d(scene, view_layer, view3d, region, view_matrix, projection_matrix, "
|
||||
"*, do_color_management=False, draw_background=True)\n"
|
||||
"\n"
|
||||
" Draw the 3d viewport in the offscreen object.\n"
|
||||
"\n"
|
||||
" :param scene: Scene to draw.\n"
|
||||
" :type scene: :class:`bpy.types.Scene`\n"
|
||||
" :param view_layer: View layer to draw.\n"
|
||||
" :type view_layer: :class:`bpy.types.ViewLayer`\n"
|
||||
" :param view3d: 3D View to get the drawing settings from.\n"
|
||||
" :type view3d: :class:`bpy.types.SpaceView3D`\n"
|
||||
" :param region: Region of the 3D View (required as temporary draw target).\n"
|
||||
" :type region: :class:`bpy.types.Region`\n"
|
||||
" :param view_matrix: View Matrix (e.g. ``camera.matrix_world.inverted()``).\n"
|
||||
" :type view_matrix: :class:`mathutils.Matrix`\n"
|
||||
" :param projection_matrix: Projection Matrix (e.g. ``camera.calc_matrix_camera(...)``).\n"
|
||||
" :type projection_matrix: :class:`mathutils.Matrix`\n"
|
||||
" :param do_color_management: Color manage the output.\n"
|
||||
" :type do_color_management: bool\n"
|
||||
" :param draw_background: Draw background.\n"
|
||||
" :type draw_background: bool\n");
|
||||
static PyObject *pygpu_offscreen_draw_view3d(BPyGPUOffScreen *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
MatrixObject *py_mat_view, *py_mat_projection;
|
||||
PyObject *py_scene, *py_view_layer, *py_region, *py_view3d;
|
||||
|
||||
Depsgraph *depsgraph;
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
View3D *v3d;
|
||||
ARegion *region;
|
||||
|
||||
bool do_color_management = false;
|
||||
bool draw_background = true;
|
||||
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
static const char *_keywords[] = {
|
||||
"scene",
|
||||
"view_layer",
|
||||
"view3d",
|
||||
"region",
|
||||
"view_matrix",
|
||||
"projection_matrix",
|
||||
"do_color_management",
|
||||
"draw_background",
|
||||
nullptr,
|
||||
};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `scene` */
|
||||
"O" /* `view_layer` */
|
||||
"O" /* `view3d` */
|
||||
"O" /* `region` */
|
||||
"O&" /* `view_matrix` */
|
||||
"O&" /* `projection_matrix` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `do_color_management` */
|
||||
"O&" /* `draw_background` */
|
||||
":draw_view3d",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&py_scene,
|
||||
&py_view_layer,
|
||||
&py_view3d,
|
||||
&py_region,
|
||||
Matrix_Parse4x4,
|
||||
&py_mat_view,
|
||||
Matrix_Parse4x4,
|
||||
&py_mat_projection,
|
||||
PyC_ParseBool,
|
||||
&do_color_management,
|
||||
PyC_ParseBool,
|
||||
&draw_background) ||
|
||||
(!(scene = static_cast<Scene *>(PyC_RNA_AsPointer(py_scene, "Scene"))) ||
|
||||
!(view_layer = static_cast<ViewLayer *>(PyC_RNA_AsPointer(py_view_layer, "ViewLayer"))) ||
|
||||
!(v3d = static_cast<View3D *>(PyC_RNA_AsPointer(py_view3d, "SpaceView3D"))) ||
|
||||
!(region = static_cast<ARegion *>(PyC_RNA_AsPointer(py_region, "Region")))))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (ED_view3d_draw_offscreen_check_nested()) {
|
||||
/* NOTE(@ideasman42): Nested draw calls could be supported.
|
||||
* Adding support for this looks to be possible but non-trivial. */
|
||||
PyErr_SetString(PyExc_RuntimeError, "Nested off-screen drawing not supported");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BLI_assert(BKE_id_is_in_global_main(&scene->id));
|
||||
|
||||
depsgraph = BKE_scene_ensure_depsgraph(G_MAIN, scene, view_layer);
|
||||
|
||||
GPU_offscreen_bind(self->ofs, true);
|
||||
|
||||
/* Cache the #GPUViewport so the frame-buffers and associated textures are
|
||||
* not reallocated each time, see: #89204 */
|
||||
if (!self->viewport) {
|
||||
self->viewport = GPU_viewport_create();
|
||||
}
|
||||
else {
|
||||
GPU_viewport_tag_update(self->viewport);
|
||||
}
|
||||
|
||||
ED_view3d_draw_offscreen(depsgraph,
|
||||
scene,
|
||||
eDrawType(v3d->shading.type),
|
||||
v3d,
|
||||
region,
|
||||
nullptr,
|
||||
GPU_offscreen_width(self->ofs),
|
||||
GPU_offscreen_height(self->ofs),
|
||||
reinterpret_cast<const float (*)[4]>(py_mat_view->matrix),
|
||||
reinterpret_cast<const float (*)[4]>(py_mat_projection->matrix),
|
||||
true,
|
||||
draw_background,
|
||||
"",
|
||||
do_color_management,
|
||||
true,
|
||||
self->ofs,
|
||||
self->viewport);
|
||||
|
||||
GPU_offscreen_unbind(self->ofs, true);
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen_free_doc,
|
||||
".. method:: free()\n"
|
||||
"\n"
|
||||
" Free the offscreen object.\n"
|
||||
" The framebuffer, texture and render objects will no longer be accessible.\n");
|
||||
static PyObject *pygpu_offscreen_free(BPyGPUOffScreen *self)
|
||||
{
|
||||
BPY_GPU_OFFSCREEN_CHECK_OBJ(self);
|
||||
|
||||
if (self->viewport) {
|
||||
GPU_viewport_free(self->viewport);
|
||||
self->viewport = nullptr;
|
||||
}
|
||||
|
||||
GPU_offscreen_free(self->ofs);
|
||||
self->ofs = nullptr;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUOffScreen__tp_dealloc(BPyGPUOffScreen *self)
|
||||
{
|
||||
if (self->viewport) {
|
||||
GPU_viewport_free(self->viewport);
|
||||
}
|
||||
if (self->ofs) {
|
||||
GPU_offscreen_free(self->ofs);
|
||||
}
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_offscreen__tp_getseters[] = {
|
||||
{"texture_color",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_texture_color_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_texture_color_doc,
|
||||
nullptr},
|
||||
{"width",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_width_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_width_doc,
|
||||
nullptr},
|
||||
{"height",
|
||||
reinterpret_cast<getter>(pygpu_offscreen_height_get),
|
||||
static_cast<setter>(nullptr),
|
||||
pygpu_offscreen_height_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_offscreen__tp_methods[] = {
|
||||
{"bind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_bind),
|
||||
METH_NOARGS,
|
||||
pygpu_offscreen_bind_doc},
|
||||
{"unbind",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_unbind),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_offscreen_unbind_doc},
|
||||
{"draw_view3d",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_draw_view3d),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_offscreen_draw_view3d_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free",
|
||||
reinterpret_cast<PyCFunction>(pygpu_offscreen_free),
|
||||
METH_NOARGS,
|
||||
pygpu_offscreen_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_offscreen__tp_doc,
|
||||
".. class:: GPUOffScreen\n"
|
||||
"\n"
|
||||
" This object gives access to off screen buffers.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(width, height, *, format='RGBA8')\n"
|
||||
"\n"
|
||||
" :param width: Horizontal dimension of the buffer.\n"
|
||||
" :type width: int\n"
|
||||
" :param height: Vertical dimension of the buffer.\n"
|
||||
" :type height: int\n"
|
||||
" :param format: Internal data format inside GPU memory for color attachment texture.\n"
|
||||
" :type format: Literal['RGBA8', 'RGBA16', 'RGBA16F', 'RGBA32F']\n");
|
||||
PyTypeObject BPyGPUOffScreen_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUOffScreen",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUOffScreen),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUOffScreen__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_offscreen__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_offscreen__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_offscreen__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_offscreen__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs)
|
||||
{
|
||||
BPyGPUOffScreen *self;
|
||||
|
||||
self = PyObject_New(BPyGPUOffScreen, &BPyGPUOffScreen_Type);
|
||||
self->ofs = ofs;
|
||||
self->viewport = nullptr;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPY_GPU_OFFSCREEN_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
33
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.hh
Normal file
33
blender-5.2.0/source/blender/python/gpu/gpu_py_offscreen.hh
Normal file
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct GPUOffScreen;
|
||||
struct GPUViewport;
|
||||
|
||||
extern PyTypeObject BPyGPUOffScreen_Type;
|
||||
extern PyTypeObject PyGPUOffscreenStackContext_Type;
|
||||
|
||||
#define BPyGPUOffScreen_Check(v) (Py_TYPE(v) == &BPyGPUOffScreen_Type)
|
||||
|
||||
struct BPyGPUOffScreen {
|
||||
PyObject_HEAD
|
||||
GPUOffScreen *ofs;
|
||||
GPUViewport *viewport;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUOffScreen_CreatePyObject(GPUOffScreen *ofs) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
419
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.cc
Normal file
419
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.cc
Normal file
@@ -0,0 +1,419 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_platform.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_platform.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Device Type
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_index_doc,
|
||||
"Device index.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *pygpu_device_index_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyLong_FromLong(self->index);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_identifier_doc,
|
||||
"Device identifier.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *pygpu_device_identifier_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyUnicode_FromString(self->identifier);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(pygpu_device_name_doc,
|
||||
"Device name.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *pygpu_device_name_get(BPyGPUDevice *self, void * /*closure*/)
|
||||
{
|
||||
return PyUnicode_FromString(self->name);
|
||||
}
|
||||
|
||||
/* Property descriptors */
|
||||
static PyGetSetDef pygpu_device_getseters[] = {
|
||||
{"index",
|
||||
reinterpret_cast<getter>(pygpu_device_index_get),
|
||||
nullptr,
|
||||
pygpu_device_index_doc,
|
||||
nullptr},
|
||||
{"identifier",
|
||||
reinterpret_cast<getter>(pygpu_device_identifier_get),
|
||||
nullptr,
|
||||
pygpu_device_identifier_doc,
|
||||
nullptr},
|
||||
{"name",
|
||||
reinterpret_cast<getter>(pygpu_device_name_get),
|
||||
nullptr,
|
||||
pygpu_device_name_doc,
|
||||
nullptr},
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
};
|
||||
|
||||
/* Representation */
|
||||
static PyObject *pygpu_device__tp_repr(BPyGPUDevice *self)
|
||||
{
|
||||
return PyUnicode_FromFormat("<GPUDevice index=%d identifier=\"%s\" name=\"%s\">",
|
||||
self->index,
|
||||
self->identifier,
|
||||
self->name);
|
||||
}
|
||||
|
||||
/** Rich comparison for GPUDevice types, compares by index. */
|
||||
static PyObject *pygpu_device__tp_richcmp(BPyGPUDevice *self, PyObject *other, int op)
|
||||
{
|
||||
if (!Py_IS_TYPE(other, &BPyGPU_DeviceType)) {
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
BPyGPUDevice *other_device = reinterpret_cast<BPyGPUDevice *>(other);
|
||||
switch (op) {
|
||||
case Py_LT:
|
||||
return PyBool_FromLong(self->index < other_device->index);
|
||||
case Py_LE:
|
||||
return PyBool_FromLong(self->index <= other_device->index);
|
||||
case Py_EQ:
|
||||
return PyBool_FromLong(self->index == other_device->index);
|
||||
case Py_NE:
|
||||
return PyBool_FromLong(self->index != other_device->index);
|
||||
case Py_GT:
|
||||
return PyBool_FromLong(self->index > other_device->index);
|
||||
case Py_GE:
|
||||
return PyBool_FromLong(self->index >= other_device->index);
|
||||
}
|
||||
Py_RETURN_NOTIMPLEMENTED;
|
||||
}
|
||||
|
||||
/* Type definition */
|
||||
PyDoc_STRVAR(pygpu_device__tp_doc,
|
||||
".. class:: GPUDevice\n"
|
||||
"\n"
|
||||
" Represents a GPU device.\n"
|
||||
"\n"
|
||||
" :ivar index: Device index.\n"
|
||||
" :type index: int\n"
|
||||
" :ivar identifier: Device identifier.\n"
|
||||
" :type identifier: str\n"
|
||||
" :ivar name: Device name.\n"
|
||||
" :type name: str\n");
|
||||
|
||||
PyTypeObject BPyGPU_DeviceType = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "gpu.platform.GPUDevice",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUDevice),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_compare*/ nullptr,
|
||||
/*tp_repr*/ reinterpret_cast<reprfunc>(pygpu_device__tp_repr),
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_device__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ reinterpret_cast<richcmpfunc>(pygpu_device__tp_richcmp),
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_device_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ nullptr,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
static BPyGPUDevice *pygpu_device_new(int index, const char *identifier, const char *name)
|
||||
{
|
||||
BPyGPUDevice *self = reinterpret_cast<BPyGPUDevice *>(
|
||||
BPyGPU_DeviceType.tp_alloc(&BPyGPU_DeviceType, 0));
|
||||
if (self != nullptr) {
|
||||
self->index = index;
|
||||
self->identifier = identifier;
|
||||
self->name = name;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Functions
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_vendor_get_doc,
|
||||
".. function:: vendor_get()\n"
|
||||
"\n"
|
||||
" Get GPU vendor.\n"
|
||||
"\n"
|
||||
" :return: Vendor name.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_vendor_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_vendor());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_renderer_get_doc,
|
||||
".. function:: renderer_get()\n"
|
||||
"\n"
|
||||
" Get GPU to be used for rendering.\n"
|
||||
"\n"
|
||||
" :return: GPU name.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_renderer_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_renderer());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_version_get_doc,
|
||||
".. function:: version_get()\n"
|
||||
"\n"
|
||||
" Get GPU driver version.\n"
|
||||
"\n"
|
||||
" :return: Driver version.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_version_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyUnicode_FromString(GPU_platform_version());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_device_type_get_doc,
|
||||
".. function:: device_type_get()\n"
|
||||
"\n"
|
||||
" Get GPU device type.\n"
|
||||
"\n"
|
||||
" :return: Device type ('APPLE', 'NVIDIA', 'AMD', 'INTEL', 'SOFTWARE', 'QUALCOMM', "
|
||||
"'UNKNOWN').\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_device_type_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *device;
|
||||
if (GPU_type_matches(GPU_DEVICE_APPLE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "APPLE";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_NVIDIA, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "NVIDIA";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_ATI, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "AMD";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_INTEL | GPU_DEVICE_INTEL_UHD, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "INTEL";
|
||||
}
|
||||
else if (GPU_type_matches(GPU_DEVICE_SOFTWARE, GPU_OS_ANY, GPU_DRIVER_ANY)) {
|
||||
device = "SOFTWARE";
|
||||
}
|
||||
/* Right now we can only detect Qualcomm GPUs on Windows, not other OSes */
|
||||
else if (GPU_type_matches(GPU_DEVICE_QUALCOMM, GPU_OS_WIN, GPU_DRIVER_ANY)) {
|
||||
device = "QUALCOMM";
|
||||
}
|
||||
else {
|
||||
device = "UNKNOWN";
|
||||
}
|
||||
return PyUnicode_FromString(device);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_backend_type_get_doc,
|
||||
".. function:: backend_type_get()\n"
|
||||
"\n"
|
||||
" Get active GPU backend.\n"
|
||||
"\n"
|
||||
" :return: Backend type ('OPENGL', 'VULKAN', 'METAL', 'NONE', 'UNKNOWN').\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_platform_backend_type_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
const char *backend = "UNKNOWN";
|
||||
switch (GPU_backend_get_type()) {
|
||||
case GPU_BACKEND_VULKAN: {
|
||||
backend = "VULKAN";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_METAL: {
|
||||
backend = "METAL";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_NONE: {
|
||||
backend = "NONE";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_OPENGL: {
|
||||
backend = "OPENGL";
|
||||
break;
|
||||
}
|
||||
case GPU_BACKEND_ANY:
|
||||
break;
|
||||
}
|
||||
return PyUnicode_FromString(backend);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform_devices_get_doc,
|
||||
".. function:: devices_get()\n"
|
||||
"\n"
|
||||
" Get all available GPU devices.\n"
|
||||
"\n"
|
||||
" :return: List of :class:`GPUDevice` objects for each device.\n"
|
||||
" :rtype: list\n");
|
||||
static PyObject *pygpu_platform_devices_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
Span<GPUDevice> devices = GPU_platform_devices_list();
|
||||
PyObject *list = PyList_New(devices.size());
|
||||
for (int i = 0; i < devices.size(); i++) {
|
||||
const GPUDevice &dev = devices[i];
|
||||
PyObject *item = reinterpret_cast<PyObject *>(
|
||||
pygpu_device_new(dev.index, dev.identifier.c_str(), dev.name.c_str()));
|
||||
PyList_SET_ITEM(list, i, item);
|
||||
}
|
||||
/* Sort by index (first attribute) for deterministic ordering. */
|
||||
PyList_Sort(list);
|
||||
return list;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_platform__tp_methods[] = {
|
||||
{"vendor_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_vendor_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_vendor_get_doc},
|
||||
{"renderer_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_renderer_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_renderer_get_doc},
|
||||
{"version_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_version_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_version_get_doc},
|
||||
{"device_type_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_device_type_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_device_type_get_doc},
|
||||
{"backend_type_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_backend_type_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_backend_type_get_doc},
|
||||
{"devices_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_platform_devices_get),
|
||||
METH_NOARGS,
|
||||
pygpu_platform_devices_get_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_platform__tp_doc,
|
||||
"This module provides access to GPU Platform definitions.");
|
||||
static PyModuleDef pygpu_platform_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.platform",
|
||||
/*m_doc*/ pygpu_platform__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_platform__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_platform_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_platform_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
27
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.hh
Normal file
27
blender-5.2.0/source/blender/python/gpu/gpu_py_platform.hh
Normal file
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* GPU Device Python object structure */
|
||||
struct BPyGPUDevice {
|
||||
PyObject_HEAD
|
||||
int index;
|
||||
const char *identifier;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
extern PyTypeObject BPyGPU_DeviceType;
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_platform_init();
|
||||
|
||||
} // namespace blender
|
||||
92
blender-5.2.0/source/blender/python/gpu/gpu_py_select.cc
Normal file
92
blender-5.2.0/source/blender/python/gpu/gpu_py_select.cc
Normal file
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.select API.
|
||||
*
|
||||
* \note Currently only used for gizmo selection,
|
||||
* will need to add begin/end and a way to access the hits.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "GPU_select.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_select.hh" /* Own include. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Methods
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_select_load_id_doc,
|
||||
".. function:: load_id(id)\n"
|
||||
"\n"
|
||||
" Set the selection ID.\n"
|
||||
"\n"
|
||||
" :param id: Number (32-bit uint).\n"
|
||||
" :type id: int\n");
|
||||
static PyObject *pygpu_select_load_id(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
uint id;
|
||||
if ((id = PyC_Long_AsU32(value)) == uint(-1)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_select_load_id(id);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
static PyMethodDef pygpu_select__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"load_id", static_cast<PyCFunction>(pygpu_select_load_id), METH_O, pygpu_select_load_id_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_select__tp_doc,
|
||||
"This module provides access to selection.");
|
||||
static PyModuleDef pygpu_select_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.select",
|
||||
/*m_doc*/ pygpu_select__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_select__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_select_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_select_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_select.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_select.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_select_init();
|
||||
|
||||
} // namespace blender
|
||||
1138
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.cc
Normal file
1138
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.cc
Normal file
File diff suppressed because it is too large
Load Diff
79
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.hh
Normal file
79
blender-5.2.0/source/blender/python/gpu/gpu_py_shader.hh
Normal file
@@ -0,0 +1,79 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
struct GPUShaderCreateInfo;
|
||||
struct GPUStageInterfaceInfo;
|
||||
namespace blender {
|
||||
|
||||
#ifndef __cplusplus
|
||||
# include "../generic/py_capi_utils.hh"
|
||||
#endif
|
||||
|
||||
namespace gpu {
|
||||
class Shader;
|
||||
} // namespace gpu
|
||||
|
||||
/* Make sure that there is always a reference count for PyObjects of type String as the strings are
|
||||
* passed by reference in the #GPUStageInterfaceInfo and #GPUShaderCreateInfo APIs. */
|
||||
#define USE_GPU_PY_REFERENCES
|
||||
|
||||
/* `gpu_py_shader.cc` */
|
||||
|
||||
extern PyTypeObject BPyGPUShader_Type;
|
||||
|
||||
#define BPyGPUShader_Check(v) (Py_TYPE(v) == &BPyGPUShader_Type)
|
||||
|
||||
struct BPyGPUShader {
|
||||
PyObject_HEAD
|
||||
gpu::Shader *shader;
|
||||
bool is_builtin;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUShader_CreatePyObject(gpu::Shader *shader, bool is_builtin);
|
||||
[[nodiscard]] PyObject *bpygpu_shader_init();
|
||||
|
||||
/* gpu_py_shader_create_info.cc */
|
||||
|
||||
extern const struct PyC_StringEnumItems pygpu_attrtype_items[];
|
||||
extern PyTypeObject BPyGPUShaderCreateInfo_Type;
|
||||
extern PyTypeObject BPyGPUStageInterfaceInfo_Type;
|
||||
|
||||
#define BPyGPUShaderCreateInfo_Check(v) (Py_TYPE(v) == &BPyGPUShaderCreateInfo_Type)
|
||||
#define BPyGPUStageInterfaceInfo_Check(v) (Py_TYPE(v) == &BPyGPUStageInterfaceInfo_Type)
|
||||
|
||||
struct BPyGPUStageInterfaceInfo {
|
||||
PyObject_HEAD
|
||||
GPUStageInterfaceInfo *interface;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *references;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct BPyGPUShaderCreateInfo {
|
||||
PyObject_HEAD
|
||||
GPUShaderCreateInfo *info;
|
||||
#ifdef USE_GPU_PY_REFERENCES
|
||||
/* Just to keep a user to prevent freeing buffers we're using. */
|
||||
PyObject *vertex_source;
|
||||
PyObject *fragment_source;
|
||||
PyObject *compute_source;
|
||||
PyObject *typedef_source;
|
||||
PyObject *references;
|
||||
#endif
|
||||
size_t constants_total_size;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUStageInterfaceInfo_CreatePyObject(GPUStageInterfaceInfo *interface);
|
||||
[[nodiscard]] PyObject *BPyGPUShaderCreateInfo_CreatePyObject(GPUShaderCreateInfo *info);
|
||||
[[nodiscard]] bool bpygpu_shader_is_polyline(gpu::Shader *shader);
|
||||
|
||||
} // namespace blender
|
||||
1477
blender-5.2.0/source/blender/python/gpu/gpu_py_shader_create_info.cc
Normal file
1477
blender-5.2.0/source/blender/python/gpu/gpu_py_shader_create_info.cc
Normal file
File diff suppressed because it is too large
Load Diff
688
blender-5.2.0/source/blender/python/gpu/gpu_py_state.cc
Normal file
688
blender-5.2.0/source/blender/python/gpu/gpu_py_state.cc
Normal file
@@ -0,0 +1,688 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the gpu.state API.
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_framebuffer.hh"
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_framebuffer.hh"
|
||||
#include "gpu_py_state.hh" /* own include */
|
||||
|
||||
/* Docstring Literal types. */
|
||||
#define PYDOC_BLEND_LITERAL \
|
||||
"Literal['NONE', 'ALPHA', 'ALPHA_PREMULT', 'ADDITIVE', " \
|
||||
"'ADDITIVE_PREMULT', 'MULTIPLY', 'SUBTRACT', 'INVERT']"
|
||||
#define PYDOC_DEPTHTEST_LITERAL \
|
||||
"Literal['NONE', 'ALWAYS', 'LESS', 'LESS_EQUAL', 'EQUAL', 'GREATER', 'GREATER_EQUAL']"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Helper Functions
|
||||
* \{ */
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_blend_items[] = {
|
||||
{GPU_BLEND_NONE, "NONE"},
|
||||
{GPU_BLEND_ALPHA, "ALPHA"},
|
||||
{GPU_BLEND_ALPHA_PREMULT, "ALPHA_PREMULT"},
|
||||
{GPU_BLEND_ADDITIVE, "ADDITIVE"},
|
||||
{GPU_BLEND_ADDITIVE_PREMULT, "ADDITIVE_PREMULT"},
|
||||
{GPU_BLEND_MULTIPLY, "MULTIPLY"},
|
||||
{GPU_BLEND_SUBTRACT, "SUBTRACT"},
|
||||
{GPU_BLEND_INVERT, "INVERT"},
|
||||
/**
|
||||
* These are quite special cases used inside the draw manager.
|
||||
* {GPU_BLEND_OIT, "OIT"},
|
||||
* {GPU_BLEND_BACKGROUND, "BACKGROUND"},
|
||||
* {GPU_BLEND_CUSTOM, "CUSTOM"},
|
||||
*/
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_depthtest_items[] = {
|
||||
{GPU_DEPTH_NONE, "NONE"},
|
||||
{GPU_DEPTH_ALWAYS, "ALWAYS"},
|
||||
{GPU_DEPTH_LESS, "LESS"},
|
||||
{GPU_DEPTH_LESS_EQUAL, "LESS_EQUAL"},
|
||||
{GPU_DEPTH_EQUAL, "EQUAL"},
|
||||
{GPU_DEPTH_GREATER, "GREATER"},
|
||||
{GPU_DEPTH_GREATER_EQUAL, "GREATER_EQUAL"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static const PyC_StringEnumItems pygpu_state_faceculling_items[] = {
|
||||
{GPU_CULL_NONE, "NONE"},
|
||||
{GPU_CULL_FRONT, "FRONT"},
|
||||
{GPU_CULL_BACK, "BACK"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Manage Stack
|
||||
* \{ */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_blend_set_doc,
|
||||
".. function:: blend_set(mode)\n"
|
||||
"\n"
|
||||
" Defines the fixed pipeline blending equation.\n"
|
||||
"\n"
|
||||
" :param mode: The type of blend mode.\n"
|
||||
"\n"
|
||||
" * ``NONE`` No blending.\n"
|
||||
" * ``ALPHA`` The original color channels are interpolated according to the alpha "
|
||||
"value.\n"
|
||||
" * ``ALPHA_PREMULT`` The original color channels are interpolated according to the "
|
||||
"alpha value with the new colors pre-multiplied by this value.\n"
|
||||
" * ``ADDITIVE`` The original color channels are added by the corresponding ones.\n"
|
||||
" * ``ADDITIVE_PREMULT`` The original color channels are added by the corresponding ones "
|
||||
"that are pre-multiplied by the alpha value.\n"
|
||||
" * ``MULTIPLY`` The original color channels are multiplied by the corresponding ones.\n"
|
||||
" * ``SUBTRACT`` The original color channels are subtracted by the corresponding ones.\n"
|
||||
" * ``INVERT`` The original color channels are replaced by its complementary color.\n"
|
||||
//" * ``OIT``.\n"
|
||||
//" * ``BACKGROUND`` .\n"
|
||||
//" * ``CUSTOM`` .\n"
|
||||
" :type mode: " PYDOC_BLEND_LITERAL "\n");
|
||||
static PyObject *pygpu_state_blend_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_blend = {pygpu_state_blend_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_blend)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_blend(GPUBlend(pygpu_blend.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_blend_get_doc,
|
||||
".. function:: blend_get()\n"
|
||||
"\n"
|
||||
" Current blending equation.\n"
|
||||
"\n"
|
||||
" :return: The current blend mode.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_state_blend_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUBlend blend = GPU_blend_get();
|
||||
return PyUnicode_FromString(PyC_StringEnum_FindIDFromValue(pygpu_state_blend_items, blend));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_clip_distances_set_doc,
|
||||
".. function:: clip_distances_set(distances_enabled)\n"
|
||||
"\n"
|
||||
" Sets the number of ``gl_ClipDistance`` planes used for clip geometry.\n"
|
||||
"\n"
|
||||
" :param distances_enabled: Number of clip distances enabled.\n"
|
||||
" :type distances_enabled: int\n");
|
||||
static PyObject *pygpu_state_clip_distances_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int distances_enabled = int(PyLong_AsUnsignedLong(value));
|
||||
if (distances_enabled == -1) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (distances_enabled > 6) {
|
||||
PyErr_SetString(PyExc_ValueError, "too many distances enabled, max is 6");
|
||||
}
|
||||
|
||||
GPU_clip_distances(distances_enabled);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_test_set_doc,
|
||||
".. function:: depth_test_set(mode)\n"
|
||||
"\n"
|
||||
" Defines the depth_test equation.\n"
|
||||
"\n"
|
||||
" :param mode: The depth test equation name.\n"
|
||||
" :type mode: " PYDOC_DEPTHTEST_LITERAL "\n");
|
||||
static PyObject *pygpu_state_depth_test_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_depth_test = {pygpu_state_depthtest_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_depth_test)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_depth_test(GPUDepthTest(pygpu_depth_test.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_test_get_doc,
|
||||
".. function:: depth_test_get()\n"
|
||||
"\n"
|
||||
" Current depth_test equation.\n"
|
||||
"\n"
|
||||
" :return: The current depth test mode.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *pygpu_state_depth_test_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
GPUDepthTest test = GPU_depth_test_get();
|
||||
return PyUnicode_FromString(PyC_StringEnum_FindIDFromValue(pygpu_state_depthtest_items, test));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_mask_set_doc,
|
||||
".. function:: depth_mask_set(value)\n"
|
||||
"\n"
|
||||
" Write to depth component.\n"
|
||||
"\n"
|
||||
" :param value: True for writing to the depth component.\n"
|
||||
" :type value: bool\n");
|
||||
static PyObject *pygpu_state_depth_mask_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool write_to_depth;
|
||||
if (!PyC_ParseBool(value, &write_to_depth)) {
|
||||
return nullptr;
|
||||
}
|
||||
GPU_depth_mask(write_to_depth);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_depth_mask_get_doc,
|
||||
".. function:: depth_mask_get()\n"
|
||||
"\n"
|
||||
" Writing status in the depth component.\n"
|
||||
"\n"
|
||||
" :return: True if writing to the depth component is enabled.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *pygpu_state_depth_mask_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
return PyBool_FromLong(GPU_depth_mask_get());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_viewport_set_doc,
|
||||
".. function:: viewport_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Specifies the viewport of the active framebuffer.\n"
|
||||
" Note: The viewport state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x coordinate, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y coordinate, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the viewport.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the viewport.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_state_viewport_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:viewport_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_viewport(x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_viewport_get_doc,
|
||||
".. function:: viewport_get()\n"
|
||||
"\n"
|
||||
" Viewport of the active framebuffer.\n"
|
||||
"\n"
|
||||
" :return: The viewport as a tuple (x, y, xsize, ysize).\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_state_viewport_get(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int viewport[4];
|
||||
GPU_viewport_size_get_i(viewport);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(viewport[0]),
|
||||
PyLong_FromLong(viewport[1]),
|
||||
PyLong_FromLong(viewport[2]),
|
||||
PyLong_FromLong(viewport[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_set_doc,
|
||||
".. function:: scissor_set(x, y, xsize, ysize)\n"
|
||||
"\n"
|
||||
" Specifies the scissor area of the active framebuffer.\n"
|
||||
" Note: The scissor state is not saved upon framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :param x: Lower left corner x coordinate, in pixels.\n"
|
||||
" :type x: int\n"
|
||||
" :param y: Lower left corner y coordinate, in pixels.\n"
|
||||
" :type y: int\n"
|
||||
" :param xsize: Width of the scissor rectangle.\n"
|
||||
" :type xsize: int\n"
|
||||
" :param ysize: Height of the scissor rectangle.\n"
|
||||
" :type ysize: int\n");
|
||||
static PyObject *pygpu_state_scissor_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int x, y, xsize, ysize;
|
||||
if (!PyArg_ParseTuple(args, "iiii:scissor_set", &x, &y, &xsize, &ysize)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_scissor(x, y, xsize, ysize);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_get_doc,
|
||||
".. function:: scissor_get()\n"
|
||||
"\n"
|
||||
" Retrieve the scissors of the active framebuffer.\n"
|
||||
" Note: Only valid between 'scissor_set' and a framebuffer rebind.\n"
|
||||
"\n"
|
||||
" :return: The scissor of the active framebuffer as a tuple\n"
|
||||
" (x, y, xsize, ysize).\n"
|
||||
" x, y: lower left corner of the scissor rectangle, in pixels.\n"
|
||||
" xsize, ysize: width and height of the scissor rectangle.\n"
|
||||
" :rtype: tuple[int, int, int, int]\n");
|
||||
static PyObject *pygpu_state_scissor_get(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int scissor[4];
|
||||
GPU_scissor_get(scissor);
|
||||
|
||||
PyObject *ret = PyTuple_New(4);
|
||||
PyTuple_SET_ITEMS(ret,
|
||||
PyLong_FromLong(scissor[0]),
|
||||
PyLong_FromLong(scissor[1]),
|
||||
PyLong_FromLong(scissor[2]),
|
||||
PyLong_FromLong(scissor[3]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_scissor_test_set_doc,
|
||||
".. function:: scissor_test_set(enable)\n"
|
||||
"\n"
|
||||
" Enable/disable scissor testing on the active framebuffer.\n"
|
||||
"\n"
|
||||
" :param enable:\n"
|
||||
" True - enable scissor testing.\n"
|
||||
" False - disable scissor testing.\n"
|
||||
" :type enable: bool\n");
|
||||
static PyObject *pygpu_state_scissor_test_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool enabled;
|
||||
if (!PyC_ParseBool(value, &enabled)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_scissor_test(enabled);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_line_width_set_doc,
|
||||
".. function:: line_width_set(width)\n"
|
||||
"\n"
|
||||
" Specify the width of rasterized lines.\n"
|
||||
"\n"
|
||||
" :param width: New width.\n"
|
||||
" :type width: float\n");
|
||||
static PyObject *pygpu_state_line_width_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float width = float(PyFloat_AsDouble(value));
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_line_width(width);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_line_width_get_doc,
|
||||
".. function:: line_width_get()\n"
|
||||
"\n"
|
||||
" Current width of rasterized lines.\n"
|
||||
"\n"
|
||||
" :return: The current line width.\n"
|
||||
" :rtype: float\n");
|
||||
static PyObject *pygpu_state_line_width_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float width = GPU_line_width_get();
|
||||
return PyFloat_FromDouble(double(width));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_point_size_set_doc,
|
||||
".. function:: point_size_set(size)\n"
|
||||
"\n"
|
||||
" Specify the diameter of rasterized points.\n"
|
||||
"\n"
|
||||
" When ``program_point_size_set(False)`` is set, this sets the ``size``\n"
|
||||
" uniform of point shaders. Any value set by ``uniform_float(\"size\", ...)``\n"
|
||||
" on a point shader will be overwritten by this function. Use\n"
|
||||
" ``program_point_size_set(True)`` to disable this override and control\n"
|
||||
" point size via ``gl_PointSize`` in the vertex shader.\n"
|
||||
"\n"
|
||||
" :param size: New diameter.\n"
|
||||
" :type size: float\n");
|
||||
static PyObject *pygpu_state_point_size_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
float size = float(PyFloat_AsDouble(value));
|
||||
if (PyErr_Occurred()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_point_size(size);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_color_mask_set_doc,
|
||||
".. function:: color_mask_set(r, g, b, a)\n"
|
||||
"\n"
|
||||
" Enable or disable writing of frame buffer color components.\n"
|
||||
"\n"
|
||||
" :param r: Red component.\n"
|
||||
" :type r: bool\n"
|
||||
" :param g: Green component.\n"
|
||||
" :type g: bool\n"
|
||||
" :param b: Blue component.\n"
|
||||
" :type b: bool\n"
|
||||
" :param a: Alpha component.\n"
|
||||
" :type a: bool\n");
|
||||
static PyObject *pygpu_state_color_mask_set(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
int r, g, b, a;
|
||||
if (!PyArg_ParseTuple(args, "pppp:color_mask_set", &r, &g, &b, &a)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_color_mask(bool(r), bool(g), bool(b), bool(a));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_face_culling_set_doc,
|
||||
".. function:: face_culling_set(culling)\n"
|
||||
"\n"
|
||||
" Specify whether none, front-facing or back-facing facets can be culled.\n"
|
||||
"\n"
|
||||
" :param culling: The face culling mode.\n"
|
||||
" :type culling: Literal['NONE', 'FRONT', 'BACK']\n");
|
||||
static PyObject *pygpu_state_face_culling_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
PyC_StringEnum pygpu_faceculling = {pygpu_state_faceculling_items};
|
||||
if (!PyC_ParseStringEnum(value, &pygpu_faceculling)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_face_culling(GPUFaceCullTest(pygpu_faceculling.value_found));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_front_facing_set_doc,
|
||||
".. function:: front_facing_set(invert)\n"
|
||||
"\n"
|
||||
" Specifies the orientation of front-facing polygons.\n"
|
||||
"\n"
|
||||
" :param invert: True for clockwise polygons as front-facing.\n"
|
||||
" :type invert: bool\n");
|
||||
static PyObject *pygpu_state_front_facing_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool invert;
|
||||
if (!PyC_ParseBool(value, &invert)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_front_facing(invert);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_program_point_size_set_doc,
|
||||
".. function:: program_point_size_set(enable)\n"
|
||||
"\n"
|
||||
" When enabled (True), point size is taken from the shader builtin\n"
|
||||
" ``gl_PointSize``. When disabled (False), the global state value can\n"
|
||||
" be applied to the shader ``size`` uniform, overwriting any user-set\n"
|
||||
" value.\n"
|
||||
"\n"
|
||||
" :param enable: True to use shader ``gl_PointSize``, False for global state.\n"
|
||||
" :type enable: bool\n");
|
||||
static PyObject *pygpu_state_program_point_size_set(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
bool enable;
|
||||
if (!PyC_ParseBool(value, &enable)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_program_point_size(enable);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state_active_framebuffer_get_doc,
|
||||
".. function:: active_framebuffer_get()\n"
|
||||
"\n"
|
||||
" Return the active frame-buffer in context.\n"
|
||||
"\n"
|
||||
" :return: The active framebuffer.\n"
|
||||
" :rtype: :class:`gpu.types.GPUFrameBuffer`\n");
|
||||
static PyObject *pygpu_state_active_framebuffer_get(PyObject * /*self*/)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
gpu::FrameBuffer *fb = GPU_framebuffer_active_get();
|
||||
return BPyGPUFrameBuffer_CreatePyObject(fb, true);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Module
|
||||
* \{ */
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_state__tp_methods[] = {
|
||||
/* Manage Stack */
|
||||
{"blend_set",
|
||||
static_cast<PyCFunction>(pygpu_state_blend_set),
|
||||
METH_O,
|
||||
pygpu_state_blend_set_doc},
|
||||
{"blend_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_blend_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_blend_get_doc},
|
||||
{"clip_distances_set",
|
||||
static_cast<PyCFunction>(pygpu_state_clip_distances_set),
|
||||
METH_O,
|
||||
pygpu_state_clip_distances_set_doc},
|
||||
{"depth_test_set",
|
||||
static_cast<PyCFunction>(pygpu_state_depth_test_set),
|
||||
METH_O,
|
||||
pygpu_state_depth_test_set_doc},
|
||||
{"depth_test_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_depth_test_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_depth_test_get_doc},
|
||||
{"depth_mask_set",
|
||||
static_cast<PyCFunction>(pygpu_state_depth_mask_set),
|
||||
METH_O,
|
||||
pygpu_state_depth_mask_set_doc},
|
||||
{"depth_mask_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_depth_mask_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_depth_mask_get_doc},
|
||||
{"viewport_set",
|
||||
static_cast<PyCFunction>(pygpu_state_viewport_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_viewport_set_doc},
|
||||
{"viewport_get",
|
||||
static_cast<PyCFunction>(pygpu_state_viewport_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_viewport_get_doc},
|
||||
{"scissor_set",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_scissor_set_doc},
|
||||
{"scissor_get",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_scissor_get_doc},
|
||||
{"scissor_test_set",
|
||||
static_cast<PyCFunction>(pygpu_state_scissor_test_set),
|
||||
METH_O,
|
||||
pygpu_state_scissor_test_set_doc},
|
||||
{"line_width_set",
|
||||
static_cast<PyCFunction>(pygpu_state_line_width_set),
|
||||
METH_O,
|
||||
pygpu_state_line_width_set_doc},
|
||||
{"line_width_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_line_width_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_line_width_get_doc},
|
||||
{"point_size_set",
|
||||
static_cast<PyCFunction>(pygpu_state_point_size_set),
|
||||
METH_O,
|
||||
pygpu_state_point_size_set_doc},
|
||||
{"color_mask_set",
|
||||
static_cast<PyCFunction>(pygpu_state_color_mask_set),
|
||||
METH_VARARGS,
|
||||
pygpu_state_color_mask_set_doc},
|
||||
{"face_culling_set",
|
||||
static_cast<PyCFunction>(pygpu_state_face_culling_set),
|
||||
METH_O,
|
||||
pygpu_state_face_culling_set_doc},
|
||||
{"front_facing_set",
|
||||
static_cast<PyCFunction>(pygpu_state_front_facing_set),
|
||||
METH_O,
|
||||
pygpu_state_front_facing_set_doc},
|
||||
{"program_point_size_set",
|
||||
static_cast<PyCFunction>(pygpu_state_program_point_size_set),
|
||||
METH_O,
|
||||
pygpu_state_program_point_size_set_doc},
|
||||
{"active_framebuffer_get",
|
||||
reinterpret_cast<PyCFunction>(pygpu_state_active_framebuffer_get),
|
||||
METH_NOARGS,
|
||||
pygpu_state_active_framebuffer_get_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_state__tp_doc,
|
||||
"This module provides access to the gpu state.");
|
||||
static PyModuleDef pygpu_state_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.state",
|
||||
/*m_doc*/ pygpu_state__tp_doc,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ pygpu_state__tp_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_state_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_state_module_def);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/gpu/gpu_py_state.hh
Normal file
17
blender-5.2.0/source/blender/python/gpu/gpu_py_state.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_state_init();
|
||||
|
||||
} // namespace blender
|
||||
1022
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.cc
Normal file
1022
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.cc
Normal file
File diff suppressed because it is too large
Load Diff
44
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.hh
Normal file
44
blender-5.2.0/source/blender/python/gpu/gpu_py_texture.hh
Normal file
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class Texture;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUTexture_Type;
|
||||
|
||||
/**
|
||||
* GPU_DEPTH24_STENCIL8 and GPU_DEPTH_COMPONENT24 are deprecated in Blender 5.0. These formats are
|
||||
* automatically converted to their 32F variant.
|
||||
*/
|
||||
constexpr int GPU_DEPTH24_STENCIL8_DEPRECATED = -1;
|
||||
constexpr int GPU_DEPTH_COMPONENT24_DEPRECATED = -2;
|
||||
extern const struct PyC_StringEnumItems pygpu_textureformat_items[];
|
||||
|
||||
#define BPyGPUTexture_Check(v) (Py_TYPE(v) == &BPyGPUTexture_Type)
|
||||
|
||||
struct BPyGPUTexture {
|
||||
PyObject_HEAD
|
||||
gpu::Texture *tex;
|
||||
};
|
||||
|
||||
[[nodiscard]] int bpygpu_ParseTexture(PyObject *o, void *p);
|
||||
[[nodiscard]] PyObject *bpygpu_texture_init();
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUTexture_CreatePyObject(gpu::Texture *tex, bool shared_reference)
|
||||
ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
109
blender-5.2.0/source/blender/python/gpu/gpu_py_types.cc
Normal file
109
blender-5.2.0/source/blender/python/gpu/gpu_py_types.cc
Normal file
@@ -0,0 +1,109 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "gpu_py_matrix.hh"
|
||||
#include "gpu_py_offscreen.hh"
|
||||
#include "gpu_py_types.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name GPU Types Module
|
||||
* \{ */
|
||||
|
||||
static PyModuleDef pygpu_types_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "gpu.types",
|
||||
/*m_doc*/ nullptr,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ nullptr,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *bpygpu_types_init()
|
||||
{
|
||||
PyObject *submodule;
|
||||
|
||||
submodule = PyModule_Create(&pygpu_types_module_def);
|
||||
|
||||
if (PyType_Ready(&BPyGPU_BufferType) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUVertFormat_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUVertBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUIndexBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUBatch_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUOffScreen_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUShader_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUTexture_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUFrameBuffer_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUUniformBuf_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUShaderCreateInfo_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPUStageInterfaceInfo_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&PyGPUMatrixStackContext_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&PyGPUOffscreenStackContext_Type) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
if (PyType_Ready(&BPyGPU_DeviceType) < 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyModule_AddType(submodule, &BPyGPU_BufferType);
|
||||
PyModule_AddType(submodule, &BPyGPUVertFormat_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUVertBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUIndexBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUBatch_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUOffScreen_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUShader_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUTexture_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUFrameBuffer_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUUniformBuf_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUShaderCreateInfo_Type);
|
||||
PyModule_AddType(submodule, &BPyGPUStageInterfaceInfo_Type);
|
||||
PyModule_AddType(submodule, &PyGPUMatrixStackContext_Type);
|
||||
PyModule_AddType(submodule, &PyGPUOffscreenStackContext_Type);
|
||||
PyModule_AddType(submodule, &BPyGPU_DeviceType);
|
||||
|
||||
return submodule;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
29
blender-5.2.0/source/blender/python/gpu/gpu_py_types.hh
Normal file
29
blender-5.2.0/source/blender/python/gpu/gpu_py_types.hh
Normal file
@@ -0,0 +1,29 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gpu_py_buffer.hh" // IWYU pragma: export
|
||||
|
||||
#include "gpu_py_batch.hh" // IWYU pragma: export
|
||||
#include "gpu_py_compute.hh" // IWYU pragma: export
|
||||
#include "gpu_py_element.hh" // IWYU pragma: export
|
||||
#include "gpu_py_framebuffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_offscreen.hh" // IWYU pragma: export
|
||||
#include "gpu_py_platform.hh" // IWYU pragma: export
|
||||
#include "gpu_py_shader.hh" // IWYU pragma: export
|
||||
#include "gpu_py_texture.hh" // IWYU pragma: export
|
||||
#include "gpu_py_uniformbuffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_vertex_buffer.hh" // IWYU pragma: export
|
||||
#include "gpu_py_vertex_format.hh" // IWYU pragma: export
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *bpygpu_types_init();
|
||||
|
||||
} // namespace blender
|
||||
257
blender-5.2.0/source/blender/python/gpu/gpu_py_uniformbuffer.cc
Normal file
257
blender-5.2.0/source/blender/python/gpu/gpu_py_uniformbuffer.cc
Normal file
@@ -0,0 +1,257 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* This file defines the uniform buffer functionalities of the 'gpu' module
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_string_utf8.h"
|
||||
|
||||
#include "GPU_context.hh"
|
||||
#include "GPU_uniform_buffer.hh"
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_uniformbuffer.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::UniformBuf Common Utilities
|
||||
* \{ */
|
||||
|
||||
static int pygpu_uniformbuffer_valid_check(BPyGPUUniformBuf *bpygpu_ub)
|
||||
{
|
||||
if (UNLIKELY(bpygpu_ub->ubo == nullptr)) {
|
||||
PyErr_SetString(PyExc_ReferenceError,
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
"GPU uniform buffer was freed, no further access is valid");
|
||||
#else
|
||||
|
||||
"GPU uniform buffer: internal error");
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define BPYGPU_UNIFORMBUF_CHECK_OBJ(bpygpu) \
|
||||
{ \
|
||||
if (UNLIKELY(pygpu_uniformbuffer_valid_check(bpygpu) == -1)) { \
|
||||
return nullptr; \
|
||||
} \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name gpu::UniformBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_uniformbuffer__tp_new(PyTypeObject * /*self*/,
|
||||
PyObject *args,
|
||||
PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
gpu::UniformBuf *ubo = nullptr;
|
||||
PyObject *pybuffer_obj;
|
||||
char err_out[256] = "unknown error. See console";
|
||||
|
||||
static const char *_keywords[] = {"data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `data` */
|
||||
":GPUUniformBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &pybuffer_obj)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!GPU_context_active_get()) {
|
||||
STRNCPY_UTF8(err_out, "No active GPU context found");
|
||||
}
|
||||
else {
|
||||
Py_buffer pybuffer;
|
||||
if (PyObject_GetBuffer(pybuffer_obj, &pybuffer, PyBUF_SIMPLE) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ((pybuffer.len % 16) != 0) {
|
||||
STRNCPY_UTF8(err_out, "UBO is not padded to size of vec4");
|
||||
}
|
||||
else {
|
||||
ubo = GPU_uniformbuf_create_ex(pybuffer.len, pybuffer.buf, "python_uniformbuffer");
|
||||
}
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
|
||||
if (ubo == nullptr) {
|
||||
PyErr_Format(PyExc_RuntimeError, "GPUUniformBuf.__new__(...) failed with '%s'", err_out);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return BPyGPUUniformBuf_CreatePyObject(ubo);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer_update_doc,
|
||||
".. method:: update(data)\n"
|
||||
"\n"
|
||||
" Update the data of the uniform buffer object.\n"
|
||||
"\n"
|
||||
" :param data: Data to fill the buffer.\n"
|
||||
" :type data: Buffer\n");
|
||||
static PyObject *pygpu_uniformbuffer_update(BPyGPUUniformBuf *self, PyObject *obj)
|
||||
{
|
||||
BPYGPU_UNIFORMBUF_CHECK_OBJ(self);
|
||||
|
||||
Py_buffer pybuffer;
|
||||
if (PyObject_GetBuffer(obj, &pybuffer, PyBUF_SIMPLE) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPU_uniformbuf_update(self->ubo, pybuffer.buf);
|
||||
PyBuffer_Release(&pybuffer);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer_free_doc,
|
||||
".. method:: free()\n"
|
||||
"\n"
|
||||
" Free the uniform buffer object.\n"
|
||||
" The uniform buffer object will no longer be accessible.\n");
|
||||
static PyObject *pygpu_uniformbuffer_free(BPyGPUUniformBuf *self)
|
||||
{
|
||||
BPYGPU_UNIFORMBUF_CHECK_OBJ(self);
|
||||
|
||||
GPU_uniformbuf_free(self->ubo);
|
||||
self->ubo = nullptr;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void BPyGPUUniformBuf__tp_dealloc(BPyGPUUniformBuf *self)
|
||||
{
|
||||
if (self->ubo) {
|
||||
GPU_uniformbuf_free(self->ubo);
|
||||
}
|
||||
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject *>(self));
|
||||
}
|
||||
|
||||
static PyGetSetDef pygpu_uniformbuffer__tp_getseters[] = {
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMethodDef pygpu_uniformbuffer__tp_methods[] = {
|
||||
{"update",
|
||||
reinterpret_cast<PyCFunction>(pygpu_uniformbuffer_update),
|
||||
METH_O,
|
||||
pygpu_uniformbuffer_update_doc},
|
||||
#ifdef BPYGPU_USE_GPUOBJ_FREE_METHOD
|
||||
{"free", (PyCFunction)pygpu_uniformbuffer_free, METH_NOARGS, pygpu_uniformbuffer_free_doc},
|
||||
#endif
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_uniformbuffer__tp_doc,
|
||||
".. class:: GPUUniformBuf\n"
|
||||
"\n"
|
||||
" This object gives access to uniform buffers.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(data)\n"
|
||||
"\n"
|
||||
" :param data: Data to fill the buffer.\n"
|
||||
" :type data: Buffer\n");
|
||||
PyTypeObject BPyGPUUniformBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUUniformBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUUniformBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(BPyGPUUniformBuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_uniformbuffer__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_uniformbuffer__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ pygpu_uniformbuffer__tp_getseters,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_uniformbuffer__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUUniformBuf_CreatePyObject(gpu::UniformBuf *ubo)
|
||||
{
|
||||
BPyGPUUniformBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUUniformBuf, &BPyGPUUniformBuf_Type);
|
||||
self->ubo = ubo;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
#undef BPYGPU_UNIFORMBUF_CHECK_OBJ
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class UniformBuf;
|
||||
} // namespace gpu
|
||||
|
||||
extern PyTypeObject BPyGPUUniformBuf_Type;
|
||||
|
||||
#define BPyGPUUniformBuf_Check(v) (Py_TYPE(v) == &BPyGPUUniformBuf_Type)
|
||||
|
||||
struct BPyGPUUniformBuf {
|
||||
PyObject_HEAD
|
||||
gpu::UniformBuf *ubo;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUUniformBuf_CreatePyObject(gpu::UniformBuf *ubo) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
434
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_buffer.cc
Normal file
434
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_buffer.cc
Normal file
@@ -0,0 +1,434 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_vertex_buffer.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_vertex_buffer.hh" /* own include */
|
||||
#include "gpu_py_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Utility Functions
|
||||
* \{ */
|
||||
|
||||
#define PYGPU_AS_NATIVE_SWITCH(attr) \
|
||||
switch (attr->type.comp_type()) { \
|
||||
case GPU_COMP_I8: { \
|
||||
PY_AS_NATIVE(int8_t, PyC_Long_AsI8); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U8: { \
|
||||
PY_AS_NATIVE(uint8_t, PyC_Long_AsU8); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_I16: { \
|
||||
PY_AS_NATIVE(int16_t, PyC_Long_AsI16); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U16: { \
|
||||
PY_AS_NATIVE(uint16_t, PyC_Long_AsU16); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_I32: { \
|
||||
PY_AS_NATIVE(int32_t, PyC_Long_AsI32); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_U32: { \
|
||||
PY_AS_NATIVE(uint32_t, PyC_Long_AsU32); \
|
||||
break; \
|
||||
} \
|
||||
case GPU_COMP_F32: { \
|
||||
PY_AS_NATIVE(float, PyFloat_AsDouble); \
|
||||
break; \
|
||||
} \
|
||||
default: \
|
||||
BLI_assert_unreachable(); \
|
||||
break; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/* No error checking, callers must run PyErr_Occurred */
|
||||
static void pygpu_fill_format_elem(void *data_dst_void, PyObject *py_src, const GPUVertAttr *attr)
|
||||
{
|
||||
#define PY_AS_NATIVE(ty_dst, py_as_native) \
|
||||
{ \
|
||||
ty_dst *data_dst = static_cast<ty_dst *>(data_dst_void); \
|
||||
*data_dst = py_as_native(py_src); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
PYGPU_AS_NATIVE_SWITCH(attr);
|
||||
|
||||
#undef PY_AS_NATIVE
|
||||
}
|
||||
|
||||
/* No error checking, callers must run PyErr_Occurred */
|
||||
static void pygpu_fill_format_sequence(void *data_dst_void,
|
||||
PyObject *py_seq_fast,
|
||||
const GPUVertAttr *attr)
|
||||
{
|
||||
const uint len = attr->type.comp_len();
|
||||
PyObject **value_fast_items = PySequence_Fast_ITEMS(py_seq_fast);
|
||||
|
||||
/**
|
||||
* Args are constants, so range checks will be optimized out if they're no-op's.
|
||||
*/
|
||||
#define PY_AS_NATIVE(ty_dst, py_as_native) \
|
||||
ty_dst *data_dst = static_cast<ty_dst *>(data_dst_void); \
|
||||
for (uint i = 0; i < len; i++) { \
|
||||
data_dst[i] = py_as_native(value_fast_items[i]); \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
PYGPU_AS_NATIVE_SWITCH(attr);
|
||||
|
||||
#undef PY_AS_NATIVE
|
||||
}
|
||||
|
||||
#undef PYGPU_AS_NATIVE_SWITCH
|
||||
#undef WARN_TYPE_LIMIT_PUSH
|
||||
#undef WARN_TYPE_LIMIT_POP
|
||||
|
||||
static bool pygpu_vertbuf_fill_impl(gpu::VertBuf *vbo,
|
||||
uint data_id,
|
||||
PyObject *seq,
|
||||
const char *error_prefix)
|
||||
{
|
||||
const char *exc_str_size_mismatch = "Expected a %s of size %d, got %u";
|
||||
|
||||
bool ok = true;
|
||||
const GPUVertAttr *attr = &GPU_vertbuf_get_format(vbo)->attrs[data_id];
|
||||
uint vert_len = GPU_vertbuf_get_vertex_len(vbo);
|
||||
|
||||
if (PyObject_CheckBuffer(seq)) {
|
||||
Py_buffer pybuffer;
|
||||
|
||||
if (PyObject_GetBuffer(seq, &pybuffer, PyBUF_STRIDES | PyBUF_ND) == -1) {
|
||||
/* PyObject_GetBuffer raise a PyExc_BufferError */
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint comp_len = pybuffer.ndim == 1 ? 1 : uint(pybuffer.shape[1]);
|
||||
|
||||
if (pybuffer.shape[0] != vert_len) {
|
||||
PyErr_Format(
|
||||
PyExc_ValueError, exc_str_size_mismatch, "sequence", vert_len, pybuffer.shape[0]);
|
||||
ok = false;
|
||||
}
|
||||
else if (comp_len != attr->type.comp_len()) {
|
||||
PyErr_Format(
|
||||
PyExc_ValueError, exc_str_size_mismatch, "component", attr->type.comp_len(), comp_len);
|
||||
ok = false;
|
||||
}
|
||||
else {
|
||||
GPU_vertbuf_attr_fill_stride(vbo, data_id, pybuffer.strides[0], pybuffer.buf);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&pybuffer);
|
||||
}
|
||||
else {
|
||||
GPUVertBufRaw data_step;
|
||||
GPU_vertbuf_attr_get_raw_data(vbo, data_id, &data_step);
|
||||
|
||||
PyObject *seq_fast = PySequence_Fast(seq, "Vertex buffer fill");
|
||||
if (seq_fast == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uint seq_len = PySequence_Fast_GET_SIZE(seq_fast);
|
||||
|
||||
if (seq_len != vert_len) {
|
||||
PyErr_Format(PyExc_ValueError, exc_str_size_mismatch, "sequence", vert_len, seq_len);
|
||||
}
|
||||
|
||||
PyObject **seq_items = PySequence_Fast_ITEMS(seq_fast);
|
||||
|
||||
if (attr->type.comp_len() == 1) {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
uchar *data = static_cast<uchar *>(GPU_vertbuf_raw_step(&data_step));
|
||||
PyObject *item = seq_items[i];
|
||||
pygpu_fill_format_elem(data, item, attr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (uint i = 0; i < seq_len; i++) {
|
||||
uchar *data = static_cast<uchar *>(GPU_vertbuf_raw_step(&data_step));
|
||||
PyObject *seq_fast_item = PySequence_Fast(seq_items[i], error_prefix);
|
||||
|
||||
if (seq_fast_item == nullptr) {
|
||||
ok = false;
|
||||
goto finally;
|
||||
}
|
||||
if (PySequence_Fast_GET_SIZE(seq_fast_item) != attr->type.comp_len()) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
exc_str_size_mismatch,
|
||||
"sequence",
|
||||
attr->type.comp_len(),
|
||||
PySequence_Fast_GET_SIZE(seq_fast_item));
|
||||
ok = false;
|
||||
Py_DECREF(seq_fast_item);
|
||||
goto finally;
|
||||
}
|
||||
|
||||
/* May trigger error, check below */
|
||||
pygpu_fill_format_sequence(data, seq_fast_item, attr);
|
||||
Py_DECREF(seq_fast_item);
|
||||
}
|
||||
}
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
ok = false;
|
||||
}
|
||||
|
||||
finally:
|
||||
|
||||
Py_DECREF(seq_fast);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int pygpu_vertbuf_fill(gpu::VertBuf *buf,
|
||||
int id,
|
||||
PyObject *py_seq_data,
|
||||
const char *error_prefix)
|
||||
{
|
||||
if (id < 0 || id >= GPU_vertbuf_get_format(buf)->attr_len) {
|
||||
PyErr_Format(PyExc_ValueError, "Format id %d out of range", id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (buf->data<char>().data() == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "Can't fill, static buffer already in use");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pygpu_vertbuf_fill_impl(buf, uint(id), py_seq_data, error_prefix)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VertBuf Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_vertbuf__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
struct {
|
||||
PyObject *py_fmt;
|
||||
uint len;
|
||||
} params;
|
||||
|
||||
static const char *_keywords[] = {"format", "len", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `format` */
|
||||
"I" /* `len` */
|
||||
":GPUVertBuf.__new__",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, &BPyGPUVertFormat_Type, ¶ms.py_fmt, ¶ms.len))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const GPUVertFormat &fmt = (reinterpret_cast<BPyGPUVertFormat *>(params.py_fmt))->fmt;
|
||||
gpu::VertBuf *vbo = GPU_vertbuf_create_with_format(fmt);
|
||||
|
||||
GPU_vertbuf_data_alloc(*vbo, params.len);
|
||||
|
||||
return BPyGPUVertBuf_CreatePyObject(vbo);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertbuf_attr_fill_doc,
|
||||
".. method:: attr_fill(id, data)\n"
|
||||
"\n"
|
||||
" Insert data into the buffer for a single attribute.\n"
|
||||
"\n"
|
||||
" :param id: Either the name or the id of the attribute.\n"
|
||||
" :type id: int | str\n"
|
||||
" :param data: Buffer or sequence of data that should be stored in the buffer\n"
|
||||
" :type data: Buffer | "
|
||||
"Sequence[float] | Sequence[int] | Sequence[Sequence[float]] | Sequence[Sequence[int]]\n");
|
||||
static PyObject *pygpu_vertbuf_attr_fill(BPyGPUVertBuf *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *data;
|
||||
PyObject *identifier;
|
||||
|
||||
static const char *_keywords[] = {"id", "data", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O" /* `id` */
|
||||
"O" /* `data` */
|
||||
":attr_fill",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &identifier, &data)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int id;
|
||||
|
||||
if (PyLong_Check(identifier)) {
|
||||
id = PyLong_AsLong(identifier);
|
||||
}
|
||||
else if (PyUnicode_Check(identifier)) {
|
||||
const GPUVertFormat *format = GPU_vertbuf_get_format(self->buf);
|
||||
const char *name = PyUnicode_AsUTF8(identifier);
|
||||
id = GPU_vertformat_attr_id_get(format, name);
|
||||
if (id == -1) {
|
||||
PyErr_Format(PyExc_ValueError, "Unknown attribute '%s'", name);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyErr_SetString(PyExc_TypeError, "expected int or str type as identifier");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!pygpu_vertbuf_fill(self->buf, id, data, "GPUVertBuf.attr_fill")) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_vertbuf__tp_methods[] = {
|
||||
{"attr_fill",
|
||||
reinterpret_cast<PyCFunction>(pygpu_vertbuf_attr_fill),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_vertbuf_attr_fill_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void pygpu_vertbuf__tp_dealloc(BPyGPUVertBuf *self)
|
||||
{
|
||||
GPU_vertbuf_discard(self->buf);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertbuf__tp_doc,
|
||||
".. class:: GPUVertBuf\n"
|
||||
"\n"
|
||||
" Contains a VBO.\n"
|
||||
"\n"
|
||||
" .. method:: __init__(format, len)\n"
|
||||
"\n"
|
||||
" :param format: Vertex format.\n"
|
||||
" :type format: :class:`gpu.types.GPUVertFormat`\n"
|
||||
" :param len: Amount of vertices that will fit into this buffer.\n"
|
||||
" :type len: int\n");
|
||||
PyTypeObject BPyGPUVertBuf_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUVertBuf",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUVertBuf),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_vertbuf__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_vertbuf__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_vertbuf__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_vertbuf__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUVertBuf_CreatePyObject(gpu::VertBuf *buf)
|
||||
{
|
||||
BPyGPUVertBuf *self;
|
||||
|
||||
self = PyObject_New(BPyGPUVertBuf, &BPyGPUVertBuf_Type);
|
||||
self->buf = buf;
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace gpu {
|
||||
class VertBuf;
|
||||
}
|
||||
|
||||
extern PyTypeObject BPyGPUVertBuf_Type;
|
||||
|
||||
#define BPyGPUVertBuf_Check(v) (Py_TYPE(v) == &BPyGPUVertBuf_Type)
|
||||
|
||||
struct BPyGPUVertBuf {
|
||||
PyObject_HEAD
|
||||
/* The buf is owned, we may support thin wrapped batches later. */
|
||||
gpu::VertBuf *buf;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUVertBuf_CreatePyObject(gpu::VertBuf *buf) ATTR_NONNULL(1);
|
||||
|
||||
} // namespace blender
|
||||
265
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_format.cc
Normal file
265
blender-5.2.0/source/blender/python/gpu/gpu_py_vertex_format.cc
Normal file
@@ -0,0 +1,265 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*
|
||||
* - Use `bpygpu_` for local API.
|
||||
* - Use `BPyGPU` for public API.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "gpu_py.hh"
|
||||
#include "gpu_py_vertex_format.hh" /* own include */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Enum Conversion
|
||||
*
|
||||
* Use with PyArg_ParseTuple's "O&" formatting.
|
||||
* \{ */
|
||||
|
||||
static PyC_StringEnumItems pygpu_vertcomptype_items[] = {
|
||||
{GPU_COMP_I8, "I8"},
|
||||
{GPU_COMP_U8, "U8"},
|
||||
{GPU_COMP_I16, "I16"},
|
||||
{GPU_COMP_U16, "U16"},
|
||||
{GPU_COMP_I32, "I32"},
|
||||
{GPU_COMP_U32, "U32"},
|
||||
{GPU_COMP_F32, "F32"},
|
||||
{GPU_COMP_I10, "I10"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
static PyC_StringEnumItems pygpu_vertfetchmode_items[] = {
|
||||
{GPU_FETCH_FLOAT, "FLOAT"},
|
||||
{GPU_FETCH_INT, "INT"},
|
||||
{GPU_FETCH_INT_TO_FLOAT_UNIT, "INT_TO_FLOAT_UNIT"},
|
||||
{0, nullptr},
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name VertFormat Type
|
||||
* \{ */
|
||||
|
||||
static PyObject *pygpu_vertformat__tp_new(PyTypeObject * /*type*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPYGPU_IS_INIT_OR_ERROR_OBJ;
|
||||
|
||||
if (PyTuple_GET_SIZE(args) || (kwds && PyDict_Size(kwds))) {
|
||||
PyErr_SetString(PyExc_ValueError, "This function takes no arguments");
|
||||
return nullptr;
|
||||
}
|
||||
return BPyGPUVertFormat_CreatePyObject(nullptr);
|
||||
}
|
||||
|
||||
static uint attr_size(GPUVertCompType type, int len)
|
||||
{
|
||||
if (type == GPU_COMP_I10) {
|
||||
return 4; /* Always packed as 10_10_10_2. */
|
||||
}
|
||||
BLI_assert(type <= GPU_COMP_F32); /* Other types have irregular sizes (not bytes). */
|
||||
const uint sizes[] = {1, 1, 2, 2, 4, 4, 4};
|
||||
return len * sizes[type];
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertformat_attr_add_doc,
|
||||
".. method:: attr_add(id, comp_type, len, fetch_mode)\n"
|
||||
"\n"
|
||||
" Add a new attribute to the format.\n"
|
||||
"\n"
|
||||
" :param id: Name of the attribute. Often ``position``, ``normal``, ...\n"
|
||||
" :type id: str\n"
|
||||
" :param comp_type: The data type that will be used to store the value in memory.\n"
|
||||
" :type comp_type: Literal['I8', 'U8', 'I16', 'U16', 'I32', 'U32', 'F32', 'I10']\n"
|
||||
" :param len: How many individual values the attribute consists of\n"
|
||||
" (e.g. 2 for uv coordinates).\n"
|
||||
" :type len: int\n"
|
||||
" :param fetch_mode: How values from memory will be converted when used in the shader.\n"
|
||||
" This is mainly useful for memory optimizations when you want to store values with\n"
|
||||
" reduced precision. E.g. you can store a float in only 1 byte but it will be\n"
|
||||
" converted to a normal 4 byte float when used.\n"
|
||||
" :type fetch_mode: Literal['FLOAT', 'INT', 'INT_TO_FLOAT_UNIT']\n");
|
||||
static PyObject *pygpu_vertformat_attr_add(BPyGPUVertFormat *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
const char *id;
|
||||
uint len;
|
||||
PyC_StringEnum comp_type = {pygpu_vertcomptype_items, GPU_COMP_I8};
|
||||
PyC_StringEnum fetch_mode = {pygpu_vertfetchmode_items, GPU_FETCH_FLOAT};
|
||||
|
||||
if (self->fmt.attr_len == GPU_VERT_ATTR_MAX_LEN) {
|
||||
PyErr_SetString(PyExc_ValueError, "Maximum attr reached " STRINGIFY(GPU_VERT_ATTR_MAX_LEN));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static const char *_keywords[] = {"id", "comp_type", "len", "fetch_mode", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"$" /* Keyword only arguments. */
|
||||
"s" /* `id` */
|
||||
"O&" /* `comp_type` */
|
||||
"I" /* `len` */
|
||||
"O&" /* `fetch_mode` */
|
||||
":attr_add",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kwds,
|
||||
&_parser,
|
||||
&id,
|
||||
PyC_ParseStringEnum,
|
||||
&comp_type,
|
||||
&len,
|
||||
PyC_ParseStringEnum,
|
||||
&fetch_mode))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GPUVertCompType comp_type_enum = GPUVertCompType(comp_type.value_found);
|
||||
GPUVertFetchMode fetch_mode_enum = GPUVertFetchMode(fetch_mode.value_found);
|
||||
|
||||
if (len > 4) {
|
||||
PyErr_WarnEx(
|
||||
PyExc_DeprecationWarning,
|
||||
"Using GPUVertFormat.attr_add(...) with component count greater than 4 is deprecated. "
|
||||
"Use several attributes for each matrix columns instead.",
|
||||
1);
|
||||
}
|
||||
|
||||
if (attr_size(comp_type_enum, len) % 4 != 0) {
|
||||
PyErr_WarnEx(PyExc_DeprecationWarning,
|
||||
"Using GPUVertFormat.attr_add(...) with a format that is not 4 bytes aligned is "
|
||||
"deprecated. Add padding components and/or higher precision integers.",
|
||||
1);
|
||||
}
|
||||
|
||||
uint attr_id = GPU_vertformat_attr_add_legacy(
|
||||
&self->fmt, id, comp_type_enum, len, fetch_mode_enum);
|
||||
|
||||
return PyLong_FromLong(attr_id);
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef pygpu_vertformat__tp_methods[] = {
|
||||
{"attr_add",
|
||||
reinterpret_cast<PyCFunction>(pygpu_vertformat_attr_add),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
pygpu_vertformat_attr_add_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void pygpu_vertformat__tp_dealloc(BPyGPUVertFormat *self)
|
||||
{
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
pygpu_vertformat__tp_doc,
|
||||
".. class:: GPUVertFormat()\n"
|
||||
"\n"
|
||||
" This object contains information about the structure of a vertex buffer.\n");
|
||||
PyTypeObject BPyGPUVertFormat_Type = {
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
/*tp_name*/ "GPUVertFormat",
|
||||
/*tp_basicsize*/ sizeof(BPyGPUVertFormat),
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ reinterpret_cast<destructor>(pygpu_vertformat__tp_dealloc),
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT,
|
||||
/*tp_doc*/ pygpu_vertformat__tp_doc,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ pygpu_vertformat__tp_methods,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ pygpu_vertformat__tp_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public API
|
||||
* \{ */
|
||||
|
||||
PyObject *BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt)
|
||||
{
|
||||
BPyGPUVertFormat *self;
|
||||
|
||||
self = PyObject_New(BPyGPUVertFormat, &BPyGPUVertFormat_Type);
|
||||
if (fmt) {
|
||||
self->fmt = *fmt;
|
||||
}
|
||||
else {
|
||||
memset(&self->fmt, 0, sizeof(self->fmt));
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup bpygpu
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "GPU_vertex_format.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
extern PyTypeObject BPyGPUVertFormat_Type;
|
||||
|
||||
#define BPyGPUVertFormat_Check(v) (Py_TYPE(v) == &BPyGPUVertFormat_Type)
|
||||
|
||||
struct BPyGPUVertFormat {
|
||||
PyObject_HEAD
|
||||
GPUVertFormat fmt;
|
||||
};
|
||||
|
||||
[[nodiscard]] PyObject *BPyGPUVertFormat_CreatePyObject(GPUVertFormat *fmt);
|
||||
|
||||
} // namespace blender
|
||||
362
blender-5.2.0/source/blender/python/intern/CMakeLists.txt
Normal file
362
blender-5.2.0/source/blender/python/intern/CMakeLists.txt
Normal file
@@ -0,0 +1,362 @@
|
||||
# SPDX-FileCopyrightText: 2006 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
..
|
||||
../../editors/include
|
||||
../../imbuf/intern/oiio
|
||||
../../makesrna
|
||||
../../../../intern/mantaflow/extern
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
bpy.cc
|
||||
bpy_app.cc
|
||||
bpy_app_alembic.cc
|
||||
bpy_app_build_options.cc
|
||||
bpy_app_ffmpeg.cc
|
||||
bpy_app_handlers.cc
|
||||
bpy_app_icons.cc
|
||||
bpy_app_ocio.cc
|
||||
bpy_app_oiio.cc
|
||||
bpy_app_opensubdiv.cc
|
||||
bpy_app_openvdb.cc
|
||||
bpy_app_sdl.cc
|
||||
bpy_app_timers.cc
|
||||
bpy_app_translations.cc
|
||||
bpy_app_usd.cc
|
||||
bpy_audaspace.cc
|
||||
bpy_capi_utils.cc
|
||||
bpy_cli_command.cc
|
||||
bpy_driver.cc
|
||||
bpy_driver_bytecode.cc
|
||||
bpy_geometry_set.cc
|
||||
bpy_gizmo_wrap.cc
|
||||
bpy_inline_shader_nodes.cc
|
||||
bpy_interface.cc
|
||||
bpy_interface_atexit.cc
|
||||
bpy_interface_run.cc
|
||||
bpy_intern_string.cc
|
||||
bpy_library_load.cc
|
||||
bpy_library_write.cc
|
||||
bpy_msgbus.cc
|
||||
bpy_operator.cc
|
||||
bpy_operator_function.cc
|
||||
bpy_operator_wrap.cc
|
||||
bpy_path.cc
|
||||
bpy_props.cc
|
||||
bpy_rna.cc
|
||||
bpy_rna_anim.cc
|
||||
bpy_rna_array.cc
|
||||
bpy_rna_callback.cc
|
||||
bpy_rna_context.cc
|
||||
bpy_rna_data.cc
|
||||
bpy_rna_driver.cc
|
||||
bpy_rna_gizmo.cc
|
||||
bpy_rna_id_collection.cc
|
||||
bpy_rna_operator.cc
|
||||
bpy_rna_text.cc
|
||||
bpy_rna_types_capi.cc
|
||||
bpy_rna_ui.cc
|
||||
bpy_rna_wm.cc
|
||||
bpy_traceback.cc
|
||||
bpy_utils_previews.cc
|
||||
bpy_utils_units.cc
|
||||
|
||||
bpy.hh
|
||||
bpy_app.hh
|
||||
bpy_app_alembic.hh
|
||||
bpy_app_build_options.hh
|
||||
bpy_app_ffmpeg.hh
|
||||
bpy_app_handlers.hh
|
||||
bpy_app_icons.hh
|
||||
bpy_app_ocio.hh
|
||||
bpy_app_oiio.hh
|
||||
bpy_app_opensubdiv.hh
|
||||
bpy_app_openvdb.hh
|
||||
bpy_app_sdl.hh
|
||||
bpy_app_timers.hh
|
||||
bpy_app_translations.hh
|
||||
bpy_app_usd.hh
|
||||
bpy_audaspace.hh
|
||||
bpy_capi_utils.hh
|
||||
bpy_cli_command.hh
|
||||
bpy_driver.hh
|
||||
bpy_driver_bytecode.hh
|
||||
bpy_geometry_set.hh
|
||||
bpy_gizmo_wrap.hh
|
||||
bpy_inline_shader_nodes.hh
|
||||
bpy_intern_string.hh
|
||||
bpy_library.hh
|
||||
bpy_msgbus.hh
|
||||
bpy_operator.hh
|
||||
bpy_operator_function.hh
|
||||
bpy_operator_wrap.hh
|
||||
bpy_path.hh
|
||||
bpy_props.hh
|
||||
bpy_rna.hh
|
||||
bpy_rna_anim.hh
|
||||
bpy_rna_callback.hh
|
||||
bpy_rna_context.hh
|
||||
bpy_rna_data.hh
|
||||
bpy_rna_driver.hh
|
||||
bpy_rna_gizmo.hh
|
||||
bpy_rna_id_collection.hh
|
||||
bpy_rna_operator.hh
|
||||
bpy_rna_text.hh
|
||||
bpy_rna_types_capi.hh
|
||||
bpy_rna_ui.hh
|
||||
bpy_rna_wm.hh
|
||||
bpy_traceback.hh
|
||||
bpy_utils_previews.hh
|
||||
bpy_utils_units.hh
|
||||
../BPY_extern.hh
|
||||
../BPY_extern_clog.hh
|
||||
../BPY_extern_python.hh
|
||||
../BPY_extern_run.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::blenloader
|
||||
PRIVATE bf::blentranslation
|
||||
PRIVATE bf::depsgraph
|
||||
PRIVATE bf::dna
|
||||
bf_editor_animation
|
||||
bf_editor_interface
|
||||
bf_editor_space_api
|
||||
PRIVATE bf::gpu
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::animrig
|
||||
bf_python_gpu
|
||||
PRIVATE bf::imbuf::opencolorio
|
||||
PRIVATE bf::nodes
|
||||
PRIVATE bf::windowmanager
|
||||
|
||||
PRIVATE bf::dependencies::optional::python
|
||||
PRIVATE bf::dependencies::optional::ffmpeg
|
||||
PRIVATE bf::dependencies::optional::sdl
|
||||
)
|
||||
|
||||
# Only to check if `buildinfo` is available.
|
||||
if(WITH_BUILDINFO)
|
||||
add_definitions(-DBUILD_DATE)
|
||||
endif()
|
||||
|
||||
if(WITH_INSTALL_PORTABLE)
|
||||
add_definitions(-DWITH_INSTALL_PORTABLE)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON_MODULE)
|
||||
add_definitions(-DWITH_PYTHON_MODULE)
|
||||
endif()
|
||||
|
||||
|
||||
# Find the SSL certificate for the portable Blender installation.
|
||||
# Without this, the absolute path on the builder is used, causing HTTPS access to fail.
|
||||
# For example `urllib.request.urlopen("https://projects.blender.org")` fails
|
||||
# (or any other HTTPS site). see: #102300 for details.
|
||||
# NOTE: that this isn't necessary on WIN32.
|
||||
if(WITH_PYTHON AND WITH_PYTHON_INSTALL AND (APPLE OR WITH_INSTALL_PORTABLE) AND (NOT WIN32))
|
||||
# - `PYTHON_SSL_CERT_FILE` absolute path to the PEM file.
|
||||
find_python_module_file("certifi/cacert.pem" PYTHON_SSL_CERT_FILE _python_ssl_cert_file_relative)
|
||||
mark_as_advanced(PYTHON_SSL_CERT_FILE)
|
||||
if(PYTHON_SSL_CERT_FILE)
|
||||
add_definitions(-DPYTHON_SSL_CERT_FILE="${_python_ssl_cert_file_relative}")
|
||||
else()
|
||||
message(WARNING
|
||||
"Unable to find \"certifi/cacert.pem\" within \"${PYTHON_LIBPATH}\", "
|
||||
"this build will not be able to use bundled certificates with the \"ssl\" module!"
|
||||
)
|
||||
endif()
|
||||
unset(_python_ssl_cert_file_relative)
|
||||
endif()
|
||||
|
||||
if(WITH_PYTHON_SAFETY)
|
||||
add_definitions(-DWITH_PYTHON_SAFETY)
|
||||
endif()
|
||||
|
||||
if(WITH_AUDASPACE)
|
||||
# It's possible to build with AUDASPACE (for file IO) but without the `aud` Python API,
|
||||
# when building without NUMPY so define both `WITH_AUDASPACE` & `DWITH_AUDASPACE_PY`.
|
||||
add_definitions(-DWITH_AUDASPACE)
|
||||
if(WITH_PYTHON_NUMPY)
|
||||
add_definitions(-DWITH_AUDASPACE_PY)
|
||||
list(APPEND LIB bf::dependencies::optional::audaspace)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_BULLET)
|
||||
add_definitions(-DWITH_BULLET)
|
||||
endif()
|
||||
|
||||
if(WITH_CODEC_SNDFILE)
|
||||
add_definitions(-DWITH_SNDFILE)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES)
|
||||
list(APPEND INC
|
||||
../../../../intern/cycles/blender
|
||||
)
|
||||
list(APPEND LIB
|
||||
bf_intern_cycles
|
||||
)
|
||||
add_definitions(-DWITH_CYCLES)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_OSL)
|
||||
add_definitions(-DWITH_CYCLES_OSL)
|
||||
endif()
|
||||
|
||||
if(WITH_CYCLES_EMBREE)
|
||||
add_definitions(-DWITH_CYCLES_EMBREE)
|
||||
endif()
|
||||
|
||||
if(WITH_FREESTYLE)
|
||||
list(APPEND INC
|
||||
../../freestyle/intern/python
|
||||
)
|
||||
add_definitions(-DWITH_FREESTYLE)
|
||||
endif()
|
||||
|
||||
if(WITH_IMAGE_CINEON)
|
||||
add_definitions(-DWITH_IMAGE_CINEON)
|
||||
endif()
|
||||
|
||||
if(WITH_IMAGE_OPENJPEG)
|
||||
add_definitions(-DWITH_IMAGE_OPENJPEG)
|
||||
endif()
|
||||
|
||||
if(WITH_IMAGE_WEBP)
|
||||
add_definitions(-DWITH_IMAGE_WEBP)
|
||||
endif()
|
||||
|
||||
if(WITH_INPUT_NDOF)
|
||||
add_definitions(-DWITH_INPUT_NDOF)
|
||||
endif()
|
||||
|
||||
if(WITH_INTERNATIONAL)
|
||||
add_definitions(-DWITH_INTERNATIONAL)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENAL)
|
||||
add_definitions(-DWITH_OPENAL)
|
||||
endif()
|
||||
|
||||
if(WITH_OPENSUBDIV)
|
||||
add_definitions(-DWITH_OPENSUBDIV)
|
||||
list(APPEND INC
|
||||
../../../../intern/opensubdiv
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_JACK)
|
||||
add_definitions(-DWITH_JACK)
|
||||
endif()
|
||||
|
||||
if(WITH_COREAUDIO)
|
||||
add_definitions(-DWITH_COREAUDIO)
|
||||
endif()
|
||||
|
||||
if(WITH_LIBMV)
|
||||
add_definitions(-DWITH_LIBMV)
|
||||
endif()
|
||||
|
||||
if(WITH_PULSEAUDIO)
|
||||
add_definitions(-DWITH_PULSEAUDIO)
|
||||
endif()
|
||||
|
||||
if(WITH_WASAPI)
|
||||
add_definitions(-DWITH_WASAPI)
|
||||
endif()
|
||||
|
||||
if(WITH_MOD_OCEANSIM)
|
||||
add_definitions(-DWITH_OCEANSIM)
|
||||
endif()
|
||||
|
||||
if(WITH_MOD_REMESH)
|
||||
add_definitions(-DWITH_MOD_REMESH)
|
||||
endif()
|
||||
|
||||
if(WITH_MOD_FLUID)
|
||||
add_definitions(-DWITH_FLUID)
|
||||
endif()
|
||||
|
||||
if(WITH_IO_WAVEFRONT_OBJ)
|
||||
add_definitions(-DWITH_IO_WAVEFRONT_OBJ)
|
||||
endif()
|
||||
|
||||
if(WITH_IO_PLY)
|
||||
add_definitions(-DWITH_IO_PLY)
|
||||
endif()
|
||||
|
||||
if(WITH_IO_STL)
|
||||
add_definitions(-DWITH_IO_STL)
|
||||
endif()
|
||||
|
||||
if(WITH_IO_FBX)
|
||||
add_definitions(-DWITH_IO_FBX)
|
||||
endif()
|
||||
|
||||
if(WITH_IO_GREASE_PENCIL)
|
||||
add_definitions(-DWITH_IO_GREASE_PENCIL)
|
||||
endif()
|
||||
|
||||
if(WITH_ALEMBIC)
|
||||
add_definitions(-DWITH_ALEMBIC)
|
||||
list(APPEND INC
|
||||
../../io/alembic
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_USD)
|
||||
add_definitions(-DWITH_USD)
|
||||
list(APPEND INC
|
||||
../../io/usd
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_XR_OPENXR)
|
||||
add_definitions(-DWITH_XR_OPENXR)
|
||||
endif()
|
||||
|
||||
if(WITH_POTRACE)
|
||||
add_definitions(-DWITH_POTRACE)
|
||||
endif()
|
||||
|
||||
if(WITH_PUGIXML)
|
||||
add_definitions(-DWITH_PUGIXML)
|
||||
endif()
|
||||
|
||||
if(WITH_HARU)
|
||||
add_definitions(-DWITH_HARU)
|
||||
endif()
|
||||
|
||||
if(WITH_SDL)
|
||||
add_definitions(-DWITH_SDL)
|
||||
endif()
|
||||
|
||||
if(WITH_HYDRA)
|
||||
list(APPEND LIB
|
||||
bf_render_hydra
|
||||
)
|
||||
add_definitions(-DWITH_HYDRA)
|
||||
endif()
|
||||
|
||||
if(WITH_EXPERIMENTAL_FEATURES)
|
||||
add_definitions(-DWITH_EXPERIMENTAL_FEATURES)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_python "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_python bf_rna)
|
||||
862
blender-5.2.0/source/blender/python/intern/bpy.cc
Normal file
862
blender-5.2.0/source/blender/python/intern/bpy.cc
Normal file
@@ -0,0 +1,862 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*
|
||||
* This file defines the '_bpy' module which is used by python's 'bpy' package
|
||||
* to access C defined builtin functions.
|
||||
* A script writer should never directly access this module.
|
||||
*/
|
||||
|
||||
/* Future-proof, See https://docs.python.org/3/c-api/arg.html#strings-and-buffers */
|
||||
#define PY_SSIZE_T_CLEAN
|
||||
|
||||
#include <Python.h>
|
||||
#include <optional>
|
||||
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_string_utils.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_bpath.hh"
|
||||
#include "BKE_global.hh" /* XXX, G_MAIN only */
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_enum_types.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "GPU_state.hh"
|
||||
|
||||
#include "WM_api.hh" /* For #WM_ghost_backend */
|
||||
|
||||
#include "bpy.hh"
|
||||
#include "bpy_app.hh"
|
||||
#include "bpy_cli_command.hh"
|
||||
#include "bpy_driver.hh"
|
||||
#include "bpy_geometry_set.hh"
|
||||
#include "bpy_inline_shader_nodes.hh"
|
||||
#include "bpy_library.hh"
|
||||
#include "bpy_operator.hh"
|
||||
#include "bpy_props.hh"
|
||||
#include "bpy_rna.hh"
|
||||
#include "bpy_rna_data.hh"
|
||||
#include "bpy_rna_gizmo.hh"
|
||||
#include "bpy_rna_id_collection.hh"
|
||||
#include "bpy_rna_types_capi.hh"
|
||||
#include "bpy_utils_previews.hh"
|
||||
#include "bpy_utils_units.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
#include "../generic/python_utildefines.hh"
|
||||
|
||||
/* external util modules */
|
||||
#include "../generic/idprop_py_api.hh"
|
||||
#include "../generic/idprop_py_ui_api.hh"
|
||||
#include "bpy_msgbus.hh"
|
||||
|
||||
#ifdef WITH_FREESTYLE
|
||||
# include "BPy_Freestyle.h"
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
PyObject *bpy_package_py = nullptr;
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_script_paths_doc,
|
||||
".. function:: script_paths()\n"
|
||||
"\n"
|
||||
" Return 2 paths to blender scripts directories.\n"
|
||||
"\n"
|
||||
" :return: (system, user) strings will be empty when not found.\n"
|
||||
" :rtype: tuple[str, str]\n");
|
||||
static PyObject *bpy_script_paths(PyObject * /*self*/)
|
||||
{
|
||||
PyObject *ret = PyTuple_New(2);
|
||||
PyObject *item;
|
||||
|
||||
std::optional<std::string> path = BKE_appdir_folder_id(BLENDER_SYSTEM_SCRIPTS, nullptr);
|
||||
item = PyC_UnicodeFromStdStr(path.value_or(""));
|
||||
BLI_assert(item != nullptr);
|
||||
PyTuple_SET_ITEM(ret, 0, item);
|
||||
path = BKE_appdir_folder_id(BLENDER_USER_SCRIPTS, nullptr);
|
||||
item = PyC_UnicodeFromStdStr(path.value_or(""));
|
||||
BLI_assert(item != nullptr);
|
||||
PyTuple_SET_ITEM(ret, 1, item);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool bpy_blend_foreach_path_cb(BPathForeachPathData *bpath_data,
|
||||
char * /*path_dst*/,
|
||||
size_t /*path_dst_maxncpy*/,
|
||||
const char *path_src)
|
||||
{
|
||||
PyObject *py_list = static_cast<PyObject *>(bpath_data->user_data);
|
||||
PyList_APPEND(py_list, PyC_UnicodeFromBytes(path_src));
|
||||
return false; /* Never edits the path. */
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_blend_paths_doc,
|
||||
".. function:: blend_paths(*, absolute=False, packed=False, local=False)\n"
|
||||
"\n"
|
||||
" Returns a list of paths to external files referenced by the loaded .blend file.\n"
|
||||
"\n"
|
||||
" :param absolute: When true the paths returned are made absolute.\n"
|
||||
" :type absolute: bool\n"
|
||||
" :param packed: When true include file paths for packed data.\n"
|
||||
" :type packed: bool\n"
|
||||
" :param local: When true skip linked library paths.\n"
|
||||
" :type local: bool\n"
|
||||
" :return: path list.\n"
|
||||
" :rtype: list[str]\n");
|
||||
static PyObject *bpy_blend_paths(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
eBPathForeachFlag flag = eBPathForeachFlag(0);
|
||||
PyObject *list;
|
||||
|
||||
bool absolute = false;
|
||||
bool packed = false;
|
||||
bool local = false;
|
||||
|
||||
static const char *_keywords[] = {"absolute", "packed", "local", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `absolute` */
|
||||
"O&" /* `packed` */
|
||||
"O&" /* `local` */
|
||||
":blend_paths",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kw,
|
||||
&_parser,
|
||||
PyC_ParseBool,
|
||||
&absolute,
|
||||
PyC_ParseBool,
|
||||
&packed,
|
||||
PyC_ParseBool,
|
||||
&local))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (absolute) {
|
||||
flag |= BKE_BPATH_FOREACH_PATH_ABSOLUTE;
|
||||
}
|
||||
if (!packed) {
|
||||
flag |= BKE_BPATH_FOREACH_PATH_SKIP_PACKED;
|
||||
}
|
||||
if (local) {
|
||||
flag |= BKE_BPATH_FOREACH_PATH_SKIP_LINKED;
|
||||
}
|
||||
|
||||
list = PyList_New(0);
|
||||
|
||||
BPathForeachPathData path_data{};
|
||||
path_data.bmain = G_MAIN;
|
||||
path_data.callback_function = bpy_blend_foreach_path_cb;
|
||||
path_data.flag = flag;
|
||||
path_data.user_data = list;
|
||||
BKE_bpath_foreach_path_main(&path_data);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_flip_name_doc,
|
||||
".. function:: flip_name(name, *, strip_digits=False)\n"
|
||||
"\n"
|
||||
" Flip a name between left/right sides, useful for \n"
|
||||
" mirroring bone names.\n"
|
||||
"\n"
|
||||
" :param name: Bone name to flip.\n"
|
||||
" :type name: str\n"
|
||||
" :param strip_digits: Whether to remove ``.###`` suffix.\n"
|
||||
" :type strip_digits: bool\n"
|
||||
" :return: The flipped name.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *bpy_flip_name(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
const char *name_src = nullptr;
|
||||
Py_ssize_t name_src_len;
|
||||
bool strip_digits = false;
|
||||
|
||||
static const char *_keywords[] = {"", "strip_digits", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"s#" /* `name` */
|
||||
"|$" /* Optional, keyword only arguments. */
|
||||
"O&" /* `strip_digits` */
|
||||
":flip_name",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &name_src, &name_src_len, PyC_ParseBool, &strip_digits))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Worst case we gain one extra byte (besides null-terminator) by changing
|
||||
* "Left" to "Right", because only the first appearance of "Left" gets replaced. */
|
||||
const size_t size = name_src_len + 2;
|
||||
char *name_dst = static_cast<char *>(PyMem_MALLOC(size));
|
||||
const size_t name_dst_len = BLI_string_flip_side_name(name_dst, name_src, strip_digits, size);
|
||||
|
||||
PyObject *result = PyUnicode_FromStringAndSize(name_dst, name_dst_len);
|
||||
|
||||
PyMem_FREE(name_dst);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* `bpy_user_resource_doc`, Now in `bpy/utils/__init__.py`. */
|
||||
static PyObject *bpy_user_resource(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
const PyC_StringEnumItems type_items[] = {
|
||||
{BLENDER_USER_DATAFILES, "DATAFILES"},
|
||||
{BLENDER_USER_CONFIG, "CONFIG"},
|
||||
{BLENDER_USER_SCRIPTS, "SCRIPTS"},
|
||||
{BLENDER_USER_EXTENSIONS, "EXTENSIONS"},
|
||||
{0, nullptr},
|
||||
};
|
||||
PyC_StringEnum type = {type_items};
|
||||
PyC_UnicodeAsBytesAndSize_Data subdir_data = {nullptr};
|
||||
|
||||
static const char *_keywords[] = {"type", "path", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O&" /* `type` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `path` */
|
||||
":user_resource",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kw,
|
||||
&_parser,
|
||||
PyC_ParseStringEnum,
|
||||
&type,
|
||||
PyC_ParseUnicodeAsBytesAndSize,
|
||||
&subdir_data))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* same logic as BKE_appdir_folder_id_create(),
|
||||
* but best leave it up to the script author to create */
|
||||
const std::optional<std::string> path = BKE_appdir_folder_id_user_notest(type.value_found,
|
||||
subdir_data.value);
|
||||
Py_XDECREF(subdir_data.value_coerce);
|
||||
|
||||
return PyC_UnicodeFromStdStr(path.value_or(""));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_system_resource_doc,
|
||||
".. function:: system_resource(type, *, path=\"\")\n"
|
||||
"\n"
|
||||
" Return a system resource path.\n"
|
||||
"\n"
|
||||
" :param type: string in ['DATAFILES', 'SCRIPTS', 'EXTENSIONS', 'PYTHON'].\n"
|
||||
" :type type: str\n"
|
||||
" :param path: Optional subdirectory.\n"
|
||||
" :type path: str | bytes\n");
|
||||
static PyObject *bpy_system_resource(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
const PyC_StringEnumItems type_items[] = {
|
||||
{BLENDER_SYSTEM_DATAFILES, "DATAFILES"},
|
||||
{BLENDER_SYSTEM_SCRIPTS, "SCRIPTS"},
|
||||
{BLENDER_SYSTEM_EXTENSIONS, "EXTENSIONS"},
|
||||
{BLENDER_SYSTEM_PYTHON, "PYTHON"},
|
||||
{0, nullptr},
|
||||
};
|
||||
PyC_StringEnum type = {type_items};
|
||||
|
||||
PyC_UnicodeAsBytesAndSize_Data subdir_data = {nullptr};
|
||||
|
||||
static const char *_keywords[] = {"type", "path", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O&" /* `type` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `path` */
|
||||
":system_resource",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kw,
|
||||
&_parser,
|
||||
PyC_ParseStringEnum,
|
||||
&type,
|
||||
PyC_ParseUnicodeAsBytesAndSize,
|
||||
&subdir_data))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::optional<std::string> path = BKE_appdir_folder_id(type.value_found, subdir_data.value);
|
||||
Py_XDECREF(subdir_data.value_coerce);
|
||||
|
||||
return PyC_UnicodeFromStdStr(path.value_or(""));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_resource_path_doc,
|
||||
".. function:: resource_path(type, *, major=None, minor=None)\n"
|
||||
"\n"
|
||||
" Return the base path for storing system files.\n"
|
||||
"\n"
|
||||
" :param type: The resource type.\n"
|
||||
" :type type: Literal['USER', 'LOCAL', 'SYSTEM', 'SYSTEM_LIBS']\n"
|
||||
" :param major: Major version. None (the default) uses ``bpy.app.version[0]``.\n"
|
||||
" :type major: int | None\n"
|
||||
" :param minor: Minor version. None (the default) uses ``bpy.app.version[1]``.\n"
|
||||
" :type minor: int | None\n"
|
||||
" :return: the resource path (not necessarily existing).\n"
|
||||
" :rtype: str\n"
|
||||
"\n"
|
||||
" .. note::\n"
|
||||
"\n"
|
||||
" ``SYSTEM_LIBS`` mirrors ``SYSTEM`` "
|
||||
"but resolves to the directory for architecture-dependent libraries "
|
||||
"(typically under ``/usr/lib/...`` rather than ``/usr/share/...``). "
|
||||
"It is an empty string on builds without a separate library directory, "
|
||||
"such as portable builds and the Python module.\n");
|
||||
static PyObject *bpy_resource_path(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
const PyC_StringEnumItems type_items[] = {
|
||||
{BLENDER_RESOURCE_PATH_USER, "USER"},
|
||||
{BLENDER_RESOURCE_PATH_LOCAL, "LOCAL"},
|
||||
{BLENDER_RESOURCE_PATH_SYSTEM, "SYSTEM"},
|
||||
{BLENDER_RESOURCE_PATH_SYSTEM_LIBS, "SYSTEM_LIBS"},
|
||||
{0, nullptr},
|
||||
};
|
||||
PyC_StringEnum type = {type_items};
|
||||
|
||||
std::optional<int> major;
|
||||
std::optional<int> minor;
|
||||
|
||||
static const char *_keywords[] = {"type", "major", "minor", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O&" /* `type` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `major` */
|
||||
"O&" /* `minor` */
|
||||
":resource_path",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kw,
|
||||
&_parser,
|
||||
PyC_ParseStringEnum,
|
||||
&type,
|
||||
PyC_ParseOptionalInt,
|
||||
&major,
|
||||
PyC_ParseOptionalInt,
|
||||
&minor))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* `None` (or missing) selects the current Blender version. */
|
||||
const int version = major.value_or(BLENDER_VERSION / 100) * 100 +
|
||||
minor.value_or(BLENDER_VERSION % 100);
|
||||
|
||||
const std::optional<std::string> path = BKE_appdir_resource_path_id_with_version(
|
||||
type.value_found, false, version);
|
||||
|
||||
return PyC_UnicodeFromStdStr(path.value_or(""));
|
||||
}
|
||||
|
||||
/* This is only exposed for tests, see: `tests/python/bl_pyapi_bpy_driver_secure_eval.py`. */
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_driver_secure_code_test_doc,
|
||||
".. function:: _driver_secure_code_test(code, *, namespace=None, verbose=False)\n"
|
||||
"\n"
|
||||
" Test if the script should be considered trusted.\n"
|
||||
"\n"
|
||||
" :param code: The code to test.\n"
|
||||
" :type code: code\n"
|
||||
" :param namespace: The namespace of values which are allowed.\n"
|
||||
" :type namespace: dict[str, Any] | None\n"
|
||||
" :param verbose: Print the reason for considering insecure to the ``stderr``.\n"
|
||||
" :type verbose: bool\n"
|
||||
" :return: True when the script is considered trusted.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *bpy_driver_secure_code_test(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
PyObject *py_code;
|
||||
PyObject *py_namespace = nullptr;
|
||||
PyC_TypeOrNone py_namespace_or_none = {&PyDict_Type, &py_namespace};
|
||||
bool verbose = false;
|
||||
static const char *_keywords[] = {"code", "namespace", "verbose", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O!" /* `expression` */
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `namespace` */
|
||||
"O&" /* `verbose` */
|
||||
":driver_secure_code_test",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args,
|
||||
kw,
|
||||
&_parser,
|
||||
&PyCode_Type,
|
||||
&py_code,
|
||||
PyC_ParseTypeOrNone,
|
||||
&py_namespace_or_none,
|
||||
PyC_ParseBool,
|
||||
&verbose))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return PyBool_FromLong(BPY_driver_secure_bytecode_test(py_code, py_namespace, verbose));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_escape_identifier_doc,
|
||||
".. function:: escape_identifier(string)\n"
|
||||
"\n"
|
||||
" Simple string escaping function used for animation paths.\n"
|
||||
"\n"
|
||||
" :param string: text\n"
|
||||
" :type string: str\n"
|
||||
" :return: The escaped string.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *bpy_escape_identifier(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
Py_ssize_t value_str_len;
|
||||
const char *value_str = PyUnicode_AsUTF8AndSize(value, &value_str_len);
|
||||
|
||||
if (value_str == nullptr) {
|
||||
PyErr_SetString(PyExc_TypeError, "expected a string");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t size = (value_str_len * 2) + 1;
|
||||
char *value_escape_str = static_cast<char *>(PyMem_MALLOC(size));
|
||||
const Py_ssize_t value_escape_str_len = BLI_str_escape(value_escape_str, value_str, size);
|
||||
|
||||
PyObject *value_escape;
|
||||
if (value_escape_str_len == value_str_len) {
|
||||
Py_INCREF(value);
|
||||
value_escape = value;
|
||||
}
|
||||
else {
|
||||
value_escape = PyUnicode_FromStringAndSize(value_escape_str, value_escape_str_len);
|
||||
}
|
||||
|
||||
PyMem_FREE(value_escape_str);
|
||||
|
||||
return value_escape;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_unescape_identifier_doc,
|
||||
".. function:: unescape_identifier(string)\n"
|
||||
"\n"
|
||||
" Simple string un-escape function used for animation paths.\n"
|
||||
" This performs the reverse of :func:`escape_identifier`.\n"
|
||||
"\n"
|
||||
" :param string: text\n"
|
||||
" :type string: str\n"
|
||||
" :return: The un-escaped string.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *bpy_unescape_identifier(PyObject * /*self*/, PyObject *value)
|
||||
{
|
||||
Py_ssize_t value_str_len;
|
||||
const char *value_str = PyUnicode_AsUTF8AndSize(value, &value_str_len);
|
||||
|
||||
if (value_str == nullptr) {
|
||||
PyErr_SetString(PyExc_TypeError, "expected a string");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t size = value_str_len + 1;
|
||||
char *value_unescape_str = static_cast<char *>(PyMem_MALLOC(size));
|
||||
const Py_ssize_t value_unescape_str_len = BLI_str_unescape(value_unescape_str, value_str, size);
|
||||
|
||||
PyObject *value_unescape;
|
||||
if (value_unescape_str_len == value_str_len) {
|
||||
Py_INCREF(value);
|
||||
value_unescape = value;
|
||||
}
|
||||
else {
|
||||
value_unescape = PyUnicode_FromStringAndSize(value_unescape_str, value_unescape_str_len);
|
||||
}
|
||||
|
||||
PyMem_FREE(value_unescape_str);
|
||||
|
||||
return value_unescape;
|
||||
}
|
||||
|
||||
extern "C" const char *buttons_context_dir[];
|
||||
extern "C" const char *clip_context_dir[];
|
||||
extern "C" const char *file_context_dir[];
|
||||
extern "C" const char *image_context_dir[];
|
||||
extern "C" const char *node_context_dir[];
|
||||
extern "C" const char *screen_context_dir[];
|
||||
extern "C" const char *sequencer_context_dir[];
|
||||
extern "C" const char *text_context_dir[];
|
||||
extern "C" const char *view3d_context_dir[];
|
||||
|
||||
/**
|
||||
* \note only exposed for generating documentation, see: `doc/python_api/sphinx_doc_gen.py`.
|
||||
*/
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_context_members_doc,
|
||||
".. function:: context_members()\n"
|
||||
"\n"
|
||||
" :return: A dict where the key is the context and the value is a tuple of it's members.\n"
|
||||
" :rtype: dict[str, tuple[str]]\n");
|
||||
static PyObject *bpy_context_members(PyObject * /*self*/)
|
||||
{
|
||||
|
||||
struct {
|
||||
const char *name;
|
||||
const char **dir;
|
||||
} context_members_all[] = {
|
||||
{"buttons", buttons_context_dir},
|
||||
{"clip", clip_context_dir},
|
||||
{"file", file_context_dir},
|
||||
{"image", image_context_dir},
|
||||
{"node", node_context_dir},
|
||||
{"screen", screen_context_dir},
|
||||
{"sequencer", sequencer_context_dir},
|
||||
{"text", text_context_dir},
|
||||
{"view3d", view3d_context_dir},
|
||||
};
|
||||
|
||||
PyObject *result = _PyDict_NewPresized(ARRAY_SIZE(context_members_all));
|
||||
for (int context_index = 0; context_index < ARRAY_SIZE(context_members_all); context_index++) {
|
||||
const char *name = context_members_all[context_index].name;
|
||||
const char **dir = context_members_all[context_index].dir;
|
||||
int i;
|
||||
for (i = 0; dir[i]; i++) {
|
||||
/* Pass. */
|
||||
}
|
||||
PyObject *members = PyTuple_New(i);
|
||||
for (i = 0; dir[i]; i++) {
|
||||
PyTuple_SET_ITEM(members, i, PyUnicode_FromString(dir[i]));
|
||||
}
|
||||
PyDict_SetItemString(result, name, members);
|
||||
Py_DECREF(members);
|
||||
}
|
||||
BLI_assert(PyDict_GET_SIZE(result) == ARRAY_SIZE(context_members_all));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \note only exposed for generating documentation, see: `doc/python_api/sphinx_doc_gen.py`.
|
||||
*/
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_rna_enum_items_static_doc,
|
||||
".. function:: rna_enum_items_static()\n"
|
||||
"\n"
|
||||
" :return: A dict where the key the name of the enum, the value is a tuple of enum items.\n"
|
||||
" :rtype: dict[str, tuple[:class:`bpy.types.EnumPropertyItem`]]\n");
|
||||
static PyObject *bpy_rna_enum_items_static(PyObject * /*self*/)
|
||||
{
|
||||
#define DEF_ENUM(id) {STRINGIFY(id), id},
|
||||
struct {
|
||||
const char *id;
|
||||
const EnumPropertyItem *items;
|
||||
} enum_info[] = {
|
||||
#include "RNA_enum_items.hh"
|
||||
};
|
||||
PyObject *result = _PyDict_NewPresized(ARRAY_SIZE(enum_info));
|
||||
for (int i = 0; i < ARRAY_SIZE(enum_info); i++) {
|
||||
/* Include all items (including headings & separators), can be shown in documentation. */
|
||||
const EnumPropertyItem *items = enum_info[i].items;
|
||||
const int items_count = RNA_enum_items_count(items);
|
||||
PyObject *value = PyTuple_New(items_count);
|
||||
for (int item_index = 0; item_index < items_count; item_index++) {
|
||||
PointerRNA ptr = RNA_pointer_create_discrete(
|
||||
nullptr, RNA_EnumPropertyItem, (void *)&items[item_index]);
|
||||
PyTuple_SET_ITEM(value, item_index, pyrna_struct_CreatePyObject(&ptr));
|
||||
}
|
||||
PyDict_SetItemString(result, enum_info[i].id, value);
|
||||
Py_DECREF(value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This is only exposed for (Unix/Linux), see: #GHOST_ISystem::getSystemBackend for details. */
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_ghost_backend_doc,
|
||||
".. function:: _ghost_backend()\n"
|
||||
"\n"
|
||||
" :return: An identifier for the GHOST back-end.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *bpy_ghost_backend(PyObject * /*self*/)
|
||||
{
|
||||
return PyUnicode_FromString(WM_ghost_backend());
|
||||
}
|
||||
|
||||
/* NOTE(@ideasman42): This is a private function because the keys in the returned dictionary,
|
||||
* are not considered stable. Sometimes a function is temporarily only supported by one platform.
|
||||
* Once all platforms support the functionality there is no need for the flag
|
||||
* and it can be removed. This is at odds with a public API that has values which are
|
||||
* intended to be kept between releases.
|
||||
* If this were to be made public we would have to document that this is subject to change. */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_wm_capabilities_doc,
|
||||
".. function:: _wm_capabilities()\n"
|
||||
"\n"
|
||||
" :return: A dictionary of capabilities (string keys, boolean values).\n"
|
||||
" :rtype: dict[str, bool]\n");
|
||||
static PyObject *bpy_wm_capabilities(PyObject *self)
|
||||
{
|
||||
PyObject *py_id_capabilities = PyUnicode_FromString("_wm_capabilities_");
|
||||
PyObject *result = nullptr;
|
||||
switch (PyObject_GetOptionalAttr(self, py_id_capabilities, &result)) {
|
||||
case 1: {
|
||||
BLI_assert(result != nullptr);
|
||||
break;
|
||||
}
|
||||
case 0: {
|
||||
result = PyDict_New();
|
||||
|
||||
const eWM_CapabilitiesFlag flag = WM_capabilities_flag();
|
||||
|
||||
#define SetFlagItem(x) \
|
||||
PyDict_SetItemString(result, STRINGIFY(x), PyBool_FromLong((WM_CAPABILITY_##x) & flag));
|
||||
|
||||
/* Only exposed flags which are used, by Blender's built-in scripts
|
||||
* since this is a private API. */
|
||||
|
||||
SetFlagItem(TRACKPAD_PHYSICAL_DIRECTION);
|
||||
SetFlagItem(KEYBOARD_HYPER_KEY);
|
||||
|
||||
#undef SetFlagItem
|
||||
PyObject_SetAttr(self, py_id_capabilities, result);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
/* Unlikely, but there may be an error, forward it. */
|
||||
BLI_assert(result == nullptr);
|
||||
break;
|
||||
}
|
||||
|
||||
Py_DECREF(py_id_capabilities);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef bpy_methods[] = {
|
||||
{"script_paths",
|
||||
reinterpret_cast<PyCFunction>(bpy_script_paths),
|
||||
METH_NOARGS,
|
||||
bpy_script_paths_doc},
|
||||
{"blend_paths",
|
||||
reinterpret_cast<PyCFunction>(bpy_blend_paths),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_blend_paths_doc},
|
||||
{"flip_name",
|
||||
reinterpret_cast<PyCFunction>(bpy_flip_name),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_flip_name_doc},
|
||||
{"user_resource",
|
||||
reinterpret_cast<PyCFunction>(bpy_user_resource),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
nullptr},
|
||||
{"system_resource",
|
||||
reinterpret_cast<PyCFunction>(bpy_system_resource),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_system_resource_doc},
|
||||
{"resource_path",
|
||||
reinterpret_cast<PyCFunction>(bpy_resource_path),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_resource_path_doc},
|
||||
{"escape_identifier",
|
||||
static_cast<PyCFunction>(bpy_escape_identifier),
|
||||
METH_O,
|
||||
bpy_escape_identifier_doc},
|
||||
{"unescape_identifier",
|
||||
static_cast<PyCFunction>(bpy_unescape_identifier),
|
||||
METH_O,
|
||||
bpy_unescape_identifier_doc},
|
||||
{"context_members",
|
||||
reinterpret_cast<PyCFunction>(bpy_context_members),
|
||||
METH_NOARGS,
|
||||
bpy_context_members_doc},
|
||||
{"rna_enum_items_static",
|
||||
reinterpret_cast<PyCFunction>(bpy_rna_enum_items_static),
|
||||
METH_NOARGS,
|
||||
bpy_rna_enum_items_static_doc},
|
||||
|
||||
/* Private functions (not part of the public API and may be removed at any time). */
|
||||
{"_driver_secure_code_test",
|
||||
reinterpret_cast<PyCFunction>(bpy_driver_secure_code_test),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_driver_secure_code_test_doc},
|
||||
{"_ghost_backend",
|
||||
reinterpret_cast<PyCFunction>(bpy_ghost_backend),
|
||||
METH_NOARGS,
|
||||
bpy_ghost_backend_doc},
|
||||
{"_wm_capabilities",
|
||||
reinterpret_cast<PyCFunction>(bpy_wm_capabilities),
|
||||
METH_NOARGS,
|
||||
bpy_wm_capabilities_doc},
|
||||
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyObject *bpy_import_test(const char *modname)
|
||||
{
|
||||
PyObject *mod = PyImport_ImportModuleLevel(modname, nullptr, nullptr, nullptr, 0);
|
||||
|
||||
if (mod) {
|
||||
Py_DECREF(mod);
|
||||
}
|
||||
else {
|
||||
PyErr_Print();
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
void BPy_init_modules(bContext *C)
|
||||
{
|
||||
PyObject *mod;
|
||||
|
||||
/* Needs to be first since this dir is needed for future modules */
|
||||
const std::optional<std::string> modpath = BKE_appdir_folder_id(BLENDER_SYSTEM_SCRIPTS,
|
||||
"modules");
|
||||
if (modpath.has_value()) {
|
||||
// printf("bpy: found module path '%s'.\n", modpath);
|
||||
PyObject *sys_path = PySys_GetObject("path"); /* borrow */
|
||||
PyObject *py_modpath = PyC_UnicodeFromStdStr(modpath.value());
|
||||
PyList_Insert(sys_path, 0, py_modpath); /* add first */
|
||||
Py_DECREF(py_modpath);
|
||||
}
|
||||
else {
|
||||
printf("bpy: couldn't find 'scripts/modules', blender probably won't start.\n");
|
||||
}
|
||||
/* stand alone utility modules not related to blender directly */
|
||||
IDProp_Init_Types(); /* not actually a submodule, just types */
|
||||
IDPropertyUIData_Init_Types();
|
||||
#ifdef WITH_FREESTYLE
|
||||
Freestyle_Init();
|
||||
#endif
|
||||
|
||||
mod = PyModule_New("_bpy");
|
||||
|
||||
/* add the module so we can import it */
|
||||
PyDict_SetItemString(PyImport_GetModuleDict(), "_bpy", mod);
|
||||
Py_DECREF(mod);
|
||||
|
||||
/* Needs to be first so `_bpy_types` can run. */
|
||||
PyObject *bpy_types = BPY_rna_types();
|
||||
PyModule_AddObject(bpy_types, "GeometrySet", BPyInit_geometry_set_type());
|
||||
PyModule_AddObject(bpy_types, "InlineShaderNodes", BPyInit_inline_shader_nodes_type());
|
||||
PyModule_AddObject(bpy_types, "BlendDataPathMeta", BPyInit_blend_data_path_meta_type());
|
||||
PyModule_AddObject(mod, "types", bpy_types);
|
||||
|
||||
/* Needs to be first so `_bpy_types` can run. */
|
||||
BPY_library_load_type_ready();
|
||||
|
||||
BPY_rna_data_context_type_ready();
|
||||
|
||||
BPY_rna_gizmo_module(mod);
|
||||
|
||||
/* Important to internalizes `_bpy_types` before creating RNA instances. */
|
||||
{
|
||||
/* Set a dummy module so the `_bpy_types.py` can access `bpy.types.ID`
|
||||
* without a null pointer dereference when instancing types. */
|
||||
PyObject *bpy_types_dict_dummy = PyDict_New();
|
||||
BPY_rna_types_dict_set(bpy_types_dict_dummy);
|
||||
PyObject *bpy_types_module_py = bpy_import_test("_bpy_types");
|
||||
/* Something has gone wrong if this is ever populated. */
|
||||
BLI_assert(PyDict_GET_SIZE(bpy_types_dict_dummy) == 0);
|
||||
Py_DECREF(bpy_types_dict_dummy);
|
||||
|
||||
PyObject *bpy_types_module_py_dict = PyModule_GetDict(bpy_types_module_py);
|
||||
BPY_rna_types_dict_set(bpy_types_module_py_dict);
|
||||
}
|
||||
PyModule_AddObject(mod, "data", BPY_rna_module());
|
||||
BPY_rna_types_finalize_external_types(bpy_types);
|
||||
|
||||
PyModule_AddObject(mod, "props", BPY_rna_props());
|
||||
PyModule_AddObject(mod, "ops", BPY_operator_module());
|
||||
PyModule_AddObject(mod, "app", BPY_app_struct());
|
||||
PyModule_AddObject(mod, "_utils_units", BPY_utils_units());
|
||||
PyModule_AddObject(mod, "_utils_previews", BPY_utils_previews_module());
|
||||
PyModule_AddObject(mod, "msgbus", BPY_msgbus_module());
|
||||
|
||||
PointerRNA ctx_ptr = RNA_pointer_create_discrete(nullptr, RNA_Context, C);
|
||||
bpy_context_module = reinterpret_cast<BPy_StructRNA *>(pyrna_struct_CreatePyObject(&ctx_ptr));
|
||||
PyModule_AddObject(mod, "context", reinterpret_cast<PyObject *>(bpy_context_module));
|
||||
|
||||
/* Register methods and property get/set for RNA types. */
|
||||
BPY_rna_types_extend_capi(bpy_types);
|
||||
|
||||
#define PYMODULE_ADD_METHOD(mod, meth) \
|
||||
PyModule_AddObject(mod, (meth)->ml_name, (PyObject *)PyCFunction_New(meth, mod))
|
||||
|
||||
for (int i = 0; bpy_methods[i].ml_name; i++) {
|
||||
PyMethodDef *m = &bpy_methods[i];
|
||||
/* Currently there is no need to support these. */
|
||||
BLI_assert((m->ml_flags & (METH_CLASS | METH_STATIC)) == 0);
|
||||
PYMODULE_ADD_METHOD(mod, m);
|
||||
}
|
||||
|
||||
/* Register functions (`bpy_rna.cc`). */
|
||||
PYMODULE_ADD_METHOD(mod, &meth_bpy_register_class);
|
||||
PYMODULE_ADD_METHOD(mod, &meth_bpy_unregister_class);
|
||||
|
||||
PYMODULE_ADD_METHOD(mod, &meth_bpy_owner_id_get);
|
||||
PYMODULE_ADD_METHOD(mod, &meth_bpy_owner_id_set);
|
||||
|
||||
/* Register command functions. */
|
||||
PYMODULE_ADD_METHOD(mod, &BPY_cli_command_register_def);
|
||||
PYMODULE_ADD_METHOD(mod, &BPY_cli_command_unregister_def);
|
||||
|
||||
#undef PYMODULE_ADD_METHOD
|
||||
|
||||
/* add our own modules dir, this is a python package */
|
||||
bpy_package_py = bpy_import_test("bpy");
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
32
blender-5.2.0/source/blender/python/intern/bpy.hh
Normal file
32
blender-5.2.0/source/blender/python/intern/bpy.hh
Normal file
@@ -0,0 +1,32 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
struct CLG_LogRef;
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct bContext;
|
||||
|
||||
/** Creates the `bpy` module and adds it to `sys.modules` for importing. */
|
||||
void BPy_init_modules(bContext *C);
|
||||
|
||||
extern PyObject *bpy_package_py;
|
||||
|
||||
/* `bpy_interface_atexit.cc` */
|
||||
|
||||
void BPY_atexit_register();
|
||||
void BPY_atexit_unregister();
|
||||
|
||||
extern CLG_LogRef *BPY_LOG_RNA;
|
||||
extern CLG_LogRef *BPY_LOG_INTERFACE;
|
||||
|
||||
} // namespace blender
|
||||
881
blender-5.2.0/source/blender/python/intern/bpy_app.cc
Normal file
881
blender-5.2.0/source/blender/python/intern/bpy_app.cc
Normal file
@@ -0,0 +1,881 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*
|
||||
* This file defines a 'PyStructSequence' accessed via 'bpy.app', mostly
|
||||
* exposing static applications variables such as version and buildinfo
|
||||
* however some writable variables have been added such as 'debug' and 'tempdir'
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "bpy_app.hh"
|
||||
|
||||
#include "bpy_app_alembic.hh"
|
||||
#include "bpy_app_build_options.hh"
|
||||
#include "bpy_app_ffmpeg.hh"
|
||||
#include "bpy_app_ocio.hh"
|
||||
#include "bpy_app_oiio.hh"
|
||||
#include "bpy_app_opensubdiv.hh"
|
||||
#include "bpy_app_openvdb.hh"
|
||||
#include "bpy_app_sdl.hh"
|
||||
|
||||
#include "bpy_app_usd.hh"
|
||||
|
||||
#include "bpy_app_translations.hh"
|
||||
|
||||
#include "bpy_app_handlers.hh"
|
||||
#include "bpy_capi_utils.hh"
|
||||
#include "bpy_driver.hh"
|
||||
|
||||
#include "BPY_extern_python.hh" /* For #BPY_python_app_help_text_fn. */
|
||||
|
||||
/* modules */
|
||||
#include "bpy_app_icons.hh"
|
||||
#include "bpy_app_timers.hh"
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_appdir.hh"
|
||||
#include "BKE_blender_version.h"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "GPU_init_exit.hh"
|
||||
#include "GPU_shader.hh"
|
||||
|
||||
#include "UI_interface_icons.hh"
|
||||
|
||||
#include "ED_undo.hh"
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "RNA_enum_types.hh" /* For `rna_enum_wm_job_type_items`. */
|
||||
|
||||
/* for notifiers */
|
||||
#include "WM_api.hh"
|
||||
#include "WM_types.hh"
|
||||
|
||||
#include "../generic/py_capi_rna.hh"
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef BUILD_DATE
|
||||
extern "C" char build_date[];
|
||||
extern "C" char build_time[];
|
||||
extern "C" ulong build_commit_timestamp;
|
||||
extern "C" char build_commit_date[];
|
||||
extern "C" char build_commit_time[];
|
||||
extern "C" char build_hash[];
|
||||
extern "C" char build_branch[];
|
||||
extern "C" char build_platform[];
|
||||
extern "C" char build_type[];
|
||||
extern "C" char build_cflags[];
|
||||
extern "C" char build_cxxflags[];
|
||||
extern "C" char build_linkflags[];
|
||||
extern "C" char build_system[];
|
||||
#endif
|
||||
|
||||
static PyTypeObject BlenderAppType;
|
||||
|
||||
static PyStructSequence_Field app_info_fields[] = {
|
||||
{"version",
|
||||
"The Blender version as a tuple of 3 numbers (major, minor, micro). eg. (4, 3, 1)\n"
|
||||
"\n"
|
||||
":type: tuple[int, int, int]\n"},
|
||||
{"version_file",
|
||||
"The Blender File version, as a tuple of 3 numbers (major, minor, file sub-version), that "
|
||||
"will be used to save a .blend file. The last item in this tuple indicates the file "
|
||||
"sub-version, which is different from the release micro version (the last item of the "
|
||||
"``bpy.app.version`` tuple). The file sub-version can be incremented multiple times while a "
|
||||
"Blender version is under development. This value is, and should be, used for handling "
|
||||
"compatibility changes between Blender versions\n"
|
||||
"\n"
|
||||
":type: tuple[int, int, int]\n"},
|
||||
{"version_string",
|
||||
"The Blender version formatted as a string\n"
|
||||
"\n"
|
||||
":type: str\n"},
|
||||
{"version_cycle",
|
||||
"The release status of this build alpha/beta/rc/release\n"
|
||||
"\n"
|
||||
":type: str\n"},
|
||||
{"background",
|
||||
"Boolean, True when blender is running without a user interface (started with -b)\n"
|
||||
"\n"
|
||||
":type: bool\n"},
|
||||
{"module",
|
||||
"Boolean, True when running Blender as a python module\n"
|
||||
"\n"
|
||||
":type: bool\n"},
|
||||
{"factory_startup",
|
||||
"Boolean, True when blender is running with --factory-startup\n"
|
||||
"\n"
|
||||
":type: bool\n"},
|
||||
{"portable",
|
||||
"Boolean, True unless blender was built to reference absolute paths (on UNIX).\n"
|
||||
"\n"
|
||||
":type: bool\n"},
|
||||
|
||||
/* buildinfo */
|
||||
{"build_date",
|
||||
"The date this Blender instance was built\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_time",
|
||||
"The time this Blender instance was built\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_commit_timestamp",
|
||||
"The unix timestamp of the commit this Blender instance was built from\n"
|
||||
"\n"
|
||||
":type: int\n"},
|
||||
{"build_commit_date",
|
||||
"The date of the commit this Blender instance was built from\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_commit_time",
|
||||
"The time of the commit this Blender instance was built from\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_hash",
|
||||
"The commit hash this Blender instance was built with\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_branch",
|
||||
"The branch this blender instance was built from\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_platform",
|
||||
"The platform this blender instance was built for\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_type",
|
||||
"The type of build (Release, Debug)\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_cflags",
|
||||
"C compiler flags\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_cxxflags",
|
||||
"C++ compiler flags\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_linkflags",
|
||||
"Binary linking flags\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
{"build_system",
|
||||
"Build system used\n"
|
||||
"\n"
|
||||
":type: bytes\n"},
|
||||
|
||||
/* submodules */
|
||||
{"alembic", "Alembic library information backend"},
|
||||
{"usd", "USD library information backend"},
|
||||
{"ffmpeg", "FFmpeg library information backend"},
|
||||
{"ocio", "OpenColorIO library information backend"},
|
||||
{"oiio", "OpenImageIO library information backend"},
|
||||
{"opensubdiv", "OpenSubdiv library information backend"},
|
||||
{"openvdb", "OpenVDB library information backend"},
|
||||
{"sdl", "SDL library information backend"},
|
||||
{"build_options", "A set containing most important enabled optional build features"},
|
||||
{"handlers", "Application handler callbacks"},
|
||||
{"translations", "Application and addons internationalization API"},
|
||||
|
||||
/* Modules (not struct sequence). */
|
||||
{"icons", "Manage custom icons"},
|
||||
{"timers", "Manage timers"},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_doc,
|
||||
"This module contains application values that remain unchanged during runtime.\n");
|
||||
static PyStructSequence_Desc app_info_desc = {
|
||||
/*name*/ "bpy.app",
|
||||
/*doc*/ bpy_app_doc,
|
||||
/*fields*/ app_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_info_fields) - 1,
|
||||
};
|
||||
|
||||
static PyObject *make_app_info()
|
||||
{
|
||||
PyObject *app_info;
|
||||
int pos = 0;
|
||||
|
||||
app_info = PyStructSequence_New(&BlenderAppType);
|
||||
if (app_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
#define SetIntItem(flag) PyStructSequence_SET_ITEM(app_info, pos++, PyLong_FromLong(flag))
|
||||
#define SetStrItem(str) PyStructSequence_SET_ITEM(app_info, pos++, PyUnicode_FromString(str))
|
||||
#define SetBytesItem(str) PyStructSequence_SET_ITEM(app_info, pos++, PyBytes_FromString(str))
|
||||
#define SetObjItem(obj) PyStructSequence_SET_ITEM(app_info, pos++, obj)
|
||||
|
||||
SetObjItem(
|
||||
PyC_Tuple_Pack_I32({BLENDER_VERSION / 100, BLENDER_VERSION % 100, BLENDER_VERSION_PATCH}));
|
||||
SetObjItem(PyC_Tuple_Pack_I32(
|
||||
{BLENDER_FILE_VERSION / 100, BLENDER_FILE_VERSION % 100, BLENDER_FILE_SUBVERSION}));
|
||||
SetStrItem(BKE_blender_version_string());
|
||||
|
||||
SetStrItem(STRINGIFY(BLENDER_VERSION_CYCLE));
|
||||
SetObjItem(PyBool_FromLong(G.background));
|
||||
#ifdef WITH_PYTHON_MODULE
|
||||
SetObjItem(Py_NewRef(Py_True));
|
||||
#else
|
||||
SetObjItem(Py_NewRef(Py_False));
|
||||
#endif
|
||||
SetObjItem(PyBool_FromLong(G.factory_startup));
|
||||
|
||||
#ifdef WITH_INSTALL_PORTABLE
|
||||
SetObjItem(Py_NewRef(Py_True));
|
||||
#else
|
||||
SetObjItem(Py_NewRef(Py_False));
|
||||
#endif
|
||||
|
||||
/* build info, use bytes since we can't assume _any_ encoding:
|
||||
* see patch #30154 for issue */
|
||||
#ifdef BUILD_DATE
|
||||
SetBytesItem(build_date);
|
||||
SetBytesItem(build_time);
|
||||
SetIntItem(build_commit_timestamp);
|
||||
SetBytesItem(build_commit_date);
|
||||
SetBytesItem(build_commit_time);
|
||||
SetBytesItem(build_hash);
|
||||
SetBytesItem(build_branch);
|
||||
SetBytesItem(build_platform);
|
||||
SetBytesItem(build_type);
|
||||
SetBytesItem(build_cflags);
|
||||
SetBytesItem(build_cxxflags);
|
||||
SetBytesItem(build_linkflags);
|
||||
SetBytesItem(build_system);
|
||||
#else
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetIntItem(0);
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
SetBytesItem("Unknown");
|
||||
#endif
|
||||
|
||||
SetObjItem(BPY_app_alembic_struct());
|
||||
SetObjItem(BPY_app_usd_struct());
|
||||
SetObjItem(BPY_app_ffmpeg_struct());
|
||||
SetObjItem(BPY_app_ocio_struct());
|
||||
SetObjItem(BPY_app_oiio_struct());
|
||||
SetObjItem(BPY_app_opensubdiv_struct());
|
||||
SetObjItem(BPY_app_openvdb_struct());
|
||||
SetObjItem(BPY_app_sdl_struct());
|
||||
SetObjItem(BPY_app_build_options_struct());
|
||||
SetObjItem(BPY_app_handlers_struct());
|
||||
SetObjItem(BPY_app_translations_struct());
|
||||
|
||||
/* modules */
|
||||
SetObjItem(BPY_app_icons_module());
|
||||
SetObjItem(BPY_app_timers_module());
|
||||
|
||||
#undef SetIntItem
|
||||
#undef SetStrItem
|
||||
#undef SetBytesItem
|
||||
#undef SetObjItem
|
||||
|
||||
if (PyErr_Occurred()) {
|
||||
Py_DECREF(app_info);
|
||||
return nullptr;
|
||||
}
|
||||
return app_info;
|
||||
}
|
||||
|
||||
/* a few getsets because it makes sense for them to be in bpy.app even though
|
||||
* they are not static */
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_debug_doc,
|
||||
"Boolean, for debug info "
|
||||
"(started with ``--debug`` / ``--debug-*`` matching this attribute name).\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
static PyObject *bpy_app_debug_get(PyObject * /*self*/, void *closure)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(closure);
|
||||
return PyBool_FromLong(G.debug & flag);
|
||||
}
|
||||
|
||||
static int bpy_app_debug_set(PyObject * /*self*/, PyObject *value, void *closure)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(closure);
|
||||
const int param = PyObject_IsTrue(value);
|
||||
|
||||
if (param == -1) {
|
||||
PyErr_SetString(PyExc_TypeError, "bpy.app.debug can only be True/False");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (param) {
|
||||
G.debug |= flag;
|
||||
}
|
||||
else {
|
||||
G.debug &= ~flag;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_internet_offline_doc,
|
||||
"Boolean, true when internet access is allowed by Blender & 3rd party scripts "
|
||||
"(read-only).\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_internet_offline_override_doc,
|
||||
"Boolean, true when internet access preference is overridden by the command line "
|
||||
"(read-only).\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_global_flag_doc,
|
||||
"Boolean, for application behavior "
|
||||
"(started with ``--enable-*`` matching this attribute name)\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_autoexec_fail_doc,
|
||||
"Boolean, True when auto-execution of scripts failed (read-only).\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_autoexec_fail_quiet_doc,
|
||||
"Boolean, True when auto-execution failure should be quiet, set after the warning is shown "
|
||||
"once for the current blend file (read-only).\n"
|
||||
"\n"
|
||||
":type: bool\n");
|
||||
|
||||
static PyObject *bpy_app_global_flag_get(PyObject * /*self*/, void *closure)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(closure);
|
||||
return PyBool_FromLong(G.f & flag);
|
||||
}
|
||||
|
||||
static int bpy_app_global_flag_set(PyObject * /*self*/, PyObject *value, void *closure)
|
||||
{
|
||||
const int flag = POINTER_AS_INT(closure);
|
||||
const int param = PyObject_IsTrue(value);
|
||||
|
||||
if (param == -1) {
|
||||
PyErr_SetString(PyExc_TypeError, "bpy.app.use_* can only be True/False");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (param) {
|
||||
G.f |= flag;
|
||||
}
|
||||
else {
|
||||
G.f &= ~flag;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bpy_app_global_flag_set__only_disable(PyObject * /*self*/,
|
||||
PyObject *value,
|
||||
void *closure)
|
||||
{
|
||||
const int param = PyObject_IsTrue(value);
|
||||
if (param == 1) {
|
||||
PyErr_SetString(PyExc_ValueError, "This bpy.app.use_* option can only be disabled");
|
||||
return -1;
|
||||
}
|
||||
return bpy_app_global_flag_set(nullptr, value, closure);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_debug_value_doc,
|
||||
"Integer value which can be set to non-zero values for testing purposes.\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *bpy_app_debug_value_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
return PyLong_FromLong(G.debug_value);
|
||||
}
|
||||
|
||||
static int bpy_app_debug_value_set(PyObject * /*self*/, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
const short param = PyC_Long_AsI16(value);
|
||||
|
||||
if (param == -1 && PyErr_Occurred()) {
|
||||
PyC_Err_SetString_Prefix(PyExc_TypeError,
|
||||
"bpy.app.debug_value can only be set to a whole number");
|
||||
return -1;
|
||||
}
|
||||
|
||||
G.debug_value = param;
|
||||
|
||||
WM_main_add_notifier(NC_WINDOW, nullptr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_tempdir_doc,
|
||||
"String, the temp directory used by blender (read-only).\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *bpy_app_tempdir_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
return PyC_UnicodeFromBytes(BKE_tempdir_session());
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_cachedir_doc,
|
||||
"String, the cache directory used by blender (read-only).\n"
|
||||
"\n"
|
||||
"In rare cases the default cache directory may not be available;\n"
|
||||
"in this case a temporary directory is used.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *bpy_app_cachedir_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
char cache_path[FILE_MAX];
|
||||
BKE_appdir_folder_caches(cache_path, sizeof(cache_path));
|
||||
BLI_assert_msg(cache_path[0], "BKE_appdir_folder_caches must never return an empty path");
|
||||
return PyC_UnicodeFromBytes(cache_path);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_driver_dict_doc,
|
||||
"Dictionary for drivers namespace, editable in-place, reset on file load (read-only).\n"
|
||||
"\n"
|
||||
":type: dict[str, Any]\n");
|
||||
static PyObject *bpy_app_driver_dict_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
if (bpy_pydriver_Dict == nullptr) {
|
||||
if (bpy_pydriver_create_dict() != 0) {
|
||||
PyErr_SetString(PyExc_RuntimeError, "bpy.app.driver_namespace failed to create dictionary");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return Py_NewRef(bpy_pydriver_Dict);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_render_icon_size_doc,
|
||||
"Reference size for icon renders (read-only).\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_render_preview_size_doc,
|
||||
"Reference size for preview renders (read-only).\n"
|
||||
"\n"
|
||||
":type: int\n");
|
||||
static PyObject *bpy_app_render_preview_or_icon_size_get(PyObject * /*self*/, void *closure)
|
||||
{
|
||||
return PyLong_FromLong(
|
||||
long(ui::icon_preview_to_render_size(eIconSizes(POINTER_AS_INT(closure)))));
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_autoexec_fail_message_doc,
|
||||
"String, message describing the auto-execution failure (read-only).\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
|
||||
static PyObject *bpy_app_autoexec_fail_message_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
return PyC_UnicodeFromBytes(G.autoexec_fail);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_python_args_doc,
|
||||
"Leading arguments to use when calling Python directly (via ``sys.executable``). "
|
||||
"These arguments match settings Blender uses to "
|
||||
"ensure Python runs with a compatible environment (read-only).\n"
|
||||
"\n"
|
||||
":type: tuple[str, ...]\n");
|
||||
static PyObject *bpy_app_python_args_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
const char *args[1];
|
||||
int args_num = 0;
|
||||
if (!BPY_python_use_system_env_get()) {
|
||||
/* Isolated Python environment. */
|
||||
args[args_num++] = "-I";
|
||||
}
|
||||
return PyC_Tuple_PackArray_String(args, args_num);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_binary_path_doc,
|
||||
"The location of Blender's executable, useful for utilities that open new instances. "
|
||||
"Read-only unless Blender is built as a Python module - in this case the value is "
|
||||
"an empty string which script authors may point to a Blender binary.\n"
|
||||
"\n"
|
||||
":type: str\n");
|
||||
static PyObject *bpy_app_binary_path_get(PyObject * /*self*/, void * /*closure*/)
|
||||
{
|
||||
return PyC_UnicodeFromBytes(BKE_appdir_program_path());
|
||||
}
|
||||
|
||||
static int bpy_app_binary_path_set(PyObject * /*self*/, PyObject *value, void * /*closure*/)
|
||||
{
|
||||
#ifndef WITH_PYTHON_MODULE
|
||||
PyErr_SetString(PyExc_AttributeError,
|
||||
"bpy.app.binary_path is only writable when built as a Python module");
|
||||
return -1;
|
||||
#endif
|
||||
PyObject *value_coerce = nullptr;
|
||||
const char *filepath = PyC_UnicodeAsBytes(value, &value_coerce);
|
||||
if (filepath == nullptr) {
|
||||
PyErr_Format(PyExc_ValueError, "expected a string or bytes, got %s", Py_TYPE(value)->tp_name);
|
||||
return -1;
|
||||
}
|
||||
BKE_appdir_program_path_init(filepath);
|
||||
Py_XDECREF(value_coerce);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyGetSetDef bpy_app_getsets[] = {
|
||||
{"debug",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG)},
|
||||
{"debug_freestyle",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_FREESTYLE)},
|
||||
{"debug_python",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_PYTHON)},
|
||||
{"debug_events",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_EVENTS)},
|
||||
{"debug_handlers",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_HANDLERS)},
|
||||
{"debug_wm",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_WM)},
|
||||
{"debug_depsgraph",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH)},
|
||||
{"debug_depsgraph_build",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH_BUILD)},
|
||||
{"debug_depsgraph_eval",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH_EVAL)},
|
||||
{"debug_depsgraph_tag",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH_TAG)},
|
||||
{"debug_depsgraph_time",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH_TIME)},
|
||||
{"debug_depsgraph_pretty",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_DEPSGRAPH_PRETTY)},
|
||||
{"debug_simdata",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_SIMDATA)},
|
||||
{"debug_io",
|
||||
bpy_app_debug_get,
|
||||
bpy_app_debug_set,
|
||||
bpy_app_debug_doc,
|
||||
reinterpret_cast<void *>(G_DEBUG_IO)},
|
||||
|
||||
{"use_event_simulate",
|
||||
bpy_app_global_flag_get,
|
||||
bpy_app_global_flag_set__only_disable,
|
||||
bpy_app_global_flag_doc,
|
||||
reinterpret_cast<void *>(G_FLAG_EVENT_SIMULATE)},
|
||||
|
||||
{"use_userpref_skip_save_on_exit",
|
||||
bpy_app_global_flag_get,
|
||||
bpy_app_global_flag_set,
|
||||
bpy_app_global_flag_doc,
|
||||
reinterpret_cast<void *>(G_FLAG_USERPREF_NO_SAVE_ON_EXIT)},
|
||||
|
||||
{"debug_value",
|
||||
bpy_app_debug_value_get,
|
||||
bpy_app_debug_value_set,
|
||||
bpy_app_debug_value_doc,
|
||||
nullptr},
|
||||
{"tempdir", bpy_app_tempdir_get, nullptr, bpy_app_tempdir_doc, nullptr},
|
||||
{"cachedir", bpy_app_cachedir_get, nullptr, bpy_app_cachedir_doc, nullptr},
|
||||
{"driver_namespace", bpy_app_driver_dict_get, nullptr, bpy_app_driver_dict_doc, nullptr},
|
||||
|
||||
{"render_icon_size",
|
||||
bpy_app_render_preview_or_icon_size_get,
|
||||
nullptr,
|
||||
bpy_app_render_icon_size_doc,
|
||||
reinterpret_cast<void *>(ICON_SIZE_ICON)},
|
||||
{"render_preview_size",
|
||||
bpy_app_render_preview_or_icon_size_get,
|
||||
nullptr,
|
||||
bpy_app_render_preview_size_doc,
|
||||
reinterpret_cast<void *>(ICON_SIZE_PREVIEW)},
|
||||
|
||||
{"online_access",
|
||||
bpy_app_global_flag_get,
|
||||
nullptr,
|
||||
bpy_app_internet_offline_doc,
|
||||
reinterpret_cast<void *>(G_FLAG_INTERNET_ALLOW)},
|
||||
{"online_access_override",
|
||||
bpy_app_global_flag_get,
|
||||
nullptr,
|
||||
bpy_app_internet_offline_override_doc,
|
||||
reinterpret_cast<void *> G_FLAG_INTERNET_OVERRIDE_PREF_ANY},
|
||||
|
||||
/* security */
|
||||
{"autoexec_fail",
|
||||
bpy_app_global_flag_get,
|
||||
nullptr,
|
||||
bpy_app_autoexec_fail_doc,
|
||||
reinterpret_cast<void *>(G_FLAG_SCRIPT_AUTOEXEC_FAIL)},
|
||||
{"autoexec_fail_quiet",
|
||||
bpy_app_global_flag_get,
|
||||
nullptr,
|
||||
bpy_app_autoexec_fail_quiet_doc,
|
||||
reinterpret_cast<void *>(G_FLAG_SCRIPT_AUTOEXEC_FAIL_QUIET)},
|
||||
{"autoexec_fail_message",
|
||||
bpy_app_autoexec_fail_message_get,
|
||||
nullptr,
|
||||
bpy_app_autoexec_fail_message_doc,
|
||||
nullptr},
|
||||
|
||||
{"python_args", bpy_app_python_args_get, nullptr, bpy_app_python_args_doc, nullptr},
|
||||
|
||||
/* Support script authors setting the Blender binary path to use, otherwise this value
|
||||
* is not known when built as a Python module. */
|
||||
{"binary_path",
|
||||
bpy_app_binary_path_get,
|
||||
bpy_app_binary_path_set,
|
||||
bpy_app_binary_path_doc,
|
||||
nullptr},
|
||||
|
||||
{nullptr, nullptr, nullptr, nullptr, nullptr},
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_is_job_running_doc,
|
||||
".. function:: is_job_running(job_type)\n"
|
||||
"\n"
|
||||
" Check whether a job of the given type is running.\n"
|
||||
"\n"
|
||||
" :param job_type: job type in :ref:`rna_enum_wm_job_type_items`.\n"
|
||||
" :type job_type: str\n"
|
||||
" :return: Whether a job of the given type is currently running.\n"
|
||||
" :rtype: bool\n");
|
||||
static PyObject *bpy_app_is_job_running(PyObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
BPy_EnumProperty_Parse job_type_enum{};
|
||||
job_type_enum.items = rna_enum_wm_job_type_items;
|
||||
job_type_enum.value = 0;
|
||||
|
||||
static const char *_keywords[] = {"job_type", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O&" /* `job_type` */
|
||||
":is_job_running",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kwds, &_parser, pyrna_enum_value_parse_string, &job_type_enum))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(G_MAIN->wm.first);
|
||||
if (job_type_enum.value == WM_JOB_TYPE_SHADER_COMPILATION) {
|
||||
/* Shader compilation no longer uses the WM_job API, so we handle this as a special case
|
||||
* to avoid breaking the Python API. */
|
||||
return PyBool_FromLong(GPU_is_init() && GPU_shader_compiler_has_pending_work());
|
||||
}
|
||||
return PyBool_FromLong(WM_jobs_has_running_type(wm, job_type_enum.value));
|
||||
}
|
||||
|
||||
char *(*BPY_python_app_help_text_fn)(bool all) = nullptr;
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_help_text_doc,
|
||||
".. function:: help_text(*, all=False)\n"
|
||||
"\n"
|
||||
" Return the help text as a string.\n"
|
||||
"\n"
|
||||
" :param all: Return all arguments, "
|
||||
"even those which aren't available for the current platform.\n"
|
||||
" :type all: bool\n"
|
||||
" :return: Help text.\n"
|
||||
" :rtype: str\n");
|
||||
static PyObject *bpy_app_help_text(PyObject * /*self*/, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
bool all = false;
|
||||
static const char *_keywords[] = {"all", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"|$" /* Optional keyword only arguments. */
|
||||
"O&" /* `all` */
|
||||
":help_text",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, PyC_ParseBool, &all)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char *buf = BPY_python_app_help_text_fn(all);
|
||||
PyObject *result = PyUnicode_FromString(buf);
|
||||
MEM_delete(buf);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_memory_usage_undo_doc,
|
||||
".. function:: memory_usage_undo()\n"
|
||||
"\n"
|
||||
" Get undo memory usage information.\n"
|
||||
"\n"
|
||||
" :return: Memory usage of the undo stack in bytes.\n"
|
||||
" :rtype: int\n");
|
||||
|
||||
static PyObject *bpy_app_memory_usage_undo(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
size_t total_memory = 0;
|
||||
UndoStack *ustack = ED_undo_stack_get();
|
||||
if (ustack) {
|
||||
total_memory = ED_undosys_total_memory_calc(ustack);
|
||||
}
|
||||
return PyLong_FromSize_t(total_memory);
|
||||
}
|
||||
|
||||
static PyMethodDef bpy_app_methods[] = {
|
||||
{"is_job_running",
|
||||
reinterpret_cast<PyCFunction>(bpy_app_is_job_running),
|
||||
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
|
||||
bpy_app_is_job_running_doc},
|
||||
{"help_text",
|
||||
reinterpret_cast<PyCFunction>(bpy_app_help_text),
|
||||
METH_VARARGS | METH_KEYWORDS | METH_STATIC,
|
||||
bpy_app_help_text_doc},
|
||||
{"memory_usage_undo",
|
||||
static_cast<PyCFunction>(bpy_app_memory_usage_undo),
|
||||
METH_NOARGS | METH_STATIC,
|
||||
bpy_app_memory_usage_undo_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static void py_struct_seq_getset_init()
|
||||
{
|
||||
/* tricky dynamic members, not to py-spec! */
|
||||
for (PyGetSetDef *getset = bpy_app_getsets; getset->name; getset++) {
|
||||
PyObject *item = PyDescr_NewGetSet(&BlenderAppType, getset);
|
||||
PyDict_SetItem(BlenderAppType.tp_dict, PyDescr_NAME(item), item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
static void py_struct_seq_method_init()
|
||||
{
|
||||
for (PyMethodDef *method = bpy_app_methods; method->ml_name; method++) {
|
||||
BLI_assert_msg(method->ml_flags & METH_STATIC, "Only static methods make sense for 'bpy.app'");
|
||||
PyObject *item = PyCFunction_New(method, nullptr);
|
||||
PyDict_SetItemString(BlenderAppType.tp_dict, method->ml_name, item);
|
||||
Py_DECREF(item);
|
||||
}
|
||||
}
|
||||
|
||||
/* end dynamic bpy.app */
|
||||
|
||||
PyObject *BPY_app_struct()
|
||||
{
|
||||
PyObject *ret;
|
||||
|
||||
PyStructSequence_InitType(&BlenderAppType, &app_info_desc);
|
||||
|
||||
ret = make_app_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppType.tp_init = nullptr;
|
||||
BlenderAppType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
/* Kind of a hack on top of #PyStructSequence. */
|
||||
py_struct_seq_getset_init();
|
||||
py_struct_seq_method_init();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/intern/bpy_app.hh
Normal file
17
blender-5.2.0/source/blender/python/intern/bpy_app.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_struct();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,97 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "bpy_app_alembic.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
# include "ABC_alembic.h"
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
static PyTypeObject BlenderAppABCType;
|
||||
|
||||
static PyStructSequence_Field app_alembic_info_fields[] = {
|
||||
{"supported", "Boolean, True when Blender is built with Alembic support"},
|
||||
{"version", "The Alembic version as a tuple of 3 numbers"},
|
||||
{"version_string", "The Alembic version formatted as a string"},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
static PyStructSequence_Desc app_alembic_info_desc = {
|
||||
/*name*/ "bpy.app.alembic",
|
||||
/*doc*/ "This module contains information about Alembic blender is linked against",
|
||||
/*fields*/ app_alembic_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_alembic_info_fields) - 1,
|
||||
};
|
||||
|
||||
static PyObject *make_alembic_info()
|
||||
{
|
||||
PyObject *alembic_info = PyStructSequence_New(&BlenderAppABCType);
|
||||
|
||||
if (alembic_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
|
||||
#ifndef WITH_ALEMBIC
|
||||
# define SetStrItem(str) PyStructSequence_SET_ITEM(alembic_info, pos++, PyUnicode_FromString(str))
|
||||
#endif
|
||||
|
||||
#define SetObjItem(obj) PyStructSequence_SET_ITEM(alembic_info, pos++, obj)
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
const int curversion = ABC_get_version();
|
||||
const int major = curversion / 10000;
|
||||
const int minor = (curversion / 100) - (major * 100);
|
||||
const int patch = curversion - ((curversion / 100) * 100);
|
||||
|
||||
SetObjItem(PyBool_FromLong(1));
|
||||
SetObjItem(PyC_Tuple_Pack_I32({major, minor, patch}));
|
||||
SetObjItem(PyUnicode_FromFormat("%2d, %2d, %2d", major, minor, patch));
|
||||
#else
|
||||
SetObjItem(PyBool_FromLong(0));
|
||||
SetObjItem(PyC_Tuple_Pack_I32({0, 0, 0}));
|
||||
SetStrItem("Unknown");
|
||||
#endif
|
||||
|
||||
if (UNLIKELY(PyErr_Occurred())) {
|
||||
Py_DECREF(alembic_info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#undef SetStrItem
|
||||
#undef SetObjItem
|
||||
|
||||
return alembic_info;
|
||||
}
|
||||
|
||||
PyObject *BPY_app_alembic_struct()
|
||||
{
|
||||
PyStructSequence_InitType(&BlenderAppABCType, &app_alembic_info_desc);
|
||||
|
||||
PyObject *ret = make_alembic_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppABCType.tp_init = nullptr;
|
||||
BlenderAppABCType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppABCType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2016 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_alembic_struct();
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,356 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "bpy_app_build_options.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static PyTypeObject BlenderAppBuildOptionsType;
|
||||
|
||||
static PyStructSequence_Field app_builtopts_info_fields[] = {
|
||||
/* names mostly follow CMake options, lowercase, after `WITH_` */
|
||||
{"bullet", nullptr},
|
||||
{"codec_avi", nullptr},
|
||||
{"codec_ffmpeg", nullptr},
|
||||
{"codec_sndfile", nullptr},
|
||||
{"compositor_cpu", nullptr},
|
||||
{"cycles", nullptr},
|
||||
{"cycles_osl", nullptr},
|
||||
{"freestyle", nullptr},
|
||||
{"image_cineon", nullptr},
|
||||
{"image_dds", nullptr},
|
||||
{"image_hdr", nullptr},
|
||||
{"image_openexr", nullptr},
|
||||
{"image_openjpeg", nullptr},
|
||||
{"image_tiff", nullptr},
|
||||
{"image_webp", nullptr},
|
||||
{"input_ndof", nullptr},
|
||||
{"audaspace", nullptr},
|
||||
{"international", nullptr},
|
||||
{"openal", nullptr},
|
||||
{"opensubdiv", nullptr},
|
||||
{"sdl", nullptr},
|
||||
{"coreaudio", nullptr},
|
||||
{"jack", nullptr},
|
||||
{"pulseaudio", nullptr},
|
||||
{"wasapi", nullptr},
|
||||
{"libmv", nullptr},
|
||||
{"mod_oceansim", nullptr},
|
||||
{"mod_remesh", nullptr},
|
||||
{"io_wavefront_obj", nullptr},
|
||||
{"io_ply", nullptr},
|
||||
{"io_stl", nullptr},
|
||||
{"io_fbx", nullptr},
|
||||
{"io_gpencil", nullptr},
|
||||
{"opencolorio", nullptr},
|
||||
{"openmp", nullptr},
|
||||
{"openvdb", nullptr},
|
||||
{"alembic", nullptr},
|
||||
{"usd", nullptr},
|
||||
{"fluid", nullptr},
|
||||
{"xr_openxr", nullptr},
|
||||
{"potrace", nullptr},
|
||||
{"pugixml", nullptr},
|
||||
{"haru", nullptr},
|
||||
{"experimental_features", nullptr},
|
||||
/* Sentinel (this line prevents `clang-format` wrapping into columns). */
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
static PyStructSequence_Desc app_builtopts_info_desc = {
|
||||
/*name*/ "bpy.app.build_options",
|
||||
/*doc*/ "This module contains information about options blender is built with",
|
||||
/*fields*/ app_builtopts_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_builtopts_info_fields) - 1,
|
||||
};
|
||||
|
||||
static PyObject *make_builtopts_info()
|
||||
{
|
||||
PyObject *builtopts_info;
|
||||
int pos = 0;
|
||||
|
||||
builtopts_info = PyStructSequence_New(&BlenderAppBuildOptionsType);
|
||||
if (builtopts_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define SetObjIncref(item) \
|
||||
PyStructSequence_SET_ITEM(builtopts_info, pos++, (Py_IncRef(item), item))
|
||||
|
||||
#ifdef WITH_BULLET
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
/* AVI */
|
||||
SetObjIncref(Py_False);
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SNDFILE
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
/* Compositor. */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
#ifdef WITH_CYCLES
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_CYCLES_OSL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_FREESTYLE
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IMAGE_CINEON
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
/* DDS */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
/* HDR */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
/* OpenEXR */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
#ifdef WITH_IMAGE_OPENJPEG
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
/* TIFF */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
#ifdef WITH_IMAGE_WEBP
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_INPUT_NDOF
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_AUDASPACE
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_INTERNATIONAL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENAL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENSUBDIV
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_SDL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_COREAUDIO
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_JACK
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PULSEAUDIO
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_WASAPI
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_LIBMV
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OCEANSIM
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_MOD_REMESH
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IO_WAVEFRONT_OBJ
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IO_PLY
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IO_STL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IO_FBX
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_IO_GREASE_PENCIL
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
/* OpenColorIO */
|
||||
SetObjIncref(Py_True);
|
||||
|
||||
#ifdef _OPENMP
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_ALEMBIC
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_USD
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_FLUID
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_XR_OPENXR
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_POTRACE
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_PUGIXML
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_HARU
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#ifdef WITH_EXPERIMENTAL_FEATURES
|
||||
SetObjIncref(Py_True);
|
||||
#else
|
||||
SetObjIncref(Py_False);
|
||||
#endif
|
||||
|
||||
#undef SetObjIncref
|
||||
|
||||
return builtopts_info;
|
||||
}
|
||||
|
||||
PyObject *BPY_app_build_options_struct()
|
||||
{
|
||||
PyObject *ret;
|
||||
|
||||
PyStructSequence_InitType(&BlenderAppBuildOptionsType, &app_builtopts_info_desc);
|
||||
|
||||
ret = make_builtopts_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppBuildOptionsType.tp_init = nullptr;
|
||||
BlenderAppBuildOptionsType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppBuildOptionsType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_build_options_struct();
|
||||
|
||||
} // namespace blender
|
||||
142
blender-5.2.0/source/blender/python/intern/bpy_app_ffmpeg.cc
Normal file
142
blender-5.2.0/source/blender/python/intern/bpy_app_ffmpeg.cc
Normal file
@@ -0,0 +1,142 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "bpy_app_ffmpeg.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
extern "C" {
|
||||
# include <libavcodec/avcodec.h>
|
||||
# include <libavdevice/avdevice.h>
|
||||
# include <libavformat/avformat.h>
|
||||
# include <libavutil/avutil.h>
|
||||
# include <libswscale/swscale.h>
|
||||
}
|
||||
#endif
|
||||
|
||||
static PyTypeObject BlenderAppFFmpegType;
|
||||
|
||||
#define DEF_FFMPEG_LIB_VERSION(lib) \
|
||||
{(#lib "_version"), ("The " #lib " version as a tuple of 3 numbers")}, \
|
||||
{ \
|
||||
(#lib "_version_string"), ("The " #lib " version formatted as a string") \
|
||||
}
|
||||
|
||||
static PyStructSequence_Field app_ffmpeg_info_fields[] = {
|
||||
{"supported", "Boolean, True when Blender is built with FFmpeg support"},
|
||||
|
||||
DEF_FFMPEG_LIB_VERSION(avcodec),
|
||||
DEF_FFMPEG_LIB_VERSION(avdevice),
|
||||
DEF_FFMPEG_LIB_VERSION(avformat),
|
||||
DEF_FFMPEG_LIB_VERSION(avutil),
|
||||
DEF_FFMPEG_LIB_VERSION(swscale),
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
#undef DEF_FFMPEG_LIB_VERSION
|
||||
|
||||
static PyStructSequence_Desc app_ffmpeg_info_desc = {
|
||||
/*name*/ "bpy.app.ffmpeg",
|
||||
/*doc*/ "This module contains information about FFmpeg blender is linked against",
|
||||
/*fields*/ app_ffmpeg_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_ffmpeg_info_fields) - 1,
|
||||
};
|
||||
|
||||
static PyObject *make_ffmpeg_info()
|
||||
{
|
||||
PyObject *ffmpeg_info;
|
||||
int pos = 0;
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
int curversion;
|
||||
#endif
|
||||
|
||||
ffmpeg_info = PyStructSequence_New(&BlenderAppFFmpegType);
|
||||
if (ffmpeg_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if 0 /* UNUSED */
|
||||
# define SetIntItem(flag) PyStructSequence_SET_ITEM(ffmpeg_info, pos++, PyLong_FromLong(flag))
|
||||
#endif
|
||||
#ifndef WITH_FFMPEG
|
||||
# define SetStrItem(str) PyStructSequence_SET_ITEM(ffmpeg_info, pos++, PyUnicode_FromString(str))
|
||||
#endif
|
||||
#define SetObjItem(obj) PyStructSequence_SET_ITEM(ffmpeg_info, pos++, obj)
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
# define FFMPEG_LIB_VERSION(lib) \
|
||||
{ \
|
||||
curversion = lib##_version(); \
|
||||
SetObjItem( \
|
||||
PyC_Tuple_Pack_I32({curversion >> 16, (curversion >> 8) % 256, curversion % 256})); \
|
||||
SetObjItem(PyUnicode_FromFormat( \
|
||||
"%2d, %2d, %2d", curversion >> 16, (curversion >> 8) % 256, curversion % 256)); \
|
||||
} \
|
||||
(void)0
|
||||
#else
|
||||
# define FFMPEG_LIB_VERSION(lib) \
|
||||
{ \
|
||||
SetStrItem("Unknown"); \
|
||||
SetStrItem("Unknown"); \
|
||||
} \
|
||||
(void)0
|
||||
#endif
|
||||
|
||||
#ifdef WITH_FFMPEG
|
||||
SetObjItem(PyBool_FromLong(1));
|
||||
#else
|
||||
SetObjItem(PyBool_FromLong(0));
|
||||
#endif
|
||||
|
||||
FFMPEG_LIB_VERSION(avcodec);
|
||||
FFMPEG_LIB_VERSION(avdevice);
|
||||
FFMPEG_LIB_VERSION(avformat);
|
||||
FFMPEG_LIB_VERSION(avutil);
|
||||
FFMPEG_LIB_VERSION(swscale);
|
||||
|
||||
#undef FFMPEG_LIB_VERSION
|
||||
|
||||
if (UNLIKELY(PyErr_Occurred())) {
|
||||
Py_DECREF(ffmpeg_info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// #undef SetIntItem
|
||||
#undef SetStrItem
|
||||
#undef SetObjItem
|
||||
|
||||
return ffmpeg_info;
|
||||
}
|
||||
|
||||
PyObject *BPY_app_ffmpeg_struct()
|
||||
{
|
||||
PyObject *ret;
|
||||
|
||||
PyStructSequence_InitType(&BlenderAppFFmpegType, &app_ffmpeg_info_desc);
|
||||
|
||||
ret = make_ffmpeg_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppFFmpegType.tp_init = nullptr;
|
||||
BlenderAppFFmpegType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppFFmpegType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/intern/bpy_app_ffmpeg.hh
Normal file
17
blender-5.2.0/source/blender/python/intern/bpy_app_ffmpeg.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_ffmpeg_struct();
|
||||
|
||||
} // namespace blender
|
||||
499
blender-5.2.0/source/blender/python/intern/bpy_app_handlers.cc
Normal file
499
blender-5.2.0/source/blender/python/intern/bpy_app_handlers.cc
Normal file
@@ -0,0 +1,499 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*
|
||||
* This file defines a #PyStructSequence accessed via `bpy.app.handlers`,
|
||||
* which exposes various lists that the script author can add callback
|
||||
* functions into (called via blenders generic BLI_cb API)
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "BKE_callbacks.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
|
||||
#include "bpy_app_handlers.hh"
|
||||
#include "bpy_rna.hh"
|
||||
|
||||
#include "BPY_extern.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void bpy_app_generic_callback(Main *main,
|
||||
PointerRNA **pointers,
|
||||
const int pointers_num,
|
||||
void *arg);
|
||||
|
||||
static PyTypeObject BlenderAppCbType;
|
||||
|
||||
#define FILEPATH_SAVE_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the file being saved, an empty string for the startup-file."
|
||||
#define PYDOC_FILEPATH_SAVE_TYPE "\n\n:type: list[Callable[[str], None]]"
|
||||
|
||||
#define FILEPATH_LOAD_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the file being loaded, an empty string for the startup-file."
|
||||
#define PYDOC_FILEPATH_LOAD_TYPE "\n\n:type: list[Callable[[str], None]]"
|
||||
|
||||
#define RENDER_STATS_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the render progress as a string containing current frame, current sample, render time and " \
|
||||
"saving time."
|
||||
#define PYDOC_RENDER_STATS_TYPE "\n\n:type: list[Callable[[str], None]]"
|
||||
|
||||
#define DEPSGRAPH_UPDATE_ARG \
|
||||
"Accepts one or two arguments: " \
|
||||
"The scene data-block, and optionally the dependency graph being updated"
|
||||
#define PYDOC_DEPSGRAPH_UPDATE_TYPE \
|
||||
"\n\n:type: list[" \
|
||||
"Callable[[bpy.types.Scene, bpy.types.Depsgraph], None] | " \
|
||||
"Callable[[bpy.types.Scene], None]" \
|
||||
"]"
|
||||
|
||||
#define RENDER_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the scene data-block being rendered"
|
||||
#define PYDOC_RENDER_TYPE "\n\n:type: list[Callable[[bpy.types.Scene], None]]"
|
||||
|
||||
#define OBJECT_BAKE_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the object data-block being baked"
|
||||
#define PYDOC_OBJECT_BAKE_TYPE "\n\n:type: list[Callable[[bpy.types.Object], None]]"
|
||||
|
||||
#define COMPOSITE_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the scene data-block"
|
||||
#define PYDOC_COMPOSITE_TYPE "\n\n:type: list[Callable[[bpy.types.Scene], None]]"
|
||||
|
||||
#define ANNOTATION_ARG \
|
||||
"Accepts two arguments: " \
|
||||
"the annotation data-block and dependency graph"
|
||||
#define PYDOC_ANNOTATION_TYPE \
|
||||
"\n\n:type: list[Callable[[bpy.types.GreasePencil, bpy.types.Depsgraph], None]]"
|
||||
|
||||
#define BLENDIMPORT_ARG \
|
||||
"Accepts one argument: " \
|
||||
"a BlendImportContext"
|
||||
#define PYDOC_BLENDIMPORT_TYPE "\n\n:type: list[Callable[[bpy.types.BlendImportContext], None]]"
|
||||
|
||||
#define SCENE_ARG \
|
||||
"Accepts one argument: " \
|
||||
"the scene data-block"
|
||||
#define PYDOC_SCENE_TYPE "\n\n:type: list[Callable[[bpy.types.Scene], None]]"
|
||||
#define PYDOC_HANDLER_TYPE_NONE "\n\n:type: list[Callable[[], None]]"
|
||||
#define PYDOC_HANDLER_TYPE_BOOL "\n\n:type: list[Callable[[bool], None]]"
|
||||
|
||||
/**
|
||||
* See `BKE_callbacks.hh` #eCbEvent declaration for the policy on naming.
|
||||
*/
|
||||
static PyStructSequence_Field app_cb_info_fields[] = {
|
||||
{"frame_change_pre",
|
||||
"Called when a frame change is triggered for playback and rendering, "
|
||||
"before any data is evaluated for the new frame. "
|
||||
"This makes it possible to change data and relations (for example swap an object "
|
||||
"to another mesh) for the new frame. Note that this handler is **not** to be used as 'before "
|
||||
"the frame changes' event. The dependency graph is not available in this handler, as data "
|
||||
"and relations may have been altered and the dependency graph has not yet been updated for "
|
||||
"that. " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"frame_change_post",
|
||||
"Called after frame change for playback and rendering, after the data has been evaluated "
|
||||
"for the new frame. " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"render_pre", "on render (before). " RENDER_ARG PYDOC_RENDER_TYPE},
|
||||
{"render_post", "on render (after). " RENDER_ARG PYDOC_RENDER_TYPE},
|
||||
{"render_write",
|
||||
"on writing a render frame (directly after the frame is written). " RENDER_ARG
|
||||
PYDOC_RENDER_TYPE},
|
||||
{"render_stats", "on printing render statistics. " RENDER_STATS_ARG PYDOC_RENDER_STATS_TYPE},
|
||||
{"render_init", "on initialization of a render job. " RENDER_ARG PYDOC_RENDER_TYPE},
|
||||
{"render_complete", "on completion of render job. " RENDER_ARG PYDOC_RENDER_TYPE},
|
||||
{"render_cancel", "on canceling a render job. " RENDER_ARG PYDOC_RENDER_TYPE},
|
||||
|
||||
{"load_pre",
|
||||
"on loading a new blend file (before). " FILEPATH_LOAD_ARG PYDOC_FILEPATH_LOAD_TYPE},
|
||||
{"load_post",
|
||||
"on loading a new blend file (after). " FILEPATH_LOAD_ARG PYDOC_FILEPATH_LOAD_TYPE},
|
||||
{"load_post_fail",
|
||||
"on failure to load a new blend file (after). " FILEPATH_LOAD_ARG PYDOC_FILEPATH_LOAD_TYPE},
|
||||
|
||||
{"save_pre", "on saving a blend file (before). " FILEPATH_SAVE_ARG PYDOC_FILEPATH_SAVE_TYPE},
|
||||
{"save_post", "on saving a blend file (after). " FILEPATH_SAVE_ARG PYDOC_FILEPATH_SAVE_TYPE},
|
||||
{"save_post_fail",
|
||||
"on failure to save a blend file (after). " FILEPATH_SAVE_ARG PYDOC_FILEPATH_SAVE_TYPE},
|
||||
|
||||
{"undo_pre", "on loading an undo step (before). " SCENE_ARG PYDOC_SCENE_TYPE},
|
||||
{"undo_post", "on loading an undo step (after). " SCENE_ARG PYDOC_SCENE_TYPE},
|
||||
{"redo_pre", "on loading a redo step (before). " SCENE_ARG PYDOC_SCENE_TYPE},
|
||||
{"redo_post", "on loading a redo step (after). " SCENE_ARG PYDOC_SCENE_TYPE},
|
||||
{"depsgraph_update_pre",
|
||||
"on depsgraph update (pre). " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"depsgraph_update_post",
|
||||
"on depsgraph update (post). " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"version_update", "on ending the versioning code" PYDOC_HANDLER_TYPE_NONE},
|
||||
{"load_factory_preferences_post",
|
||||
"on loading factory preferences (after)" PYDOC_HANDLER_TYPE_NONE},
|
||||
{"load_factory_startup_post", "on loading factory startup (after)" PYDOC_HANDLER_TYPE_NONE},
|
||||
{"xr_session_start_pre", "on starting an xr session (before)" PYDOC_HANDLER_TYPE_NONE},
|
||||
{"annotation_pre", "on drawing an annotation (before). " ANNOTATION_ARG PYDOC_ANNOTATION_TYPE},
|
||||
{"annotation_post", "on drawing an annotation (after). " ANNOTATION_ARG PYDOC_ANNOTATION_TYPE},
|
||||
{"object_bake_pre", "before starting a bake job. " OBJECT_BAKE_ARG PYDOC_OBJECT_BAKE_TYPE},
|
||||
{"object_bake_complete",
|
||||
"on completing a bake job; will be called in the main "
|
||||
"thread. " OBJECT_BAKE_ARG PYDOC_OBJECT_BAKE_TYPE},
|
||||
{"object_bake_cancel",
|
||||
"on canceling a bake job; will be called in the main "
|
||||
"thread. " OBJECT_BAKE_ARG PYDOC_OBJECT_BAKE_TYPE},
|
||||
{"composite_pre",
|
||||
"on a compositing background job (before). " COMPOSITE_ARG PYDOC_COMPOSITE_TYPE},
|
||||
{"composite_post",
|
||||
"on a compositing background job (after). " COMPOSITE_ARG PYDOC_COMPOSITE_TYPE},
|
||||
{"composite_cancel",
|
||||
"on a compositing background job (cancel). " COMPOSITE_ARG PYDOC_COMPOSITE_TYPE},
|
||||
{"animation_playback_pre",
|
||||
"on starting animation playback. " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"animation_playback_post",
|
||||
"on ending animation playback. " DEPSGRAPH_UPDATE_ARG PYDOC_DEPSGRAPH_UPDATE_TYPE},
|
||||
{"translation_update_post", "on translation settings update" PYDOC_HANDLER_TYPE_NONE},
|
||||
/* NOTE(@ideasman42): This avoids bad-level calls into BPY API
|
||||
* but should not be considered part of the public Python API.
|
||||
* If there is a compelling reason to make these public, the leading `_` can be removed. */
|
||||
{"_extension_repos_update_pre", "on changes to extension repos (before)"},
|
||||
{"_extension_repos_update_post", "on changes to extension repos (after)"},
|
||||
{"_extension_repos_sync", "on creating or synchronizing the active repository"},
|
||||
{"_extension_repos_files_clear",
|
||||
"remove files from the repository directory (uses as a string argument)"},
|
||||
{"blend_import_pre",
|
||||
"on linking or appending data (before). " BLENDIMPORT_ARG PYDOC_BLENDIMPORT_TYPE},
|
||||
{"blend_import_post",
|
||||
"on linking or appending data (after). " BLENDIMPORT_ARG PYDOC_BLENDIMPORT_TYPE},
|
||||
{"exit_pre",
|
||||
"just before Blender shuts down, while all data is still valid. "
|
||||
"Accepts one boolean argument. True indicates either that a user has been using Blender and "
|
||||
"exited, or that Blender is exiting in a circumstance that should be treated as if that were "
|
||||
"the case. False indicates that Blender is running in background mode, or is exiting due to "
|
||||
"failed command line arguments, etc." PYDOC_HANDLER_TYPE_BOOL},
|
||||
|
||||
/* sets the permanent tag */
|
||||
#define APP_CB_OTHER_FIELDS 1
|
||||
{"persistent",
|
||||
"Function decorator for callback functions not to be removed when loading new files"
|
||||
"\n\n:type: type"},
|
||||
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
static PyStructSequence_Desc app_cb_info_desc = {
|
||||
/*name*/ "bpy.app.handlers",
|
||||
/*doc*/ "This module contains callback lists",
|
||||
/*fields*/ app_cb_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_cb_info_fields) - 1,
|
||||
};
|
||||
|
||||
#if 0
|
||||
# if (BKE_CB_EVT_TOT != ARRAY_SIZE(app_cb_info_fields))
|
||||
# error "Callbacks are out of sync"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Permanent Tagging Code
|
||||
* \{ */
|
||||
|
||||
#define PERMINENT_CB_ID "_bpy_persistent"
|
||||
|
||||
static PyObject *bpy_app_handlers_persistent_new(PyTypeObject * /*type*/,
|
||||
PyObject *args,
|
||||
PyObject * /*kwds*/)
|
||||
{
|
||||
PyObject *value;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O:bpy.app.handlers.persistent", &value)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (PyFunction_Check(value)) {
|
||||
PyObject **dict_ptr = _PyObject_GetDictPtr(value);
|
||||
if (dict_ptr == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"bpy.app.handlers.persistent wasn't able to "
|
||||
"get the dictionary from the function passed");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* set id */
|
||||
if (*dict_ptr == nullptr) {
|
||||
*dict_ptr = PyDict_New();
|
||||
}
|
||||
|
||||
PyDict_SetItemString(*dict_ptr, PERMINENT_CB_ID, Py_None);
|
||||
|
||||
Py_INCREF(value);
|
||||
return value;
|
||||
}
|
||||
|
||||
PyErr_SetString(PyExc_ValueError, "bpy.app.handlers.persistent expected a function");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** Dummy type because decorators can't be a #PyCFunction. */
|
||||
static PyTypeObject BPyPersistent_Type = {
|
||||
#if defined(_MSC_VER)
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(nullptr, 0)
|
||||
#else
|
||||
/*ob_base*/ PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
#endif
|
||||
/*tp_name*/ "persistent",
|
||||
/*tp_basicsize*/ 0,
|
||||
/*tp_itemsize*/ 0,
|
||||
/*tp_dealloc*/ nullptr,
|
||||
/*tp_vectorcall_offset*/ 0,
|
||||
/*tp_getattr*/ nullptr,
|
||||
/*tp_setattr*/ nullptr,
|
||||
/*tp_as_async*/ nullptr,
|
||||
/*tp_repr*/ nullptr,
|
||||
/*tp_as_number*/ nullptr,
|
||||
/*tp_as_sequence*/ nullptr,
|
||||
/*tp_as_mapping*/ nullptr,
|
||||
/*tp_hash*/ nullptr,
|
||||
/*tp_call*/ nullptr,
|
||||
/*tp_str*/ nullptr,
|
||||
/*tp_getattro*/ nullptr,
|
||||
/*tp_setattro*/ nullptr,
|
||||
/*tp_as_buffer*/ nullptr,
|
||||
/*tp_flags*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
|
||||
/*tp_doc*/ nullptr,
|
||||
/*tp_traverse*/ nullptr,
|
||||
/*tp_clear*/ nullptr,
|
||||
/*tp_richcompare*/ nullptr,
|
||||
/*tp_weaklistoffset*/ 0,
|
||||
/*tp_iter*/ nullptr,
|
||||
/*tp_iternext*/ nullptr,
|
||||
/*tp_methods*/ nullptr,
|
||||
/*tp_members*/ nullptr,
|
||||
/*tp_getset*/ nullptr,
|
||||
/*tp_base*/ nullptr,
|
||||
/*tp_dict*/ nullptr,
|
||||
/*tp_descr_get*/ nullptr,
|
||||
/*tp_descr_set*/ nullptr,
|
||||
/*tp_dictoffset*/ 0,
|
||||
/*tp_init*/ nullptr,
|
||||
/*tp_alloc*/ nullptr,
|
||||
/*tp_new*/ bpy_app_handlers_persistent_new,
|
||||
/*tp_free*/ nullptr,
|
||||
/*tp_is_gc*/ nullptr,
|
||||
/*tp_bases*/ nullptr,
|
||||
/*tp_mro*/ nullptr,
|
||||
/*tp_cache*/ nullptr,
|
||||
/*tp_subclasses*/ nullptr,
|
||||
/*tp_weaklist*/ nullptr,
|
||||
/*tp_del*/ nullptr,
|
||||
/*tp_version_tag*/ 0,
|
||||
/*tp_finalize*/ nullptr,
|
||||
/*tp_vectorcall*/ nullptr,
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
static PyObject *py_cb_array[BKE_CB_EVT_TOT] = {nullptr};
|
||||
|
||||
static PyObject *make_app_cb_info()
|
||||
{
|
||||
PyObject *app_cb_info;
|
||||
int pos;
|
||||
|
||||
app_cb_info = PyStructSequence_New(&BlenderAppCbType);
|
||||
if (app_cb_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
|
||||
if (app_cb_info_fields[pos].name == nullptr) {
|
||||
Py_FatalError("invalid callback slots 1");
|
||||
}
|
||||
PyStructSequence_SET_ITEM(app_cb_info, pos, (py_cb_array[pos] = PyList_New(0)));
|
||||
}
|
||||
if (app_cb_info_fields[pos + APP_CB_OTHER_FIELDS].name != nullptr) {
|
||||
Py_FatalError("invalid callback slots 2");
|
||||
}
|
||||
|
||||
/* custom function */
|
||||
PyStructSequence_SET_ITEM(app_cb_info, pos++, (PyObject *)&BPyPersistent_Type);
|
||||
|
||||
return app_cb_info;
|
||||
}
|
||||
|
||||
PyObject *BPY_app_handlers_struct()
|
||||
{
|
||||
PyObject *ret;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
BPyPersistent_Type.ob_base.ob_base.ob_type = &PyType_Type;
|
||||
#endif
|
||||
|
||||
if (PyType_Ready(&BPyPersistent_Type) < 0) {
|
||||
BLI_assert_msg(0, "error initializing 'bpy.app.handlers.persistent'");
|
||||
}
|
||||
|
||||
PyStructSequence_InitType(&BlenderAppCbType, &app_cb_info_desc);
|
||||
|
||||
ret = make_app_cb_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppCbType.tp_init = nullptr;
|
||||
BlenderAppCbType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppCbType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
/* assign the C callbacks */
|
||||
if (ret) {
|
||||
static bCallbackFuncStore funcstore_array[BKE_CB_EVT_TOT] = {{nullptr}};
|
||||
bCallbackFuncStore *funcstore;
|
||||
int pos = 0;
|
||||
|
||||
for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
|
||||
funcstore = &funcstore_array[pos];
|
||||
funcstore->func = bpy_app_generic_callback;
|
||||
funcstore->alloc = 0;
|
||||
funcstore->arg = POINTER_FROM_INT(pos);
|
||||
BKE_callback_add(funcstore, eCbEvent(pos));
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void BPY_app_handlers_reset(const bool do_all)
|
||||
{
|
||||
PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
int pos = 0;
|
||||
|
||||
if (do_all) {
|
||||
for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
|
||||
/* clear list */
|
||||
PyList_SetSlice(py_cb_array[pos], 0, PY_SSIZE_T_MAX, nullptr);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* save string conversion thrashing */
|
||||
PyObject *perm_id_str = PyUnicode_FromString(PERMINENT_CB_ID);
|
||||
|
||||
for (pos = 0; pos < BKE_CB_EVT_TOT; pos++) {
|
||||
/* clear only items without PERMINENT_CB_ID */
|
||||
PyObject *ls = py_cb_array[pos];
|
||||
Py_ssize_t i;
|
||||
|
||||
for (i = PyList_GET_SIZE(ls) - 1; i >= 0; i--) {
|
||||
PyObject *item = PyList_GET_ITEM(ls, i);
|
||||
|
||||
if (PyMethod_Check(item)) {
|
||||
PyObject *item_test = PyMethod_GET_FUNCTION(item);
|
||||
if (item_test) {
|
||||
item = item_test;
|
||||
}
|
||||
}
|
||||
|
||||
PyObject **dict_ptr;
|
||||
if (PyFunction_Check(item) && (dict_ptr = _PyObject_GetDictPtr(item)) && (*dict_ptr) &&
|
||||
(PyDict_GetItem(*dict_ptr, perm_id_str) != nullptr))
|
||||
{
|
||||
/* keep */
|
||||
}
|
||||
else {
|
||||
/* remove */
|
||||
// PySequence_DelItem(ls, i); /* more obvious but slower */
|
||||
PyList_SetSlice(ls, i, i + 1, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(perm_id_str);
|
||||
}
|
||||
|
||||
PyGILState_Release(gilstate);
|
||||
}
|
||||
|
||||
static PyObject *choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single)
|
||||
{
|
||||
if (!PyFunction_Check(func)) {
|
||||
return args_all;
|
||||
}
|
||||
PyCodeObject *code = reinterpret_cast<PyCodeObject *>(PyFunction_GetCode(func));
|
||||
if (code->co_argcount == 1) {
|
||||
return args_single;
|
||||
}
|
||||
return args_all;
|
||||
}
|
||||
|
||||
/* the actual callback - not necessarily called from py */
|
||||
void bpy_app_generic_callback(Main * /*main*/,
|
||||
PointerRNA **pointers,
|
||||
const int pointers_num,
|
||||
void *arg)
|
||||
{
|
||||
PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)];
|
||||
if (PyList_GET_SIZE(cb_list) > 0) {
|
||||
const PyGILState_STATE gilstate = PyGILState_Ensure();
|
||||
|
||||
const int num_arguments = 2;
|
||||
PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */
|
||||
PyObject *args_single = PyTuple_New(1);
|
||||
PyObject *func;
|
||||
PyObject *ret;
|
||||
Py_ssize_t pos;
|
||||
|
||||
/* setup arguments */
|
||||
for (int i = 0; i < pointers_num; ++i) {
|
||||
PyTuple_SET_ITEM(
|
||||
args_all, i, pyrna_struct_CreatePyObject_with_primitive_support(pointers[i]));
|
||||
}
|
||||
for (int i = pointers_num; i < num_arguments; ++i) {
|
||||
PyTuple_SET_ITEM(args_all, i, Py_NewRef(Py_None));
|
||||
}
|
||||
|
||||
if (pointers_num == 0) {
|
||||
PyTuple_SET_ITEM(args_single, 0, Py_NewRef(Py_None));
|
||||
}
|
||||
else {
|
||||
PyTuple_SET_ITEM(
|
||||
args_single, 0, pyrna_struct_CreatePyObject_with_primitive_support(pointers[0]));
|
||||
}
|
||||
|
||||
/* Iterate the list and run the callbacks
|
||||
* NOTE: don't store the list size since the scripts may remove themselves. */
|
||||
for (pos = 0; pos < PyList_GET_SIZE(cb_list); pos++) {
|
||||
func = PyList_GET_ITEM(cb_list, pos);
|
||||
PyObject *args = choose_arguments(func, args_all, args_single);
|
||||
ret = PyObject_Call(func, args, nullptr);
|
||||
if (ret == nullptr) {
|
||||
/* Don't set last system variables because they might cause some
|
||||
* dangling pointers to external render engines (when exception
|
||||
* happens during rendering) which will break logic of render pipeline
|
||||
* which expects to be the only user of render engine when rendering
|
||||
* is finished. */
|
||||
|
||||
/* Note the handler called, the exception itself typically has the function name. */
|
||||
PySys_WriteStderr("Error in bpy.app.handlers.%s[%d]:\n",
|
||||
app_cb_info_fields[POINTER_AS_INT(arg)].name,
|
||||
int(pos));
|
||||
PyErr_PrintEx(0);
|
||||
}
|
||||
else {
|
||||
Py_DECREF(ret);
|
||||
}
|
||||
}
|
||||
|
||||
Py_DECREF(args_all);
|
||||
Py_DECREF(args_single);
|
||||
|
||||
PyGILState_Release(gilstate);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_handlers_struct();
|
||||
|
||||
} // namespace blender
|
||||
219
blender-5.2.0/source/blender/python/intern/bpy_app_icons.cc
Normal file
219
blender-5.2.0/source/blender/python/intern/bpy_app_icons.cc
Normal file
@@ -0,0 +1,219 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*
|
||||
* Runtime defined icons.
|
||||
*/
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BKE_icons.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "bpy_app_icons.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* We may want to load direct from file. */
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_icons_new_triangles_doc,
|
||||
".. function:: new_triangles(range, coords, colors)\n"
|
||||
"\n"
|
||||
" Create a new icon from triangle geometry.\n"
|
||||
"\n"
|
||||
" :param range: Pair of ints.\n"
|
||||
" :type range: tuple[int, int]\n"
|
||||
" :param coords: Sequence of bytes (6 floats for one triangle) for (X, Y) coordinates.\n"
|
||||
" :type coords: bytes\n"
|
||||
" :param colors: Sequence of bytes (12 for one triangle) for RGBA.\n"
|
||||
" :type colors: bytes\n"
|
||||
" :return: Unique icon value (pass to interface ``icon_value`` argument).\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *bpy_app_icons_new_triangles(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
/* bytes */
|
||||
uchar coords_range[2];
|
||||
PyObject *py_coords, *py_colors;
|
||||
|
||||
static const char *_keywords[] = {"range", "coords", "colors", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"(BB)" /* `range` */
|
||||
"S" /* `coords` */
|
||||
"S" /* `colors` */
|
||||
":new_triangles",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, &coords_range[0], &coords_range[1], &py_coords, &py_colors))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const int coords_len = PyBytes_GET_SIZE(py_coords);
|
||||
const int tris_len = coords_len / 6;
|
||||
if (tris_len * 6 != coords_len) {
|
||||
PyErr_SetString(PyExc_ValueError, "coords must be multiple of 6");
|
||||
return nullptr;
|
||||
}
|
||||
if (PyBytes_GET_SIZE(py_colors) != 2 * coords_len) {
|
||||
PyErr_SetString(PyExc_ValueError, "colors must be twice size of coords");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const size_t items_num = size_t(tris_len) * 3;
|
||||
uchar(*coords)[2] = MEM_new_array_uninitialized<uchar[2]>(items_num, __func__);
|
||||
uchar(*colors)[4] = MEM_new_array_uninitialized<uchar[4]>(items_num, __func__);
|
||||
|
||||
memcpy(coords, PyBytes_AS_STRING(py_coords), sizeof(*coords) * items_num);
|
||||
memcpy(colors, PyBytes_AS_STRING(py_colors), sizeof(*colors) * items_num);
|
||||
|
||||
Icon_Geom *geom = MEM_new_uninitialized<Icon_Geom>(__func__);
|
||||
geom->coords_len = tris_len;
|
||||
geom->coords_range[0] = coords_range[0];
|
||||
geom->coords_range[1] = coords_range[1];
|
||||
geom->coords = coords;
|
||||
geom->colors = colors;
|
||||
geom->icon_id = 0;
|
||||
geom->mem = nullptr;
|
||||
const int icon_id = BKE_icon_geom_ensure(geom);
|
||||
return PyLong_FromLong(icon_id);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_icons_new_triangles_from_file_doc,
|
||||
".. function:: new_triangles_from_file(filepath)\n"
|
||||
"\n"
|
||||
" Create a new icon from triangle geometry.\n"
|
||||
"\n"
|
||||
" :param filepath: File path.\n"
|
||||
" :type filepath: str | bytes\n"
|
||||
" :return: Unique icon value (pass to interface ``icon_value`` argument).\n"
|
||||
" :rtype: int\n");
|
||||
static PyObject *bpy_app_icons_new_triangles_from_file(PyObject * /*self*/,
|
||||
PyObject *args,
|
||||
PyObject *kw)
|
||||
{
|
||||
PyC_UnicodeAsBytesAndSize_Data filepath_data = {nullptr};
|
||||
|
||||
static const char *_keywords[] = {"filepath", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"O&" /* `filepath` */
|
||||
":new_triangles_from_file",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(
|
||||
args, kw, &_parser, PyC_ParseUnicodeAsBytesAndSize, &filepath_data))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Icon_Geom *geom = BKE_icon_geom_from_file(filepath_data.value);
|
||||
Py_XDECREF(filepath_data.value_coerce);
|
||||
|
||||
if (geom == nullptr) {
|
||||
PyErr_SetString(PyExc_ValueError, "Unable to load from file");
|
||||
return nullptr;
|
||||
}
|
||||
const int icon_id = BKE_icon_geom_ensure(geom);
|
||||
return PyLong_FromLong(icon_id);
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(
|
||||
/* Wrap. */
|
||||
bpy_app_icons_release_doc,
|
||||
".. function:: release(icon_id)\n"
|
||||
"\n"
|
||||
" Release the icon.\n"
|
||||
"\n"
|
||||
" :param icon_id: The icon id to release.\n"
|
||||
" :type icon_id: int\n");
|
||||
static PyObject *bpy_app_icons_release(PyObject * /*self*/, PyObject *args, PyObject *kw)
|
||||
{
|
||||
int icon_id;
|
||||
static const char *_keywords[] = {"icon_id", nullptr};
|
||||
static _PyArg_Parser _parser = {
|
||||
"i" /* `icon_id` */
|
||||
":release",
|
||||
_keywords,
|
||||
nullptr,
|
||||
};
|
||||
if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, &icon_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!BKE_icon_delete_unmanaged(icon_id)) {
|
||||
PyErr_SetString(PyExc_ValueError, "invalid icon_id");
|
||||
return nullptr;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wcast-function-type"
|
||||
# else
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wcast-function-type"
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyMethodDef M_AppIcons_methods[] = {
|
||||
{"new_triangles",
|
||||
reinterpret_cast<PyCFunction>(bpy_app_icons_new_triangles),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_app_icons_new_triangles_doc},
|
||||
{"new_triangles_from_file",
|
||||
reinterpret_cast<PyCFunction>(bpy_app_icons_new_triangles_from_file),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_app_icons_new_triangles_from_file_doc},
|
||||
{"release",
|
||||
reinterpret_cast<PyCFunction>(bpy_app_icons_release),
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
bpy_app_icons_release_doc},
|
||||
{nullptr, nullptr, 0, nullptr},
|
||||
};
|
||||
|
||||
#ifdef __GNUC__
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
# else
|
||||
# pragma GCC diagnostic pop
|
||||
# endif
|
||||
#endif
|
||||
|
||||
static PyModuleDef M_AppIcons_module_def = {
|
||||
/*m_base*/ PyModuleDef_HEAD_INIT,
|
||||
/*m_name*/ "bpy.app.icons",
|
||||
/*m_doc*/ nullptr,
|
||||
/*m_size*/ 0,
|
||||
/*m_methods*/ M_AppIcons_methods,
|
||||
/*m_slots*/ nullptr,
|
||||
/*m_traverse*/ nullptr,
|
||||
/*m_clear*/ nullptr,
|
||||
/*m_free*/ nullptr,
|
||||
};
|
||||
|
||||
PyObject *BPY_app_icons_module()
|
||||
{
|
||||
PyObject *sys_modules = PyImport_GetModuleDict();
|
||||
|
||||
PyObject *mod = PyModule_Create(&M_AppIcons_module_def);
|
||||
|
||||
PyC_Module_AddToSysModules(sys_modules, mod);
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/intern/bpy_app_icons.hh
Normal file
17
blender-5.2.0/source/blender/python/intern/bpy_app_icons.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_icons_module();
|
||||
|
||||
} // namespace blender
|
||||
83
blender-5.2.0/source/blender/python/intern/bpy_app_ocio.cc
Normal file
83
blender-5.2.0/source/blender/python/intern/bpy_app_ocio.cc
Normal file
@@ -0,0 +1,83 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
#include <Python.h>
|
||||
|
||||
#include "../generic/python_compat.hh" /* IWYU pragma: keep. */
|
||||
|
||||
#include "bpy_app_ocio.hh"
|
||||
|
||||
#include "../generic/py_capi_utils.hh"
|
||||
|
||||
#include "OCIO_version.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static PyTypeObject BlenderAppOCIOType;
|
||||
|
||||
static PyStructSequence_Field app_ocio_info_fields[] = {
|
||||
{"supported", "Boolean, True when Blender is built with OpenColorIO support"},
|
||||
{"version", "The OpenColorIO version as a tuple of 3 numbers"},
|
||||
{"version_string", "The OpenColorIO version formatted as a string"},
|
||||
{nullptr},
|
||||
};
|
||||
|
||||
static PyStructSequence_Desc app_ocio_info_desc = {
|
||||
/*name*/ "bpy.app.ocio",
|
||||
/*doc*/ "This module contains information about OpenColorIO blender is linked against",
|
||||
/*fields*/ app_ocio_info_fields,
|
||||
/*n_in_sequence*/ ARRAY_SIZE(app_ocio_info_fields) - 1,
|
||||
};
|
||||
|
||||
static PyObject *make_ocio_info()
|
||||
{
|
||||
PyObject *ocio_info;
|
||||
int pos = 0;
|
||||
|
||||
ocio_info = PyStructSequence_New(&BlenderAppOCIOType);
|
||||
if (ocio_info == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#define SetObjItem(obj) PyStructSequence_SET_ITEM(ocio_info, pos++, obj)
|
||||
|
||||
const ocio::Version ocio_version = ocio::get_version();
|
||||
SetObjItem(PyBool_FromLong(1));
|
||||
SetObjItem(PyC_Tuple_Pack_I32({ocio_version.major, ocio_version.minor, ocio_version.patch}));
|
||||
SetObjItem(PyUnicode_FromFormat(
|
||||
"%2d, %2d, %2d", ocio_version.major, ocio_version.minor, ocio_version.patch));
|
||||
|
||||
if (UNLIKELY(PyErr_Occurred())) {
|
||||
Py_DECREF(ocio_info);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#undef SetObjItem
|
||||
|
||||
return ocio_info;
|
||||
}
|
||||
|
||||
PyObject *BPY_app_ocio_struct()
|
||||
{
|
||||
PyObject *ret;
|
||||
|
||||
PyStructSequence_InitType(&BlenderAppOCIOType, &app_ocio_info_desc);
|
||||
|
||||
ret = make_ocio_info();
|
||||
|
||||
/* prevent user from creating new instances */
|
||||
BlenderAppOCIOType.tp_init = nullptr;
|
||||
BlenderAppOCIOType.tp_new = nullptr;
|
||||
/* Without this we can't do `set(sys.modules)` #29635. */
|
||||
BlenderAppOCIOType.tp_hash = reinterpret_cast<hashfunc>(Py_HashPointer);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
17
blender-5.2.0/source/blender/python/intern/bpy_app_ocio.hh
Normal file
17
blender-5.2.0/source/blender/python/intern/bpy_app_ocio.hh
Normal file
@@ -0,0 +1,17 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup pythonintern
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
namespace blender {
|
||||
|
||||
[[nodiscard]] PyObject *BPY_app_ocio_struct();
|
||||
|
||||
} // namespace blender
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user