diff --git a/tests/test_animation_deformers.py b/tests/test_animation_deformers.py new file mode 100644 index 0000000..42a0715 --- /dev/null +++ b/tests/test_animation_deformers.py @@ -0,0 +1,132 @@ +""" +Test animation and deformer API functionality +""" + +import ufbx + + +def test_scene_has_animation_properties(): + """Test that Scene has animation-related properties""" + assert hasattr(ufbx.Scene, 'anim_stacks') + assert hasattr(ufbx.Scene, 'anim_curves') + + +def test_scene_has_deformer_properties(): + """Test that Scene has deformer-related properties""" + assert hasattr(ufbx.Scene, 'skin_deformers') + assert hasattr(ufbx.Scene, 'blend_deformers') + assert hasattr(ufbx.Scene, 'blend_shapes') + assert hasattr(ufbx.Scene, 'constraints') + + +def test_anim_stack_class_has_properties(): + """Test that AnimStack class has expected properties""" + expected_properties = ['name', 'time_begin', 'time_end', 'layers'] + for prop in expected_properties: + assert hasattr(ufbx.AnimStack, prop), f"AnimStack missing property: {prop}" + + +def test_anim_layer_class_has_properties(): + """Test that AnimLayer class has expected properties""" + expected_properties = [ + 'name', 'weight', 'weight_is_animated', 'blended', + 'additive', 'compose_rotation', 'compose_scale' + ] + for prop in expected_properties: + assert hasattr(ufbx.AnimLayer, prop), f"AnimLayer missing property: {prop}" + + +def test_anim_curve_class_has_properties(): + """Test that AnimCurve class has expected properties""" + expected_properties = [ + 'name', 'num_keyframes', 'min_value', 'max_value', + 'min_time', 'max_time' + ] + for prop in expected_properties: + assert hasattr(ufbx.AnimCurve, prop), f"AnimCurve missing property: {prop}" + + +def test_skin_deformer_class_has_properties(): + """Test that SkinDeformer class has expected properties""" + expected_properties = ['name', 'clusters'] + for prop in expected_properties: + assert hasattr(ufbx.SkinDeformer, prop), f"SkinDeformer missing property: {prop}" + + +def test_skin_cluster_class_has_properties(): + """Test that SkinCluster class has expected properties""" + expected_properties = ['name', 'num_weights'] + for prop in expected_properties: + assert hasattr(ufbx.SkinCluster, prop), f"SkinCluster missing property: {prop}" + + +def test_blend_deformer_class_has_properties(): + """Test that BlendDeformer class has expected properties""" + expected_properties = ['name', 'channels'] + for prop in expected_properties: + assert hasattr(ufbx.BlendDeformer, prop), f"BlendDeformer missing property: {prop}" + + +def test_blend_channel_class_has_properties(): + """Test that BlendChannel class has expected properties""" + expected_properties = ['name', 'weight'] + for prop in expected_properties: + assert hasattr(ufbx.BlendChannel, prop), f"BlendChannel missing property: {prop}" + + +def test_blend_shape_class_has_properties(): + """Test that BlendShape class has expected properties""" + expected_properties = ['name', 'num_offsets'] + for prop in expected_properties: + assert hasattr(ufbx.BlendShape, prop), f"BlendShape missing property: {prop}" + + +def test_constraint_class_has_properties(): + """Test that Constraint class has expected properties""" + expected_properties = ['name', 'type', 'weight', 'active'] + for prop in expected_properties: + assert hasattr(ufbx.Constraint, prop), f"Constraint missing property: {prop}" + + +def test_all_animation_deformer_classes_are_elements(): + """Test that all animation/deformer classes are subclasses of Element""" + assert issubclass(ufbx.AnimStack, ufbx.Element) + assert issubclass(ufbx.AnimLayer, ufbx.Element) + assert issubclass(ufbx.AnimCurve, ufbx.Element) + assert issubclass(ufbx.SkinDeformer, ufbx.Element) + assert issubclass(ufbx.SkinCluster, ufbx.Element) + assert issubclass(ufbx.BlendDeformer, ufbx.Element) + assert issubclass(ufbx.BlendChannel, ufbx.Element) + assert issubclass(ufbx.BlendShape, ufbx.Element) + assert issubclass(ufbx.Constraint, ufbx.Element) + + +if __name__ == "__main__": + import sys + + test_functions = [ + test_scene_has_animation_properties, + test_scene_has_deformer_properties, + test_anim_stack_class_has_properties, + test_anim_layer_class_has_properties, + test_anim_curve_class_has_properties, + test_skin_deformer_class_has_properties, + test_skin_cluster_class_has_properties, + test_blend_deformer_class_has_properties, + test_blend_channel_class_has_properties, + test_blend_shape_class_has_properties, + test_constraint_class_has_properties, + test_all_animation_deformer_classes_are_elements, + ] + + failed = 0 + for test_func in test_functions: + try: + test_func() + print(f"✓ {test_func.__name__}") + except Exception as e: + print(f"✗ {test_func.__name__}: {e}") + failed += 1 + + print(f"\n{len(test_functions) - failed}/{len(test_functions)} tests passed") + sys.exit(0 if failed == 0 else 1) diff --git a/tests/test_new_api.py b/tests/test_new_api.py new file mode 100644 index 0000000..9492f24 --- /dev/null +++ b/tests/test_new_api.py @@ -0,0 +1,95 @@ +""" +Test new API functionality for Light, Camera, Bone, and Texture +""" + +import ufbx + + +def test_scene_has_new_properties(): + """Test that Scene has the new properties""" + # We can't test with real FBX data without files, but we can verify + # the properties exist and are callable + assert hasattr(ufbx.Scene, 'lights') + assert hasattr(ufbx.Scene, 'cameras') + assert hasattr(ufbx.Scene, 'bones') + assert hasattr(ufbx.Scene, 'textures') + + +def test_node_has_new_properties(): + """Test that Node has the new properties""" + assert hasattr(ufbx.Node, 'light') + assert hasattr(ufbx.Node, 'camera') + assert hasattr(ufbx.Node, 'bone') + + +def test_light_class_has_properties(): + """Test that Light class has expected properties""" + expected_properties = [ + 'name', 'color', 'intensity', 'local_direction', + 'type', 'decay', 'area_shape', 'inner_angle', + 'outer_angle', 'cast_light', 'cast_shadows' + ] + for prop in expected_properties: + assert hasattr(ufbx.Light, prop), f"Light missing property: {prop}" + + +def test_camera_class_has_properties(): + """Test that Camera class has expected properties""" + expected_properties = [ + 'name', 'projection_mode', 'resolution', 'resolution_is_pixels', + 'field_of_view_deg', 'field_of_view_tan', 'orthographic_extent', + 'orthographic_size', 'aspect_ratio', 'near_plane', 'far_plane' + ] + for prop in expected_properties: + assert hasattr(ufbx.Camera, prop), f"Camera missing property: {prop}" + + +def test_bone_class_has_properties(): + """Test that Bone class has expected properties""" + expected_properties = ['name', 'radius', 'relative_length', 'is_root'] + for prop in expected_properties: + assert hasattr(ufbx.Bone, prop), f"Bone missing property: {prop}" + + +def test_texture_class_has_properties(): + """Test that Texture class has expected properties""" + expected_properties = [ + 'name', 'filename', 'absolute_filename', + 'relative_filename', 'type' + ] + for prop in expected_properties: + assert hasattr(ufbx.Texture, prop), f"Texture missing property: {prop}" + + +def test_light_camera_bone_are_elements(): + """Test that Light, Camera, and Bone are subclasses of Element""" + assert issubclass(ufbx.Light, ufbx.Element) + assert issubclass(ufbx.Camera, ufbx.Element) + assert issubclass(ufbx.Bone, ufbx.Element) + assert issubclass(ufbx.Texture, ufbx.Element) + + +if __name__ == "__main__": + import sys + + test_functions = [ + test_scene_has_new_properties, + test_node_has_new_properties, + test_light_class_has_properties, + test_camera_class_has_properties, + test_bone_class_has_properties, + test_texture_class_has_properties, + test_light_camera_bone_are_elements, + ] + + failed = 0 + for test_func in test_functions: + try: + test_func() + print(f"✓ {test_func.__name__}") + except Exception as e: + print(f"✗ {test_func.__name__}: {e}") + failed += 1 + + print(f"\n{len(test_functions) - failed}/{len(test_functions)} tests passed") + sys.exit(0 if failed == 0 else 1) diff --git a/ufbx/__init__.pyi b/ufbx/__init__.pyi index 8943b5c..9558726 100644 --- a/ufbx/__init__.pyi +++ b/ufbx/__init__.pyi @@ -185,20 +185,158 @@ class Transform: def to_matrix(self) -> Matrix: ... class Element: ... -class Light(Element): ... -class Camera(Element): ... -class Bone(Element): ... -class Texture(Element): ... + +class Light(Element): + @property + def name(self) -> str: ... + @property + def color(self) -> Vec3: ... + @property + def intensity(self) -> float: ... + @property + def local_direction(self) -> Vec3: ... + @property + def type(self) -> LightType: ... + @property + def decay(self) -> LightDecay: ... + @property + def area_shape(self) -> LightAreaShape: ... + @property + def inner_angle(self) -> float: ... + @property + def outer_angle(self) -> float: ... + @property + def cast_light(self) -> bool: ... + @property + def cast_shadows(self) -> bool: ... + +class Camera(Element): + @property + def name(self) -> str: ... + @property + def projection_mode(self) -> ProjectionMode: ... + @property + def resolution(self) -> Vec2: ... + @property + def resolution_is_pixels(self) -> bool: ... + @property + def field_of_view_deg(self) -> Vec2: ... + @property + def field_of_view_tan(self) -> Vec2: ... + @property + def orthographic_extent(self) -> float: ... + @property + def orthographic_size(self) -> Vec2: ... + @property + def aspect_ratio(self) -> float: ... + @property + def near_plane(self) -> float: ... + @property + def far_plane(self) -> float: ... + +class Bone(Element): + @property + def name(self) -> str: ... + @property + def radius(self) -> float: ... + @property + def relative_length(self) -> float: ... + @property + def is_root(self) -> bool: ... + +class Texture(Element): + @property + def name(self) -> str: ... + @property + def filename(self) -> str: ... + @property + def absolute_filename(self) -> str: ... + @property + def relative_filename(self) -> str: ... + @property + def type(self) -> TextureType: ... + +class AnimStack(Element): + @property + def name(self) -> str: ... + @property + def time_begin(self) -> float: ... + @property + def time_end(self) -> float: ... + @property + def layers(self) -> list[AnimLayer]: ... + +class AnimLayer(Element): + @property + def name(self) -> str: ... + @property + def weight(self) -> float: ... + @property + def weight_is_animated(self) -> bool: ... + @property + def blended(self) -> bool: ... + @property + def additive(self) -> bool: ... + @property + def compose_rotation(self) -> bool: ... + @property + def compose_scale(self) -> bool: ... + +class AnimCurve(Element): + @property + def name(self) -> str: ... + @property + def num_keyframes(self) -> int: ... + @property + def min_value(self) -> float: ... + @property + def max_value(self) -> float: ... + @property + def min_time(self) -> float: ... + @property + def max_time(self) -> float: ... + class Anim(Element): ... -class AnimStack(Element): ... -class AnimLayer(Element): ... -class AnimCurve(Element): ... -class SkinDeformer(Element): ... -class SkinCluster(Element): ... -class BlendDeformer(Element): ... -class BlendChannel(Element): ... -class BlendShape(Element): ... -class Constraint(Element): ... + +class SkinDeformer(Element): + @property + def name(self) -> str: ... + @property + def clusters(self) -> list[SkinCluster]: ... + +class SkinCluster(Element): + @property + def name(self) -> str: ... + @property + def num_weights(self) -> int: ... + +class BlendDeformer(Element): + @property + def name(self) -> str: ... + @property + def channels(self) -> list[BlendChannel]: ... + +class BlendChannel(Element): + @property + def name(self) -> str: ... + @property + def weight(self) -> float: ... + +class BlendShape(Element): + @property + def name(self) -> str: ... + @property + def num_offsets(self) -> int: ... + +class Constraint(Element): + @property + def name(self) -> str: ... + @property + def type(self) -> ConstraintType: ... + @property + def weight(self) -> float: ... + @property + def active(self) -> bool: ... class Scene: @classmethod @@ -215,6 +353,26 @@ class Scene: @property def materials(self) -> list[Material]: ... @property + def lights(self) -> list[Light]: ... + @property + def cameras(self) -> list[Camera]: ... + @property + def bones(self) -> list[Bone]: ... + @property + def textures(self) -> list[Texture]: ... + @property + def anim_stacks(self) -> list[AnimStack]: ... + @property + def anim_curves(self) -> list[AnimCurve]: ... + @property + def skin_deformers(self) -> list[SkinDeformer]: ... + @property + def blend_deformers(self) -> list[BlendDeformer]: ... + @property + def blend_shapes(self) -> list[BlendShape]: ... + @property + def constraints(self) -> list[Constraint]: ... + @property def root_node(self) -> Node | None: ... @property def axes(self) -> CoordinateAxes: ... @@ -235,6 +393,12 @@ class Node(Element): @property def mesh(self) -> Mesh | None: ... @property + def light(self) -> Light | None: ... + @property + def camera(self) -> Camera | None: ... + @property + def bone(self) -> Bone | None: ... + @property def is_root(self) -> bool: ... @property def world_transform(self) -> np.ndarray[Any, Any]: ... diff --git a/ufbx/_ufbx.pyx b/ufbx/_ufbx.pyx index 14c44ad..c2696b4 100644 --- a/ufbx/_ufbx.pyx +++ b/ufbx/_ufbx.pyx @@ -21,6 +21,32 @@ cdef extern from "ufbx_wrapper.h": pass ctypedef struct ufbx_material: pass + ctypedef struct ufbx_light: + pass + ctypedef struct ufbx_camera: + pass + ctypedef struct ufbx_bone: + pass + ctypedef struct ufbx_texture: + pass + ctypedef struct ufbx_anim_stack: + pass + ctypedef struct ufbx_anim_layer: + pass + ctypedef struct ufbx_anim_curve: + pass + ctypedef struct ufbx_skin_deformer: + pass + ctypedef struct ufbx_skin_cluster: + pass + ctypedef struct ufbx_blend_deformer: + pass + ctypedef struct ufbx_blend_channel: + pass + ctypedef struct ufbx_blend_shape: + pass + ctypedef struct ufbx_constraint: + pass # Scene management ufbx_scene* ufbx_wrapper_load_file(const char *filename, char **error_msg) @@ -66,6 +92,120 @@ cdef extern from "ufbx_wrapper.h": ufbx_material* ufbx_wrapper_mesh_get_material(const ufbx_mesh *mesh, size_t index) const char* ufbx_wrapper_material_get_name(const ufbx_material *material) + # Light access + size_t ufbx_wrapper_scene_get_num_lights(const ufbx_scene *scene) + ufbx_light* ufbx_wrapper_scene_get_light(const ufbx_scene *scene, size_t index) + ufbx_light* ufbx_wrapper_node_get_light(const ufbx_node *node) + const char* ufbx_wrapper_light_get_name(const ufbx_light *light) + void ufbx_wrapper_light_get_color(const ufbx_light *light, float *rgb) + double ufbx_wrapper_light_get_intensity(const ufbx_light *light) + void ufbx_wrapper_light_get_local_direction(const ufbx_light *light, float *xyz) + int ufbx_wrapper_light_get_type(const ufbx_light *light) + int ufbx_wrapper_light_get_decay(const ufbx_light *light) + int ufbx_wrapper_light_get_area_shape(const ufbx_light *light) + double ufbx_wrapper_light_get_inner_angle(const ufbx_light *light) + double ufbx_wrapper_light_get_outer_angle(const ufbx_light *light) + bint ufbx_wrapper_light_get_cast_light(const ufbx_light *light) + bint ufbx_wrapper_light_get_cast_shadows(const ufbx_light *light) + + # Camera access + size_t ufbx_wrapper_scene_get_num_cameras(const ufbx_scene *scene) + ufbx_camera* ufbx_wrapper_scene_get_camera(const ufbx_scene *scene, size_t index) + ufbx_camera* ufbx_wrapper_node_get_camera(const ufbx_node *node) + const char* ufbx_wrapper_camera_get_name(const ufbx_camera *camera) + int ufbx_wrapper_camera_get_projection_mode(const ufbx_camera *camera) + void ufbx_wrapper_camera_get_resolution(const ufbx_camera *camera, float *xy) + bint ufbx_wrapper_camera_get_resolution_is_pixels(const ufbx_camera *camera) + void ufbx_wrapper_camera_get_field_of_view_deg(const ufbx_camera *camera, float *xy) + void ufbx_wrapper_camera_get_field_of_view_tan(const ufbx_camera *camera, float *xy) + double ufbx_wrapper_camera_get_orthographic_extent(const ufbx_camera *camera) + void ufbx_wrapper_camera_get_orthographic_size(const ufbx_camera *camera, float *xy) + double ufbx_wrapper_camera_get_aspect_ratio(const ufbx_camera *camera) + double ufbx_wrapper_camera_get_near_plane(const ufbx_camera *camera) + double ufbx_wrapper_camera_get_far_plane(const ufbx_camera *camera) + + # Bone access + size_t ufbx_wrapper_scene_get_num_bones(const ufbx_scene *scene) + ufbx_bone* ufbx_wrapper_scene_get_bone(const ufbx_scene *scene, size_t index) + ufbx_bone* ufbx_wrapper_node_get_bone(const ufbx_node *node) + const char* ufbx_wrapper_bone_get_name(const ufbx_bone *bone) + double ufbx_wrapper_bone_get_radius(const ufbx_bone *bone) + double ufbx_wrapper_bone_get_relative_length(const ufbx_bone *bone) + bint ufbx_wrapper_bone_is_root(const ufbx_bone *bone) + + # Texture access + size_t ufbx_wrapper_scene_get_num_textures(const ufbx_scene *scene) + ufbx_texture* ufbx_wrapper_scene_get_texture(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_texture_get_name(const ufbx_texture *texture) + const char* ufbx_wrapper_texture_get_filename(const ufbx_texture *texture) + const char* ufbx_wrapper_texture_get_absolute_filename(const ufbx_texture *texture) + const char* ufbx_wrapper_texture_get_relative_filename(const ufbx_texture *texture) + int ufbx_wrapper_texture_get_type(const ufbx_texture *texture) + + # AnimStack access + size_t ufbx_wrapper_scene_get_num_anim_stacks(const ufbx_scene *scene) + ufbx_anim_stack* ufbx_wrapper_scene_get_anim_stack(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_anim_stack_get_name(const ufbx_anim_stack *anim_stack) + double ufbx_wrapper_anim_stack_get_time_begin(const ufbx_anim_stack *anim_stack) + double ufbx_wrapper_anim_stack_get_time_end(const ufbx_anim_stack *anim_stack) + size_t ufbx_wrapper_anim_stack_get_num_layers(const ufbx_anim_stack *anim_stack) + ufbx_anim_layer* ufbx_wrapper_anim_stack_get_layer(const ufbx_anim_stack *anim_stack, size_t index) + + # AnimLayer access + const char* ufbx_wrapper_anim_layer_get_name(const ufbx_anim_layer *anim_layer) + double ufbx_wrapper_anim_layer_get_weight(const ufbx_anim_layer *anim_layer) + bint ufbx_wrapper_anim_layer_get_weight_is_animated(const ufbx_anim_layer *anim_layer) + bint ufbx_wrapper_anim_layer_get_blended(const ufbx_anim_layer *anim_layer) + bint ufbx_wrapper_anim_layer_get_additive(const ufbx_anim_layer *anim_layer) + bint ufbx_wrapper_anim_layer_get_compose_rotation(const ufbx_anim_layer *anim_layer) + bint ufbx_wrapper_anim_layer_get_compose_scale(const ufbx_anim_layer *anim_layer) + + # AnimCurve access + size_t ufbx_wrapper_scene_get_num_anim_curves(const ufbx_scene *scene) + ufbx_anim_curve* ufbx_wrapper_scene_get_anim_curve(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_anim_curve_get_name(const ufbx_anim_curve *anim_curve) + size_t ufbx_wrapper_anim_curve_get_num_keyframes(const ufbx_anim_curve *anim_curve) + double ufbx_wrapper_anim_curve_get_min_value(const ufbx_anim_curve *anim_curve) + double ufbx_wrapper_anim_curve_get_max_value(const ufbx_anim_curve *anim_curve) + double ufbx_wrapper_anim_curve_get_min_time(const ufbx_anim_curve *anim_curve) + double ufbx_wrapper_anim_curve_get_max_time(const ufbx_anim_curve *anim_curve) + + # SkinDeformer access + size_t ufbx_wrapper_scene_get_num_skin_deformers(const ufbx_scene *scene) + ufbx_skin_deformer* ufbx_wrapper_scene_get_skin_deformer(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_skin_deformer_get_name(const ufbx_skin_deformer *skin_deformer) + size_t ufbx_wrapper_skin_deformer_get_num_clusters(const ufbx_skin_deformer *skin_deformer) + ufbx_skin_cluster* ufbx_wrapper_skin_deformer_get_cluster(const ufbx_skin_deformer *skin_deformer, size_t index) + + # SkinCluster access + const char* ufbx_wrapper_skin_cluster_get_name(const ufbx_skin_cluster *skin_cluster) + size_t ufbx_wrapper_skin_cluster_get_num_weights(const ufbx_skin_cluster *skin_cluster) + + # BlendDeformer access + size_t ufbx_wrapper_scene_get_num_blend_deformers(const ufbx_scene *scene) + ufbx_blend_deformer* ufbx_wrapper_scene_get_blend_deformer(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_blend_deformer_get_name(const ufbx_blend_deformer *blend_deformer) + size_t ufbx_wrapper_blend_deformer_get_num_channels(const ufbx_blend_deformer *blend_deformer) + ufbx_blend_channel* ufbx_wrapper_blend_deformer_get_channel(const ufbx_blend_deformer *blend_deformer, size_t index) + + # BlendChannel access + const char* ufbx_wrapper_blend_channel_get_name(const ufbx_blend_channel *blend_channel) + double ufbx_wrapper_blend_channel_get_weight(const ufbx_blend_channel *blend_channel) + + # BlendShape access + size_t ufbx_wrapper_scene_get_num_blend_shapes(const ufbx_scene *scene) + ufbx_blend_shape* ufbx_wrapper_scene_get_blend_shape(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_blend_shape_get_name(const ufbx_blend_shape *blend_shape) + size_t ufbx_wrapper_blend_shape_get_num_offsets(const ufbx_blend_shape *blend_shape) + + # Constraint access + size_t ufbx_wrapper_scene_get_num_constraints(const ufbx_scene *scene) + ufbx_constraint* ufbx_wrapper_scene_get_constraint(const ufbx_scene *scene, size_t index) + const char* ufbx_wrapper_constraint_get_name(const ufbx_constraint *constraint) + int ufbx_wrapper_constraint_get_type(const ufbx_constraint *constraint) + double ufbx_wrapper_constraint_get_weight(const ufbx_constraint *constraint) + bint ufbx_wrapper_constraint_get_active(const ufbx_constraint *constraint) + # Python classes class UfbxError(Exception): @@ -397,59 +537,657 @@ cdef class Element: cdef class Light(Element): - pass + """Light source""" + cdef Scene _scene + cdef ufbx_light* _light + + @staticmethod + cdef Light _create(Scene scene, ufbx_light* light): + """Internal factory method""" + cdef Light obj = Light.__new__(Light) + obj._scene = scene + obj._light = light + return obj + + @property + def name(self): + """Light name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_name(self._light).decode('utf-8', errors='replace') + + @property + def color(self): + """Light color (RGB)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float rgb[3] + ufbx_wrapper_light_get_color(self._light, rgb) + return Vec3(rgb[0], rgb[1], rgb[2]) + + @property + def intensity(self): + """Light intensity""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_intensity(self._light) + + @property + def local_direction(self): + """Direction the light is aimed at in node's local space""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float xyz[3] + ufbx_wrapper_light_get_local_direction(self._light, xyz) + return Vec3(xyz[0], xyz[1], xyz[2]) + + @property + def type(self): + """Light type (LightType enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return LightType(ufbx_wrapper_light_get_type(self._light)) + + @property + def decay(self): + """Light decay mode (LightDecay enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return LightDecay(ufbx_wrapper_light_get_decay(self._light)) + + @property + def area_shape(self): + """Area light shape (LightAreaShape enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return LightAreaShape(ufbx_wrapper_light_get_area_shape(self._light)) + + @property + def inner_angle(self): + """Spotlight inner angle in degrees""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_inner_angle(self._light) + + @property + def outer_angle(self): + """Spotlight outer angle in degrees""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_outer_angle(self._light) + + @property + def cast_light(self): + """Whether the light casts light""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_cast_light(self._light) + + @property + def cast_shadows(self): + """Whether the light casts shadows""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_light_get_cast_shadows(self._light) cdef class Camera(Element): - pass + """Camera""" + cdef Scene _scene + cdef ufbx_camera* _camera + + @staticmethod + cdef Camera _create(Scene scene, ufbx_camera* camera): + """Internal factory method""" + cdef Camera obj = Camera.__new__(Camera) + obj._scene = scene + obj._camera = camera + return obj + + @property + def name(self): + """Camera name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_name(self._camera).decode('utf-8', errors='replace') + + @property + def projection_mode(self): + """Projection mode (ProjectionMode enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ProjectionMode(ufbx_wrapper_camera_get_projection_mode(self._camera)) + + @property + def resolution(self): + """Render resolution""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float xy[2] + ufbx_wrapper_camera_get_resolution(self._camera, xy) + return Vec2(xy[0], xy[1]) + + @property + def resolution_is_pixels(self): + """Whether resolution is in pixels""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_resolution_is_pixels(self._camera) + + @property + def field_of_view_deg(self): + """Field of view in degrees (horizontal, vertical)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float xy[2] + ufbx_wrapper_camera_get_field_of_view_deg(self._camera, xy) + return Vec2(xy[0], xy[1]) + + @property + def field_of_view_tan(self): + """Tangent of field of view""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float xy[2] + ufbx_wrapper_camera_get_field_of_view_tan(self._camera, xy) + return Vec2(xy[0], xy[1]) + + @property + def orthographic_extent(self): + """Orthographic camera extent""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_orthographic_extent(self._camera) + + @property + def orthographic_size(self): + """Orthographic camera size""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef float xy[2] + ufbx_wrapper_camera_get_orthographic_size(self._camera, xy) + return Vec2(xy[0], xy[1]) + + @property + def aspect_ratio(self): + """Camera aspect ratio""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_aspect_ratio(self._camera) + + @property + def near_plane(self): + """Near plane distance""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_near_plane(self._camera) + + @property + def far_plane(self): + """Far plane distance""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_camera_get_far_plane(self._camera) cdef class Bone(Element): - pass + """Bone""" + cdef Scene _scene + cdef ufbx_bone* _bone + + @staticmethod + cdef Bone _create(Scene scene, ufbx_bone* bone): + """Internal factory method""" + cdef Bone obj = Bone.__new__(Bone) + obj._scene = scene + obj._bone = bone + return obj + + @property + def name(self): + """Bone name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_bone_get_name(self._bone).decode('utf-8', errors='replace') + + @property + def radius(self): + """Visual radius of the bone""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_bone_get_radius(self._bone) + + @property + def relative_length(self): + """Length of the bone relative to the distance between two nodes""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_bone_get_relative_length(self._bone) + + @property + def is_root(self): + """Is this a root bone""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_bone_is_root(self._bone) cdef class Texture(Element): - pass + """Texture""" + cdef Scene _scene + cdef ufbx_texture* _texture + @staticmethod + cdef Texture _create(Scene scene, ufbx_texture* texture): + """Internal factory method""" + cdef Texture obj = Texture.__new__(Texture) + obj._scene = scene + obj._texture = texture + return obj -cdef class Anim(Element): - pass + @property + def name(self): + """Texture name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_texture_get_name(self._texture).decode('utf-8', errors='replace') + + @property + def filename(self): + """Texture filename""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_texture_get_filename(self._texture).decode('utf-8', errors='replace') + + @property + def absolute_filename(self): + """Absolute path to texture file""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_texture_get_absolute_filename(self._texture).decode('utf-8', errors='replace') + + @property + def relative_filename(self): + """Relative path to texture file""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_texture_get_relative_filename(self._texture).decode('utf-8', errors='replace') + + @property + def type(self): + """Texture type (TextureType enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return TextureType(ufbx_wrapper_texture_get_type(self._texture)) cdef class AnimStack(Element): - pass + """Animation stack (timeline)""" + cdef Scene _scene + cdef ufbx_anim_stack* _anim_stack + + @staticmethod + cdef AnimStack _create(Scene scene, ufbx_anim_stack* anim_stack): + """Internal factory method""" + cdef AnimStack obj = AnimStack.__new__(AnimStack) + obj._scene = scene + obj._anim_stack = anim_stack + return obj + + @property + def name(self): + """Animation stack name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_stack_get_name(self._anim_stack).decode('utf-8', errors='replace') + + @property + def time_begin(self): + """Start time of the animation""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_stack_get_time_begin(self._anim_stack) + + @property + def time_end(self): + """End time of the animation""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_stack_get_time_end(self._anim_stack) + + @property + def layers(self): + """Animation layers in this stack""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_anim_stack_get_num_layers(self._anim_stack) + cdef list result = [] + cdef ufbx_anim_layer* layer + for i in range(count): + layer = ufbx_wrapper_anim_stack_get_layer(self._anim_stack, i) + if layer != NULL: + result.append(AnimLayer._create(self._scene, layer)) + return result cdef class AnimLayer(Element): - pass + """Animation layer""" + cdef Scene _scene + cdef ufbx_anim_layer* _anim_layer + + @staticmethod + cdef AnimLayer _create(Scene scene, ufbx_anim_layer* anim_layer): + """Internal factory method""" + cdef AnimLayer obj = AnimLayer.__new__(AnimLayer) + obj._scene = scene + obj._anim_layer = anim_layer + return obj + + @property + def name(self): + """Animation layer name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_name(self._anim_layer).decode('utf-8', errors='replace') + + @property + def weight(self): + """Layer weight""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_weight(self._anim_layer) + + @property + def weight_is_animated(self): + """Whether the layer weight is animated""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_weight_is_animated(self._anim_layer) + + @property + def blended(self): + """Whether the layer is blended""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_blended(self._anim_layer) + + @property + def additive(self): + """Whether the layer is additive""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_additive(self._anim_layer) + + @property + def compose_rotation(self): + """Whether to compose rotation""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_compose_rotation(self._anim_layer) + + @property + def compose_scale(self): + """Whether to compose scale""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_layer_get_compose_scale(self._anim_layer) cdef class AnimCurve(Element): + """Animation curve""" + cdef Scene _scene + cdef ufbx_anim_curve* _anim_curve + + @staticmethod + cdef AnimCurve _create(Scene scene, ufbx_anim_curve* anim_curve): + """Internal factory method""" + cdef AnimCurve obj = AnimCurve.__new__(AnimCurve) + obj._scene = scene + obj._anim_curve = anim_curve + return obj + + @property + def name(self): + """Animation curve name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_name(self._anim_curve).decode('utf-8', errors='replace') + + @property + def num_keyframes(self): + """Number of keyframes in the curve""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_num_keyframes(self._anim_curve) + + @property + def min_value(self): + """Minimum value in the curve""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_min_value(self._anim_curve) + + @property + def max_value(self): + """Maximum value in the curve""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_max_value(self._anim_curve) + + @property + def min_time(self): + """Minimum time in the curve""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_min_time(self._anim_curve) + + @property + def max_time(self): + """Maximum time in the curve""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_anim_curve_get_max_time(self._anim_curve) + + +cdef class Anim(Element): + """Animation definition (placeholder for future implementation)""" pass cdef class SkinDeformer(Element): - pass + """Skin deformer (skinning/rigging)""" + cdef Scene _scene + cdef ufbx_skin_deformer* _skin_deformer + + @staticmethod + cdef SkinDeformer _create(Scene scene, ufbx_skin_deformer* skin_deformer): + """Internal factory method""" + cdef SkinDeformer obj = SkinDeformer.__new__(SkinDeformer) + obj._scene = scene + obj._skin_deformer = skin_deformer + return obj + + @property + def name(self): + """Skin deformer name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_skin_deformer_get_name(self._skin_deformer).decode('utf-8', errors='replace') + + @property + def clusters(self): + """Skin clusters (bones) in this deformer""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_skin_deformer_get_num_clusters(self._skin_deformer) + cdef list result = [] + cdef ufbx_skin_cluster* cluster + for i in range(count): + cluster = ufbx_wrapper_skin_deformer_get_cluster(self._skin_deformer, i) + if cluster != NULL: + result.append(SkinCluster._create(self._scene, cluster)) + return result cdef class SkinCluster(Element): - pass + """Skin cluster (single bone binding)""" + cdef Scene _scene + cdef ufbx_skin_cluster* _skin_cluster + + @staticmethod + cdef SkinCluster _create(Scene scene, ufbx_skin_cluster* skin_cluster): + """Internal factory method""" + cdef SkinCluster obj = SkinCluster.__new__(SkinCluster) + obj._scene = scene + obj._skin_cluster = skin_cluster + return obj + + @property + def name(self): + """Skin cluster name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_skin_cluster_get_name(self._skin_cluster).decode('utf-8', errors='replace') + + @property + def num_weights(self): + """Number of vertex weights""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_skin_cluster_get_num_weights(self._skin_cluster) cdef class BlendDeformer(Element): - pass + """Blend shape deformer""" + cdef Scene _scene + cdef ufbx_blend_deformer* _blend_deformer + + @staticmethod + cdef BlendDeformer _create(Scene scene, ufbx_blend_deformer* blend_deformer): + """Internal factory method""" + cdef BlendDeformer obj = BlendDeformer.__new__(BlendDeformer) + obj._scene = scene + obj._blend_deformer = blend_deformer + return obj + + @property + def name(self): + """Blend deformer name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_blend_deformer_get_name(self._blend_deformer).decode('utf-8', errors='replace') + + @property + def channels(self): + """Blend channels (morph targets)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_blend_deformer_get_num_channels(self._blend_deformer) + cdef list result = [] + cdef ufbx_blend_channel* channel + for i in range(count): + channel = ufbx_wrapper_blend_deformer_get_channel(self._blend_deformer, i) + if channel != NULL: + result.append(BlendChannel._create(self._scene, channel)) + return result cdef class BlendChannel(Element): - pass + """Blend channel (single morph target)""" + cdef Scene _scene + cdef ufbx_blend_channel* _blend_channel + + @staticmethod + cdef BlendChannel _create(Scene scene, ufbx_blend_channel* blend_channel): + """Internal factory method""" + cdef BlendChannel obj = BlendChannel.__new__(BlendChannel) + obj._scene = scene + obj._blend_channel = blend_channel + return obj + + @property + def name(self): + """Blend channel name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_blend_channel_get_name(self._blend_channel).decode('utf-8', errors='replace') + + @property + def weight(self): + """Current weight of the channel""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_blend_channel_get_weight(self._blend_channel) cdef class BlendShape(Element): - pass + """Blend shape (vertex offsets)""" + cdef Scene _scene + cdef ufbx_blend_shape* _blend_shape + + @staticmethod + cdef BlendShape _create(Scene scene, ufbx_blend_shape* blend_shape): + """Internal factory method""" + cdef BlendShape obj = BlendShape.__new__(BlendShape) + obj._scene = scene + obj._blend_shape = blend_shape + return obj + + @property + def name(self): + """Blend shape name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_blend_shape_get_name(self._blend_shape).decode('utf-8', errors='replace') + + @property + def num_offsets(self): + """Number of vertex offsets""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_blend_shape_get_num_offsets(self._blend_shape) cdef class Constraint(Element): - pass + """Constraint""" + cdef Scene _scene + cdef ufbx_constraint* _constraint + + @staticmethod + cdef Constraint _create(Scene scene, ufbx_constraint* constraint): + """Internal factory method""" + cdef Constraint obj = Constraint.__new__(Constraint) + obj._scene = scene + obj._constraint = constraint + return obj + + @property + def name(self): + """Constraint name""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_constraint_get_name(self._constraint).decode('utf-8', errors='replace') + + @property + def type(self): + """Constraint type (ConstraintType enum)""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ConstraintType(ufbx_wrapper_constraint_get_type(self._constraint)) + + @property + def weight(self): + """Constraint weight""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_constraint_get_weight(self._constraint) + + @property + def active(self): + """Whether the constraint is active""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + return ufbx_wrapper_constraint_get_active(self._constraint) cdef class Scene: @@ -529,6 +1267,86 @@ cdef class Scene: cdef int f = ufbx_wrapper_scene_get_axes_front(self._scene) return CoordinateAxes(r, u, f) + @property + def lights(self): + """Get all lights in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_lights(self._scene) + return [self._get_light(i) for i in range(count)] + + @property + def cameras(self): + """Get all cameras in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_cameras(self._scene) + return [self._get_camera(i) for i in range(count)] + + @property + def bones(self): + """Get all bones in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_bones(self._scene) + return [self._get_bone(i) for i in range(count)] + + @property + def textures(self): + """Get all textures in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_textures(self._scene) + return [self._get_texture(i) for i in range(count)] + + @property + def anim_stacks(self): + """Get all animation stacks in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_anim_stacks(self._scene) + return [self._get_anim_stack(i) for i in range(count)] + + @property + def anim_curves(self): + """Get all animation curves in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_anim_curves(self._scene) + return [self._get_anim_curve(i) for i in range(count)] + + @property + def skin_deformers(self): + """Get all skin deformers in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_skin_deformers(self._scene) + return [self._get_skin_deformer(i) for i in range(count)] + + @property + def blend_deformers(self): + """Get all blend deformers in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_blend_deformers(self._scene) + return [self._get_blend_deformer(i) for i in range(count)] + + @property + def blend_shapes(self): + """Get all blend shapes in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_blend_shapes(self._scene) + return [self._get_blend_shape(i) for i in range(count)] + + @property + def constraints(self): + """Get all constraints in the scene""" + if self._closed: + raise RuntimeError("Scene is closed") + cdef size_t count = ufbx_wrapper_scene_get_num_constraints(self._scene) + return [self._get_constraint(i) for i in range(count)] + cdef Node _get_node(self, size_t index): """Internal: get node by index""" cdef ufbx_node* node = ufbx_wrapper_scene_get_node(self._scene, index) @@ -550,6 +1368,76 @@ cdef class Scene: return Material._create(self, material) return None + cdef Light _get_light(self, size_t index): + """Internal: get light by index""" + cdef ufbx_light* light = ufbx_wrapper_scene_get_light(self._scene, index) + if light != NULL: + return Light._create(self, light) + return None + + cdef Camera _get_camera(self, size_t index): + """Internal: get camera by index""" + cdef ufbx_camera* camera = ufbx_wrapper_scene_get_camera(self._scene, index) + if camera != NULL: + return Camera._create(self, camera) + return None + + cdef Bone _get_bone(self, size_t index): + """Internal: get bone by index""" + cdef ufbx_bone* bone = ufbx_wrapper_scene_get_bone(self._scene, index) + if bone != NULL: + return Bone._create(self, bone) + return None + + cdef Texture _get_texture(self, size_t index): + """Internal: get texture by index""" + cdef ufbx_texture* texture = ufbx_wrapper_scene_get_texture(self._scene, index) + if texture != NULL: + return Texture._create(self, texture) + return None + + cdef AnimStack _get_anim_stack(self, size_t index): + """Internal: get animation stack by index""" + cdef ufbx_anim_stack* anim_stack = ufbx_wrapper_scene_get_anim_stack(self._scene, index) + if anim_stack != NULL: + return AnimStack._create(self, anim_stack) + return None + + cdef AnimCurve _get_anim_curve(self, size_t index): + """Internal: get animation curve by index""" + cdef ufbx_anim_curve* anim_curve = ufbx_wrapper_scene_get_anim_curve(self._scene, index) + if anim_curve != NULL: + return AnimCurve._create(self, anim_curve) + return None + + cdef SkinDeformer _get_skin_deformer(self, size_t index): + """Internal: get skin deformer by index""" + cdef ufbx_skin_deformer* skin_deformer = ufbx_wrapper_scene_get_skin_deformer(self._scene, index) + if skin_deformer != NULL: + return SkinDeformer._create(self, skin_deformer) + return None + + cdef BlendDeformer _get_blend_deformer(self, size_t index): + """Internal: get blend deformer by index""" + cdef ufbx_blend_deformer* blend_deformer = ufbx_wrapper_scene_get_blend_deformer(self._scene, index) + if blend_deformer != NULL: + return BlendDeformer._create(self, blend_deformer) + return None + + cdef BlendShape _get_blend_shape(self, size_t index): + """Internal: get blend shape by index""" + cdef ufbx_blend_shape* blend_shape = ufbx_wrapper_scene_get_blend_shape(self._scene, index) + if blend_shape != NULL: + return BlendShape._create(self, blend_shape) + return None + + cdef Constraint _get_constraint(self, size_t index): + """Internal: get constraint by index""" + cdef ufbx_constraint* constraint = ufbx_wrapper_scene_get_constraint(self._scene, index) + if constraint != NULL: + return Constraint._create(self, constraint) + return None + cdef class Node(Element): """Scene node with transform and hierarchy""" @@ -605,6 +1493,36 @@ cdef class Node(Element): return Mesh._create(self._scene, mesh) return None + @property + def light(self): + """Attached light""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef ufbx_light* light = ufbx_wrapper_node_get_light(self._node) + if light != NULL: + return Light._create(self._scene, light) + return None + + @property + def camera(self): + """Attached camera""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef ufbx_camera* camera = ufbx_wrapper_node_get_camera(self._node) + if camera != NULL: + return Camera._create(self._scene, camera) + return None + + @property + def bone(self): + """Attached bone""" + if self._scene._closed: + raise RuntimeError("Scene is closed") + cdef ufbx_bone* bone = ufbx_wrapper_node_get_bone(self._node) + if bone != NULL: + return Bone._create(self._scene, bone) + return None + @property def is_root(self): """Is this the root node""" diff --git a/ufbx/src/ufbx_wrapper.c b/ufbx/src/ufbx_wrapper.c index b934b82..f2697b2 100644 --- a/ufbx/src/ufbx_wrapper.c +++ b/ufbx/src/ufbx_wrapper.c @@ -198,3 +198,411 @@ const char* ufbx_wrapper_material_get_name(const ufbx_material *material) { if (!material) return ""; return material->name.data ? material->name.data : ""; } + +// Light access +size_t ufbx_wrapper_scene_get_num_lights(const ufbx_scene *scene) { + return scene ? scene->lights.count : 0; +} + +ufbx_light* ufbx_wrapper_scene_get_light(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->lights.count) return NULL; + return scene->lights.data[index]; +} + +ufbx_light* ufbx_wrapper_node_get_light(const ufbx_node *node) { + return node ? node->light : NULL; +} + +const char* ufbx_wrapper_light_get_name(const ufbx_light *light) { + if (!light) return ""; + return light->name.data ? light->name.data : ""; +} + +void ufbx_wrapper_light_get_color(const ufbx_light *light, float *rgb) { + if (!light || !rgb) return; + rgb[0] = (float)light->color.x; + rgb[1] = (float)light->color.y; + rgb[2] = (float)light->color.z; +} + +double ufbx_wrapper_light_get_intensity(const ufbx_light *light) { + return light ? light->intensity : 0.0; +} + +void ufbx_wrapper_light_get_local_direction(const ufbx_light *light, float *xyz) { + if (!light || !xyz) return; + xyz[0] = (float)light->local_direction.x; + xyz[1] = (float)light->local_direction.y; + xyz[2] = (float)light->local_direction.z; +} + +int ufbx_wrapper_light_get_type(const ufbx_light *light) { + return light ? (int)light->type : 0; +} + +int ufbx_wrapper_light_get_decay(const ufbx_light *light) { + return light ? (int)light->decay : 0; +} + +int ufbx_wrapper_light_get_area_shape(const ufbx_light *light) { + return light ? (int)light->area_shape : 0; +} + +double ufbx_wrapper_light_get_inner_angle(const ufbx_light *light) { + return light ? light->inner_angle : 0.0; +} + +double ufbx_wrapper_light_get_outer_angle(const ufbx_light *light) { + return light ? light->outer_angle : 0.0; +} + +bool ufbx_wrapper_light_get_cast_light(const ufbx_light *light) { + return light ? light->cast_light : false; +} + +bool ufbx_wrapper_light_get_cast_shadows(const ufbx_light *light) { + return light ? light->cast_shadows : false; +} + +// Camera access +size_t ufbx_wrapper_scene_get_num_cameras(const ufbx_scene *scene) { + return scene ? scene->cameras.count : 0; +} + +ufbx_camera* ufbx_wrapper_scene_get_camera(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->cameras.count) return NULL; + return scene->cameras.data[index]; +} + +ufbx_camera* ufbx_wrapper_node_get_camera(const ufbx_node *node) { + return node ? node->camera : NULL; +} + +const char* ufbx_wrapper_camera_get_name(const ufbx_camera *camera) { + if (!camera) return ""; + return camera->name.data ? camera->name.data : ""; +} + +int ufbx_wrapper_camera_get_projection_mode(const ufbx_camera *camera) { + return camera ? (int)camera->projection_mode : 0; +} + +void ufbx_wrapper_camera_get_resolution(const ufbx_camera *camera, float *xy) { + if (!camera || !xy) return; + xy[0] = (float)camera->resolution.x; + xy[1] = (float)camera->resolution.y; +} + +bool ufbx_wrapper_camera_get_resolution_is_pixels(const ufbx_camera *camera) { + return camera ? camera->resolution_is_pixels : false; +} + +void ufbx_wrapper_camera_get_field_of_view_deg(const ufbx_camera *camera, float *xy) { + if (!camera || !xy) return; + xy[0] = (float)camera->field_of_view_deg.x; + xy[1] = (float)camera->field_of_view_deg.y; +} + +void ufbx_wrapper_camera_get_field_of_view_tan(const ufbx_camera *camera, float *xy) { + if (!camera || !xy) return; + xy[0] = (float)camera->field_of_view_tan.x; + xy[1] = (float)camera->field_of_view_tan.y; +} + +double ufbx_wrapper_camera_get_orthographic_extent(const ufbx_camera *camera) { + return camera ? camera->orthographic_extent : 0.0; +} + +void ufbx_wrapper_camera_get_orthographic_size(const ufbx_camera *camera, float *xy) { + if (!camera || !xy) return; + xy[0] = (float)camera->orthographic_size.x; + xy[1] = (float)camera->orthographic_size.y; +} + +double ufbx_wrapper_camera_get_aspect_ratio(const ufbx_camera *camera) { + return camera ? camera->aspect_ratio : 1.0; +} + +double ufbx_wrapper_camera_get_near_plane(const ufbx_camera *camera) { + return camera ? camera->near_plane : 0.0; +} + +double ufbx_wrapper_camera_get_far_plane(const ufbx_camera *camera) { + return camera ? camera->far_plane : 0.0; +} + +// Bone access +size_t ufbx_wrapper_scene_get_num_bones(const ufbx_scene *scene) { + return scene ? scene->bones.count : 0; +} + +ufbx_bone* ufbx_wrapper_scene_get_bone(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->bones.count) return NULL; + return scene->bones.data[index]; +} + +ufbx_bone* ufbx_wrapper_node_get_bone(const ufbx_node *node) { + return node ? node->bone : NULL; +} + +const char* ufbx_wrapper_bone_get_name(const ufbx_bone *bone) { + if (!bone) return ""; + return bone->name.data ? bone->name.data : ""; +} + +double ufbx_wrapper_bone_get_radius(const ufbx_bone *bone) { + return bone ? bone->radius : 0.0; +} + +double ufbx_wrapper_bone_get_relative_length(const ufbx_bone *bone) { + return bone ? bone->relative_length : 0.0; +} + +bool ufbx_wrapper_bone_is_root(const ufbx_bone *bone) { + return bone ? bone->is_root : false; +} + +// Texture access +size_t ufbx_wrapper_scene_get_num_textures(const ufbx_scene *scene) { + return scene ? scene->textures.count : 0; +} + +ufbx_texture* ufbx_wrapper_scene_get_texture(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->textures.count) return NULL; + return scene->textures.data[index]; +} + +const char* ufbx_wrapper_texture_get_name(const ufbx_texture *texture) { + if (!texture) return ""; + return texture->name.data ? texture->name.data : ""; +} + +const char* ufbx_wrapper_texture_get_filename(const ufbx_texture *texture) { + if (!texture) return ""; + return texture->filename.data ? texture->filename.data : ""; +} + +const char* ufbx_wrapper_texture_get_absolute_filename(const ufbx_texture *texture) { + if (!texture) return ""; + return texture->absolute_filename.data ? texture->absolute_filename.data : ""; +} + +const char* ufbx_wrapper_texture_get_relative_filename(const ufbx_texture *texture) { + if (!texture) return ""; + return texture->relative_filename.data ? texture->relative_filename.data : ""; +} + +int ufbx_wrapper_texture_get_type(const ufbx_texture *texture) { + return texture ? (int)texture->type : 0; +} + +// AnimStack access +size_t ufbx_wrapper_scene_get_num_anim_stacks(const ufbx_scene *scene) { + return scene ? scene->anim_stacks.count : 0; +} + +ufbx_anim_stack* ufbx_wrapper_scene_get_anim_stack(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->anim_stacks.count) return NULL; + return scene->anim_stacks.data[index]; +} + +const char* ufbx_wrapper_anim_stack_get_name(const ufbx_anim_stack *anim_stack) { + if (!anim_stack) return ""; + return anim_stack->name.data ? anim_stack->name.data : ""; +} + +double ufbx_wrapper_anim_stack_get_time_begin(const ufbx_anim_stack *anim_stack) { + return anim_stack ? anim_stack->time_begin : 0.0; +} + +double ufbx_wrapper_anim_stack_get_time_end(const ufbx_anim_stack *anim_stack) { + return anim_stack ? anim_stack->time_end : 0.0; +} + +size_t ufbx_wrapper_anim_stack_get_num_layers(const ufbx_anim_stack *anim_stack) { + return anim_stack ? anim_stack->layers.count : 0; +} + +ufbx_anim_layer* ufbx_wrapper_anim_stack_get_layer(const ufbx_anim_stack *anim_stack, size_t index) { + if (!anim_stack || index >= anim_stack->layers.count) return NULL; + return anim_stack->layers.data[index]; +} + +// AnimLayer access +const char* ufbx_wrapper_anim_layer_get_name(const ufbx_anim_layer *anim_layer) { + if (!anim_layer) return ""; + return anim_layer->name.data ? anim_layer->name.data : ""; +} + +double ufbx_wrapper_anim_layer_get_weight(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->weight : 0.0; +} + +bool ufbx_wrapper_anim_layer_get_weight_is_animated(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->weight_is_animated : false; +} + +bool ufbx_wrapper_anim_layer_get_blended(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->blended : false; +} + +bool ufbx_wrapper_anim_layer_get_additive(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->additive : false; +} + +bool ufbx_wrapper_anim_layer_get_compose_rotation(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->compose_rotation : false; +} + +bool ufbx_wrapper_anim_layer_get_compose_scale(const ufbx_anim_layer *anim_layer) { + return anim_layer ? anim_layer->compose_scale : false; +} + +// AnimCurve access +size_t ufbx_wrapper_scene_get_num_anim_curves(const ufbx_scene *scene) { + return scene ? scene->anim_curves.count : 0; +} + +ufbx_anim_curve* ufbx_wrapper_scene_get_anim_curve(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->anim_curves.count) return NULL; + return scene->anim_curves.data[index]; +} + +const char* ufbx_wrapper_anim_curve_get_name(const ufbx_anim_curve *anim_curve) { + if (!anim_curve) return ""; + return anim_curve->name.data ? anim_curve->name.data : ""; +} + +size_t ufbx_wrapper_anim_curve_get_num_keyframes(const ufbx_anim_curve *anim_curve) { + return anim_curve ? anim_curve->keyframes.count : 0; +} + +double ufbx_wrapper_anim_curve_get_min_value(const ufbx_anim_curve *anim_curve) { + return anim_curve ? anim_curve->min_value : 0.0; +} + +double ufbx_wrapper_anim_curve_get_max_value(const ufbx_anim_curve *anim_curve) { + return anim_curve ? anim_curve->max_value : 0.0; +} + +double ufbx_wrapper_anim_curve_get_min_time(const ufbx_anim_curve *anim_curve) { + return anim_curve ? anim_curve->min_time : 0.0; +} + +double ufbx_wrapper_anim_curve_get_max_time(const ufbx_anim_curve *anim_curve) { + return anim_curve ? anim_curve->max_time : 0.0; +} + +// SkinDeformer access +size_t ufbx_wrapper_scene_get_num_skin_deformers(const ufbx_scene *scene) { + return scene ? scene->skin_deformers.count : 0; +} + +ufbx_skin_deformer* ufbx_wrapper_scene_get_skin_deformer(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->skin_deformers.count) return NULL; + return scene->skin_deformers.data[index]; +} + +const char* ufbx_wrapper_skin_deformer_get_name(const ufbx_skin_deformer *skin_deformer) { + if (!skin_deformer) return ""; + return skin_deformer->name.data ? skin_deformer->name.data : ""; +} + +size_t ufbx_wrapper_skin_deformer_get_num_clusters(const ufbx_skin_deformer *skin_deformer) { + return skin_deformer ? skin_deformer->clusters.count : 0; +} + +ufbx_skin_cluster* ufbx_wrapper_skin_deformer_get_cluster(const ufbx_skin_deformer *skin_deformer, size_t index) { + if (!skin_deformer || index >= skin_deformer->clusters.count) return NULL; + return skin_deformer->clusters.data[index]; +} + +// SkinCluster access +const char* ufbx_wrapper_skin_cluster_get_name(const ufbx_skin_cluster *skin_cluster) { + if (!skin_cluster) return ""; + return skin_cluster->name.data ? skin_cluster->name.data : ""; +} + +size_t ufbx_wrapper_skin_cluster_get_num_weights(const ufbx_skin_cluster *skin_cluster) { + return skin_cluster ? skin_cluster->num_weights : 0; +} + +// BlendDeformer access +size_t ufbx_wrapper_scene_get_num_blend_deformers(const ufbx_scene *scene) { + return scene ? scene->blend_deformers.count : 0; +} + +ufbx_blend_deformer* ufbx_wrapper_scene_get_blend_deformer(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->blend_deformers.count) return NULL; + return scene->blend_deformers.data[index]; +} + +const char* ufbx_wrapper_blend_deformer_get_name(const ufbx_blend_deformer *blend_deformer) { + if (!blend_deformer) return ""; + return blend_deformer->name.data ? blend_deformer->name.data : ""; +} + +size_t ufbx_wrapper_blend_deformer_get_num_channels(const ufbx_blend_deformer *blend_deformer) { + return blend_deformer ? blend_deformer->channels.count : 0; +} + +ufbx_blend_channel* ufbx_wrapper_blend_deformer_get_channel(const ufbx_blend_deformer *blend_deformer, size_t index) { + if (!blend_deformer || index >= blend_deformer->channels.count) return NULL; + return blend_deformer->channels.data[index]; +} + +// BlendChannel access +const char* ufbx_wrapper_blend_channel_get_name(const ufbx_blend_channel *blend_channel) { + if (!blend_channel) return ""; + return blend_channel->name.data ? blend_channel->name.data : ""; +} + +double ufbx_wrapper_blend_channel_get_weight(const ufbx_blend_channel *blend_channel) { + return blend_channel ? blend_channel->weight : 0.0; +} + +// BlendShape access +size_t ufbx_wrapper_scene_get_num_blend_shapes(const ufbx_scene *scene) { + return scene ? scene->blend_shapes.count : 0; +} + +ufbx_blend_shape* ufbx_wrapper_scene_get_blend_shape(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->blend_shapes.count) return NULL; + return scene->blend_shapes.data[index]; +} + +const char* ufbx_wrapper_blend_shape_get_name(const ufbx_blend_shape *blend_shape) { + if (!blend_shape) return ""; + return blend_shape->name.data ? blend_shape->name.data : ""; +} + +size_t ufbx_wrapper_blend_shape_get_num_offsets(const ufbx_blend_shape *blend_shape) { + return blend_shape ? blend_shape->num_offsets : 0; +} + +// Constraint access +size_t ufbx_wrapper_scene_get_num_constraints(const ufbx_scene *scene) { + return scene ? scene->constraints.count : 0; +} + +ufbx_constraint* ufbx_wrapper_scene_get_constraint(const ufbx_scene *scene, size_t index) { + if (!scene || index >= scene->constraints.count) return NULL; + return scene->constraints.data[index]; +} + +const char* ufbx_wrapper_constraint_get_name(const ufbx_constraint *constraint) { + if (!constraint) return ""; + return constraint->name.data ? constraint->name.data : ""; +} + +int ufbx_wrapper_constraint_get_type(const ufbx_constraint *constraint) { + return constraint ? (int)constraint->type : 0; +} + +double ufbx_wrapper_constraint_get_weight(const ufbx_constraint *constraint) { + return constraint ? constraint->weight : 0.0; +} + +bool ufbx_wrapper_constraint_get_active(const ufbx_constraint *constraint) { + return constraint ? constraint->active : false; +} diff --git a/ufbx/src/ufbx_wrapper.h b/ufbx/src/ufbx_wrapper.h index eede020..c652025 100644 --- a/ufbx/src/ufbx_wrapper.h +++ b/ufbx/src/ufbx_wrapper.h @@ -15,6 +15,18 @@ typedef struct ufbx_mesh ufbx_mesh; typedef struct ufbx_node ufbx_node; typedef struct ufbx_material ufbx_material; typedef struct ufbx_texture ufbx_texture; +typedef struct ufbx_light ufbx_light; +typedef struct ufbx_camera ufbx_camera; +typedef struct ufbx_bone ufbx_bone; +typedef struct ufbx_anim_stack ufbx_anim_stack; +typedef struct ufbx_anim_layer ufbx_anim_layer; +typedef struct ufbx_anim_curve ufbx_anim_curve; +typedef struct ufbx_skin_deformer ufbx_skin_deformer; +typedef struct ufbx_skin_cluster ufbx_skin_cluster; +typedef struct ufbx_blend_deformer ufbx_blend_deformer; +typedef struct ufbx_blend_channel ufbx_blend_channel; +typedef struct ufbx_blend_shape ufbx_blend_shape; +typedef struct ufbx_constraint ufbx_constraint; // Scene management ufbx_scene* ufbx_wrapper_load_file(const char *filename, char **error_msg); @@ -64,6 +76,120 @@ size_t ufbx_wrapper_mesh_get_num_materials(const ufbx_mesh *mesh); ufbx_material* ufbx_wrapper_mesh_get_material(const ufbx_mesh *mesh, size_t index); const char* ufbx_wrapper_material_get_name(const ufbx_material *material); +// Light access +size_t ufbx_wrapper_scene_get_num_lights(const ufbx_scene *scene); +ufbx_light* ufbx_wrapper_scene_get_light(const ufbx_scene *scene, size_t index); +ufbx_light* ufbx_wrapper_node_get_light(const ufbx_node *node); +const char* ufbx_wrapper_light_get_name(const ufbx_light *light); +void ufbx_wrapper_light_get_color(const ufbx_light *light, float *rgb); +double ufbx_wrapper_light_get_intensity(const ufbx_light *light); +void ufbx_wrapper_light_get_local_direction(const ufbx_light *light, float *xyz); +int ufbx_wrapper_light_get_type(const ufbx_light *light); +int ufbx_wrapper_light_get_decay(const ufbx_light *light); +int ufbx_wrapper_light_get_area_shape(const ufbx_light *light); +double ufbx_wrapper_light_get_inner_angle(const ufbx_light *light); +double ufbx_wrapper_light_get_outer_angle(const ufbx_light *light); +bool ufbx_wrapper_light_get_cast_light(const ufbx_light *light); +bool ufbx_wrapper_light_get_cast_shadows(const ufbx_light *light); + +// Camera access +size_t ufbx_wrapper_scene_get_num_cameras(const ufbx_scene *scene); +ufbx_camera* ufbx_wrapper_scene_get_camera(const ufbx_scene *scene, size_t index); +ufbx_camera* ufbx_wrapper_node_get_camera(const ufbx_node *node); +const char* ufbx_wrapper_camera_get_name(const ufbx_camera *camera); +int ufbx_wrapper_camera_get_projection_mode(const ufbx_camera *camera); +void ufbx_wrapper_camera_get_resolution(const ufbx_camera *camera, float *xy); +bool ufbx_wrapper_camera_get_resolution_is_pixels(const ufbx_camera *camera); +void ufbx_wrapper_camera_get_field_of_view_deg(const ufbx_camera *camera, float *xy); +void ufbx_wrapper_camera_get_field_of_view_tan(const ufbx_camera *camera, float *xy); +double ufbx_wrapper_camera_get_orthographic_extent(const ufbx_camera *camera); +void ufbx_wrapper_camera_get_orthographic_size(const ufbx_camera *camera, float *xy); +double ufbx_wrapper_camera_get_aspect_ratio(const ufbx_camera *camera); +double ufbx_wrapper_camera_get_near_plane(const ufbx_camera *camera); +double ufbx_wrapper_camera_get_far_plane(const ufbx_camera *camera); + +// Bone access +size_t ufbx_wrapper_scene_get_num_bones(const ufbx_scene *scene); +ufbx_bone* ufbx_wrapper_scene_get_bone(const ufbx_scene *scene, size_t index); +ufbx_bone* ufbx_wrapper_node_get_bone(const ufbx_node *node); +const char* ufbx_wrapper_bone_get_name(const ufbx_bone *bone); +double ufbx_wrapper_bone_get_radius(const ufbx_bone *bone); +double ufbx_wrapper_bone_get_relative_length(const ufbx_bone *bone); +bool ufbx_wrapper_bone_is_root(const ufbx_bone *bone); + +// Texture access +size_t ufbx_wrapper_scene_get_num_textures(const ufbx_scene *scene); +ufbx_texture* ufbx_wrapper_scene_get_texture(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_texture_get_name(const ufbx_texture *texture); +const char* ufbx_wrapper_texture_get_filename(const ufbx_texture *texture); +const char* ufbx_wrapper_texture_get_absolute_filename(const ufbx_texture *texture); +const char* ufbx_wrapper_texture_get_relative_filename(const ufbx_texture *texture); +int ufbx_wrapper_texture_get_type(const ufbx_texture *texture); + +// AnimStack access +size_t ufbx_wrapper_scene_get_num_anim_stacks(const ufbx_scene *scene); +ufbx_anim_stack* ufbx_wrapper_scene_get_anim_stack(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_anim_stack_get_name(const ufbx_anim_stack *anim_stack); +double ufbx_wrapper_anim_stack_get_time_begin(const ufbx_anim_stack *anim_stack); +double ufbx_wrapper_anim_stack_get_time_end(const ufbx_anim_stack *anim_stack); +size_t ufbx_wrapper_anim_stack_get_num_layers(const ufbx_anim_stack *anim_stack); +ufbx_anim_layer* ufbx_wrapper_anim_stack_get_layer(const ufbx_anim_stack *anim_stack, size_t index); + +// AnimLayer access +const char* ufbx_wrapper_anim_layer_get_name(const ufbx_anim_layer *anim_layer); +double ufbx_wrapper_anim_layer_get_weight(const ufbx_anim_layer *anim_layer); +bool ufbx_wrapper_anim_layer_get_weight_is_animated(const ufbx_anim_layer *anim_layer); +bool ufbx_wrapper_anim_layer_get_blended(const ufbx_anim_layer *anim_layer); +bool ufbx_wrapper_anim_layer_get_additive(const ufbx_anim_layer *anim_layer); +bool ufbx_wrapper_anim_layer_get_compose_rotation(const ufbx_anim_layer *anim_layer); +bool ufbx_wrapper_anim_layer_get_compose_scale(const ufbx_anim_layer *anim_layer); + +// AnimCurve access +size_t ufbx_wrapper_scene_get_num_anim_curves(const ufbx_scene *scene); +ufbx_anim_curve* ufbx_wrapper_scene_get_anim_curve(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_anim_curve_get_name(const ufbx_anim_curve *anim_curve); +size_t ufbx_wrapper_anim_curve_get_num_keyframes(const ufbx_anim_curve *anim_curve); +double ufbx_wrapper_anim_curve_get_min_value(const ufbx_anim_curve *anim_curve); +double ufbx_wrapper_anim_curve_get_max_value(const ufbx_anim_curve *anim_curve); +double ufbx_wrapper_anim_curve_get_min_time(const ufbx_anim_curve *anim_curve); +double ufbx_wrapper_anim_curve_get_max_time(const ufbx_anim_curve *anim_curve); + +// SkinDeformer access +size_t ufbx_wrapper_scene_get_num_skin_deformers(const ufbx_scene *scene); +ufbx_skin_deformer* ufbx_wrapper_scene_get_skin_deformer(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_skin_deformer_get_name(const ufbx_skin_deformer *skin_deformer); +size_t ufbx_wrapper_skin_deformer_get_num_clusters(const ufbx_skin_deformer *skin_deformer); +ufbx_skin_cluster* ufbx_wrapper_skin_deformer_get_cluster(const ufbx_skin_deformer *skin_deformer, size_t index); + +// SkinCluster access +const char* ufbx_wrapper_skin_cluster_get_name(const ufbx_skin_cluster *skin_cluster); +size_t ufbx_wrapper_skin_cluster_get_num_weights(const ufbx_skin_cluster *skin_cluster); + +// BlendDeformer access +size_t ufbx_wrapper_scene_get_num_blend_deformers(const ufbx_scene *scene); +ufbx_blend_deformer* ufbx_wrapper_scene_get_blend_deformer(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_blend_deformer_get_name(const ufbx_blend_deformer *blend_deformer); +size_t ufbx_wrapper_blend_deformer_get_num_channels(const ufbx_blend_deformer *blend_deformer); +ufbx_blend_channel* ufbx_wrapper_blend_deformer_get_channel(const ufbx_blend_deformer *blend_deformer, size_t index); + +// BlendChannel access +const char* ufbx_wrapper_blend_channel_get_name(const ufbx_blend_channel *blend_channel); +double ufbx_wrapper_blend_channel_get_weight(const ufbx_blend_channel *blend_channel); + +// BlendShape access +size_t ufbx_wrapper_scene_get_num_blend_shapes(const ufbx_scene *scene); +ufbx_blend_shape* ufbx_wrapper_scene_get_blend_shape(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_blend_shape_get_name(const ufbx_blend_shape *blend_shape); +size_t ufbx_wrapper_blend_shape_get_num_offsets(const ufbx_blend_shape *blend_shape); + +// Constraint access +size_t ufbx_wrapper_scene_get_num_constraints(const ufbx_scene *scene); +ufbx_constraint* ufbx_wrapper_scene_get_constraint(const ufbx_scene *scene, size_t index); +const char* ufbx_wrapper_constraint_get_name(const ufbx_constraint *constraint); +int ufbx_wrapper_constraint_get_type(const ufbx_constraint *constraint); +double ufbx_wrapper_constraint_get_weight(const ufbx_constraint *constraint); +bool ufbx_wrapper_constraint_get_active(const ufbx_constraint *constraint); + #ifdef __cplusplus } #endif