From eb522af4fec58876ac1b0a73ad9bcdae2d82d33f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 6 Mar 2020 16:19:35 +0100 Subject: Cleanup: move Alembic, AVI, Collada, and USD to `source/blender/io` This moves the `alembic`, `avi`, `collada`, and `usd` modules into a common `io` directory. This also cleans up some `#include "../../{somedir}/{somefile}.h"` by adding `../../io/{somedir}` to `CMakeLists.txt` and then just using `#include "{somefile}.h"`. No functional changes. --- source/blender/CMakeLists.txt | 16 +- source/blender/alembic/ABC_alembic.h | 141 -- source/blender/alembic/CMakeLists.txt | 110 - source/blender/alembic/intern/abc_customdata.cc | 484 ----- source/blender/alembic/intern/abc_customdata.h | 104 - source/blender/alembic/intern/abc_exporter.cc | 677 ------ source/blender/alembic/intern/abc_exporter.h | 128 -- .../blender/alembic/intern/abc_reader_archive.cc | 140 -- source/blender/alembic/intern/abc_reader_archive.h | 67 - source/blender/alembic/intern/abc_reader_camera.cc | 113 - source/blender/alembic/intern/abc_reader_camera.h | 40 - source/blender/alembic/intern/abc_reader_curves.cc | 354 ---- source/blender/alembic/intern/abc_reader_curves.h | 56 - source/blender/alembic/intern/abc_reader_mesh.cc | 889 -------- source/blender/alembic/intern/abc_reader_mesh.h | 86 - source/blender/alembic/intern/abc_reader_nurbs.cc | 225 -- source/blender/alembic/intern/abc_reader_nurbs.h | 40 - source/blender/alembic/intern/abc_reader_object.cc | 333 --- source/blender/alembic/intern/abc_reader_object.h | 171 -- source/blender/alembic/intern/abc_reader_points.cc | 157 -- source/blender/alembic/intern/abc_reader_points.h | 54 - .../blender/alembic/intern/abc_reader_transform.cc | 76 - .../blender/alembic/intern/abc_reader_transform.h | 42 - source/blender/alembic/intern/abc_util.cc | 393 ---- source/blender/alembic/intern/abc_util.h | 236 --- .../blender/alembic/intern/abc_writer_archive.cc | 114 - source/blender/alembic/intern/abc_writer_archive.h | 58 - source/blender/alembic/intern/abc_writer_camera.cc | 81 - source/blender/alembic/intern/abc_writer_camera.h | 45 - source/blender/alembic/intern/abc_writer_curves.cc | 189 -- source/blender/alembic/intern/abc_writer_curves.h | 55 - source/blender/alembic/intern/abc_writer_hair.cc | 292 --- source/blender/alembic/intern/abc_writer_hair.h | 62 - source/blender/alembic/intern/abc_writer_mball.cc | 97 - source/blender/alembic/intern/abc_writer_mball.h | 56 - source/blender/alembic/intern/abc_writer_mesh.cc | 592 ------ source/blender/alembic/intern/abc_writer_mesh.h | 91 - source/blender/alembic/intern/abc_writer_nurbs.cc | 172 -- source/blender/alembic/intern/abc_writer_nurbs.h | 42 - source/blender/alembic/intern/abc_writer_object.cc | 79 - source/blender/alembic/intern/abc_writer_object.h | 71 - source/blender/alembic/intern/abc_writer_points.cc | 123 -- source/blender/alembic/intern/abc_writer_points.h | 49 - .../blender/alembic/intern/abc_writer_transform.cc | 121 -- .../blender/alembic/intern/abc_writer_transform.h | 60 - source/blender/alembic/intern/alembic_capi.cc | 1052 --------- source/blender/avi/AVI_avi.h | 299 --- source/blender/avi/CMakeLists.txt | 53 - source/blender/avi/intern/avi.c | 1056 --------- source/blender/avi/intern/avi_codecs.c | 138 -- source/blender/avi/intern/avi_endian.c | 203 -- source/blender/avi/intern/avi_endian.h | 40 - source/blender/avi/intern/avi_intern.h | 65 - source/blender/avi/intern/avi_mjpeg.c | 547 ----- source/blender/avi/intern/avi_mjpeg.h | 30 - source/blender/avi/intern/avi_options.c | 148 -- source/blender/avi/intern/avi_rgb.c | 154 -- source/blender/avi/intern/avi_rgb.h | 30 - source/blender/avi/intern/avi_rgb32.c | 94 - source/blender/avi/intern/avi_rgb32.h | 30 - source/blender/blenkernel/CMakeLists.txt | 4 +- source/blender/blenloader/CMakeLists.txt | 2 +- source/blender/collada/AnimationClipExporter.cpp | 50 - source/blender/collada/AnimationClipExporter.h | 51 - source/blender/collada/AnimationExporter.cpp | 877 -------- source/blender/collada/AnimationExporter.h | 266 --- source/blender/collada/AnimationImporter.cpp | 2232 -------------------- source/blender/collada/AnimationImporter.h | 254 --- source/blender/collada/ArmatureExporter.cpp | 328 --- source/blender/collada/ArmatureExporter.h | 107 - source/blender/collada/ArmatureImporter.cpp | 1115 ---------- source/blender/collada/ArmatureImporter.h | 187 -- source/blender/collada/BCAnimationCurve.cpp | 691 ------ source/blender/collada/BCAnimationCurve.h | 152 -- source/blender/collada/BCAnimationSampler.cpp | 662 ------ source/blender/collada/BCAnimationSampler.h | 194 -- source/blender/collada/BCMath.cpp | 244 --- source/blender/collada/BCMath.h | 110 - source/blender/collada/BCSampleData.cpp | 97 - source/blender/collada/BCSampleData.h | 66 - source/blender/collada/BlenderContext.cpp | 156 -- source/blender/collada/BlenderContext.h | 73 - source/blender/collada/BlenderTypes.h | 48 - source/blender/collada/CMakeLists.txt | 147 -- source/blender/collada/CameraExporter.cpp | 98 - source/blender/collada/CameraExporter.h | 46 - source/blender/collada/ControllerExporter.cpp | 649 ------ source/blender/collada/ControllerExporter.h | 137 -- source/blender/collada/DocumentExporter.cpp | 346 --- source/blender/collada/DocumentExporter.h | 44 - source/blender/collada/DocumentImporter.cpp | 1265 ----------- source/blender/collada/DocumentImporter.h | 172 -- source/blender/collada/EffectExporter.cpp | 312 --- source/blender/collada/EffectExporter.h | 89 - source/blender/collada/ErrorHandler.cpp | 118 -- source/blender/collada/ErrorHandler.h | 57 - source/blender/collada/ExportSettings.cpp | 21 - source/blender/collada/ExportSettings.h | 295 --- source/blender/collada/ExtraHandler.cpp | 93 - source/blender/collada/ExtraHandler.h | 83 - source/blender/collada/ExtraTags.cpp | 126 -- source/blender/collada/ExtraTags.h | 77 - source/blender/collada/GeometryExporter.cpp | 718 ------- source/blender/collada/GeometryExporter.h | 140 -- source/blender/collada/ImageExporter.cpp | 169 -- source/blender/collada/ImageExporter.h | 51 - source/blender/collada/ImportSettings.cpp | 21 - source/blender/collada/ImportSettings.h | 34 - source/blender/collada/InstanceWriter.cpp | 70 - source/blender/collada/InstanceWriter.h | 35 - source/blender/collada/LightExporter.cpp | 156 -- source/blender/collada/LightExporter.h | 44 - source/blender/collada/MaterialExporter.cpp | 75 - source/blender/collada/MaterialExporter.h | 98 - source/blender/collada/Materials.cpp | 396 ---- source/blender/collada/Materials.h | 76 - source/blender/collada/MeshImporter.cpp | 1208 ----------- source/blender/collada/MeshImporter.h | 182 -- source/blender/collada/SceneExporter.cpp | 242 --- source/blender/collada/SceneExporter.h | 117 - source/blender/collada/SkinInfo.cpp | 357 ---- source/blender/collada/SkinInfo.h | 130 -- source/blender/collada/TransformReader.cpp | 151 -- source/blender/collada/TransformReader.h | 72 - source/blender/collada/TransformWriter.cpp | 141 -- source/blender/collada/TransformWriter.h | 48 - source/blender/collada/collada.cpp | 117 - source/blender/collada/collada.h | 50 - source/blender/collada/collada_internal.cpp | 340 --- source/blender/collada/collada_internal.h | 98 - source/blender/collada/collada_utils.cpp | 1458 ------------- source/blender/collada/collada_utils.h | 399 ---- source/blender/collada/version.conf | 1 - source/blender/editors/io/CMakeLists.txt | 6 +- source/blender/editors/io/io_collada.c | 2 +- source/blender/imbuf/CMakeLists.txt | 2 +- source/blender/io/CMakeLists.txt | 35 + source/blender/io/alembic/ABC_alembic.h | 141 ++ source/blender/io/alembic/CMakeLists.txt | 110 + source/blender/io/alembic/intern/abc_customdata.cc | 484 +++++ source/blender/io/alembic/intern/abc_customdata.h | 104 + source/blender/io/alembic/intern/abc_exporter.cc | 677 ++++++ source/blender/io/alembic/intern/abc_exporter.h | 128 ++ .../io/alembic/intern/abc_reader_archive.cc | 140 ++ .../blender/io/alembic/intern/abc_reader_archive.h | 67 + .../blender/io/alembic/intern/abc_reader_camera.cc | 113 + .../blender/io/alembic/intern/abc_reader_camera.h | 40 + .../blender/io/alembic/intern/abc_reader_curves.cc | 354 ++++ .../blender/io/alembic/intern/abc_reader_curves.h | 56 + .../blender/io/alembic/intern/abc_reader_mesh.cc | 889 ++++++++ source/blender/io/alembic/intern/abc_reader_mesh.h | 86 + .../blender/io/alembic/intern/abc_reader_nurbs.cc | 225 ++ .../blender/io/alembic/intern/abc_reader_nurbs.h | 40 + .../blender/io/alembic/intern/abc_reader_object.cc | 333 +++ .../blender/io/alembic/intern/abc_reader_object.h | 171 ++ .../blender/io/alembic/intern/abc_reader_points.cc | 157 ++ .../blender/io/alembic/intern/abc_reader_points.h | 54 + .../io/alembic/intern/abc_reader_transform.cc | 76 + .../io/alembic/intern/abc_reader_transform.h | 42 + source/blender/io/alembic/intern/abc_util.cc | 393 ++++ source/blender/io/alembic/intern/abc_util.h | 236 +++ .../io/alembic/intern/abc_writer_archive.cc | 114 + .../blender/io/alembic/intern/abc_writer_archive.h | 58 + .../blender/io/alembic/intern/abc_writer_camera.cc | 81 + .../blender/io/alembic/intern/abc_writer_camera.h | 45 + .../blender/io/alembic/intern/abc_writer_curves.cc | 189 ++ .../blender/io/alembic/intern/abc_writer_curves.h | 55 + .../blender/io/alembic/intern/abc_writer_hair.cc | 292 +++ source/blender/io/alembic/intern/abc_writer_hair.h | 62 + .../blender/io/alembic/intern/abc_writer_mball.cc | 97 + .../blender/io/alembic/intern/abc_writer_mball.h | 56 + .../blender/io/alembic/intern/abc_writer_mesh.cc | 592 ++++++ source/blender/io/alembic/intern/abc_writer_mesh.h | 91 + .../blender/io/alembic/intern/abc_writer_nurbs.cc | 172 ++ .../blender/io/alembic/intern/abc_writer_nurbs.h | 42 + .../blender/io/alembic/intern/abc_writer_object.cc | 79 + .../blender/io/alembic/intern/abc_writer_object.h | 71 + .../blender/io/alembic/intern/abc_writer_points.cc | 123 ++ .../blender/io/alembic/intern/abc_writer_points.h | 49 + .../io/alembic/intern/abc_writer_transform.cc | 121 ++ .../io/alembic/intern/abc_writer_transform.h | 60 + source/blender/io/alembic/intern/alembic_capi.cc | 1052 +++++++++ source/blender/io/avi/AVI_avi.h | 299 +++ source/blender/io/avi/CMakeLists.txt | 53 + source/blender/io/avi/intern/avi.c | 1056 +++++++++ source/blender/io/avi/intern/avi_codecs.c | 138 ++ source/blender/io/avi/intern/avi_endian.c | 203 ++ source/blender/io/avi/intern/avi_endian.h | 40 + source/blender/io/avi/intern/avi_intern.h | 65 + source/blender/io/avi/intern/avi_mjpeg.c | 547 +++++ source/blender/io/avi/intern/avi_mjpeg.h | 30 + source/blender/io/avi/intern/avi_options.c | 148 ++ source/blender/io/avi/intern/avi_rgb.c | 154 ++ source/blender/io/avi/intern/avi_rgb.h | 30 + source/blender/io/avi/intern/avi_rgb32.c | 94 + source/blender/io/avi/intern/avi_rgb32.h | 30 + .../blender/io/collada/AnimationClipExporter.cpp | 50 + source/blender/io/collada/AnimationClipExporter.h | 51 + source/blender/io/collada/AnimationExporter.cpp | 877 ++++++++ source/blender/io/collada/AnimationExporter.h | 266 +++ source/blender/io/collada/AnimationImporter.cpp | 2232 ++++++++++++++++++++ source/blender/io/collada/AnimationImporter.h | 254 +++ source/blender/io/collada/ArmatureExporter.cpp | 328 +++ source/blender/io/collada/ArmatureExporter.h | 107 + source/blender/io/collada/ArmatureImporter.cpp | 1115 ++++++++++ source/blender/io/collada/ArmatureImporter.h | 187 ++ source/blender/io/collada/BCAnimationCurve.cpp | 691 ++++++ source/blender/io/collada/BCAnimationCurve.h | 152 ++ source/blender/io/collada/BCAnimationSampler.cpp | 662 ++++++ source/blender/io/collada/BCAnimationSampler.h | 194 ++ source/blender/io/collada/BCMath.cpp | 244 +++ source/blender/io/collada/BCMath.h | 110 + source/blender/io/collada/BCSampleData.cpp | 97 + source/blender/io/collada/BCSampleData.h | 66 + source/blender/io/collada/BlenderContext.cpp | 156 ++ source/blender/io/collada/BlenderContext.h | 73 + source/blender/io/collada/BlenderTypes.h | 48 + source/blender/io/collada/CMakeLists.txt | 147 ++ source/blender/io/collada/CameraExporter.cpp | 98 + source/blender/io/collada/CameraExporter.h | 46 + source/blender/io/collada/ControllerExporter.cpp | 649 ++++++ source/blender/io/collada/ControllerExporter.h | 137 ++ source/blender/io/collada/DocumentExporter.cpp | 346 +++ source/blender/io/collada/DocumentExporter.h | 44 + source/blender/io/collada/DocumentImporter.cpp | 1265 +++++++++++ source/blender/io/collada/DocumentImporter.h | 172 ++ source/blender/io/collada/EffectExporter.cpp | 312 +++ source/blender/io/collada/EffectExporter.h | 89 + source/blender/io/collada/ErrorHandler.cpp | 118 ++ source/blender/io/collada/ErrorHandler.h | 57 + source/blender/io/collada/ExportSettings.cpp | 21 + source/blender/io/collada/ExportSettings.h | 295 +++ source/blender/io/collada/ExtraHandler.cpp | 93 + source/blender/io/collada/ExtraHandler.h | 83 + source/blender/io/collada/ExtraTags.cpp | 126 ++ source/blender/io/collada/ExtraTags.h | 77 + source/blender/io/collada/GeometryExporter.cpp | 718 +++++++ source/blender/io/collada/GeometryExporter.h | 140 ++ source/blender/io/collada/ImageExporter.cpp | 169 ++ source/blender/io/collada/ImageExporter.h | 51 + source/blender/io/collada/ImportSettings.cpp | 21 + source/blender/io/collada/ImportSettings.h | 34 + source/blender/io/collada/InstanceWriter.cpp | 70 + source/blender/io/collada/InstanceWriter.h | 35 + source/blender/io/collada/LightExporter.cpp | 156 ++ source/blender/io/collada/LightExporter.h | 44 + source/blender/io/collada/MaterialExporter.cpp | 75 + source/blender/io/collada/MaterialExporter.h | 98 + source/blender/io/collada/Materials.cpp | 396 ++++ source/blender/io/collada/Materials.h | 76 + source/blender/io/collada/MeshImporter.cpp | 1208 +++++++++++ source/blender/io/collada/MeshImporter.h | 182 ++ source/blender/io/collada/SceneExporter.cpp | 242 +++ source/blender/io/collada/SceneExporter.h | 117 + source/blender/io/collada/SkinInfo.cpp | 357 ++++ source/blender/io/collada/SkinInfo.h | 130 ++ source/blender/io/collada/TransformReader.cpp | 151 ++ source/blender/io/collada/TransformReader.h | 72 + source/blender/io/collada/TransformWriter.cpp | 141 ++ source/blender/io/collada/TransformWriter.h | 48 + source/blender/io/collada/collada.cpp | 117 + source/blender/io/collada/collada.h | 50 + source/blender/io/collada/collada_internal.cpp | 340 +++ source/blender/io/collada/collada_internal.h | 98 + source/blender/io/collada/collada_utils.cpp | 1458 +++++++++++++ source/blender/io/collada/collada_utils.h | 399 ++++ source/blender/io/collada/version.conf | 1 + source/blender/io/usd/CMakeLists.txt | 111 + .../io/usd/intern/abstract_hierarchy_iterator.cc | 595 ++++++ .../io/usd/intern/abstract_hierarchy_iterator.h | 251 +++ source/blender/io/usd/intern/usd_capi.cc | 233 ++ .../blender/io/usd/intern/usd_exporter_context.h | 44 + .../io/usd/intern/usd_hierarchy_iterator.cc | 150 ++ .../blender/io/usd/intern/usd_hierarchy_iterator.h | 71 + .../blender/io/usd/intern/usd_writer_abstract.cc | 147 ++ source/blender/io/usd/intern/usd_writer_abstract.h | 77 + source/blender/io/usd/intern/usd_writer_camera.cc | 111 + source/blender/io/usd/intern/usd_writer_camera.h | 38 + source/blender/io/usd/intern/usd_writer_hair.cc | 90 + source/blender/io/usd/intern/usd_writer_hair.h | 38 + source/blender/io/usd/intern/usd_writer_light.cc | 112 + source/blender/io/usd/intern/usd_writer_light.h | 37 + source/blender/io/usd/intern/usd_writer_mesh.cc | 489 +++++ source/blender/io/usd/intern/usd_writer_mesh.h | 66 + .../blender/io/usd/intern/usd_writer_metaball.cc | 81 + source/blender/io/usd/intern/usd_writer_metaball.h | 42 + .../blender/io/usd/intern/usd_writer_transform.cc | 64 + .../blender/io/usd/intern/usd_writer_transform.h | 42 + source/blender/io/usd/usd.h | 63 + source/blender/makesrna/intern/CMakeLists.txt | 2 +- source/blender/makesrna/intern/rna_cachefile.c | 2 +- source/blender/makesrna/intern/rna_scene_api.c | 2 +- source/blender/modifiers/CMakeLists.txt | 2 +- source/blender/python/intern/CMakeLists.txt | 4 +- source/blender/usd/CMakeLists.txt | 111 - .../usd/intern/abstract_hierarchy_iterator.cc | 595 ------ .../usd/intern/abstract_hierarchy_iterator.h | 251 --- source/blender/usd/intern/usd_capi.cc | 233 -- source/blender/usd/intern/usd_exporter_context.h | 44 - .../blender/usd/intern/usd_hierarchy_iterator.cc | 150 -- source/blender/usd/intern/usd_hierarchy_iterator.h | 71 - source/blender/usd/intern/usd_writer_abstract.cc | 147 -- source/blender/usd/intern/usd_writer_abstract.h | 77 - source/blender/usd/intern/usd_writer_camera.cc | 111 - source/blender/usd/intern/usd_writer_camera.h | 38 - source/blender/usd/intern/usd_writer_hair.cc | 90 - source/blender/usd/intern/usd_writer_hair.h | 38 - source/blender/usd/intern/usd_writer_light.cc | 112 - source/blender/usd/intern/usd_writer_light.h | 37 - source/blender/usd/intern/usd_writer_mesh.cc | 489 ----- source/blender/usd/intern/usd_writer_mesh.h | 66 - source/blender/usd/intern/usd_writer_metaball.cc | 81 - source/blender/usd/intern/usd_writer_metaball.h | 42 - source/blender/usd/intern/usd_writer_transform.cc | 64 - source/blender/usd/intern/usd_writer_transform.h | 42 - source/blender/usd/usd.h | 63 - tests/gtests/alembic/CMakeLists.txt | 2 +- tests/gtests/usd/CMakeLists.txt | 2 +- 318 files changed, 33837 insertions(+), 33816 deletions(-) delete mode 100644 source/blender/alembic/ABC_alembic.h delete mode 100644 source/blender/alembic/CMakeLists.txt delete mode 100644 source/blender/alembic/intern/abc_customdata.cc delete mode 100644 source/blender/alembic/intern/abc_customdata.h delete mode 100644 source/blender/alembic/intern/abc_exporter.cc delete mode 100644 source/blender/alembic/intern/abc_exporter.h delete mode 100644 source/blender/alembic/intern/abc_reader_archive.cc delete mode 100644 source/blender/alembic/intern/abc_reader_archive.h delete mode 100644 source/blender/alembic/intern/abc_reader_camera.cc delete mode 100644 source/blender/alembic/intern/abc_reader_camera.h delete mode 100644 source/blender/alembic/intern/abc_reader_curves.cc delete mode 100644 source/blender/alembic/intern/abc_reader_curves.h delete mode 100644 source/blender/alembic/intern/abc_reader_mesh.cc delete mode 100644 source/blender/alembic/intern/abc_reader_mesh.h delete mode 100644 source/blender/alembic/intern/abc_reader_nurbs.cc delete mode 100644 source/blender/alembic/intern/abc_reader_nurbs.h delete mode 100644 source/blender/alembic/intern/abc_reader_object.cc delete mode 100644 source/blender/alembic/intern/abc_reader_object.h delete mode 100644 source/blender/alembic/intern/abc_reader_points.cc delete mode 100644 source/blender/alembic/intern/abc_reader_points.h delete mode 100644 source/blender/alembic/intern/abc_reader_transform.cc delete mode 100644 source/blender/alembic/intern/abc_reader_transform.h delete mode 100644 source/blender/alembic/intern/abc_util.cc delete mode 100644 source/blender/alembic/intern/abc_util.h delete mode 100644 source/blender/alembic/intern/abc_writer_archive.cc delete mode 100644 source/blender/alembic/intern/abc_writer_archive.h delete mode 100644 source/blender/alembic/intern/abc_writer_camera.cc delete mode 100644 source/blender/alembic/intern/abc_writer_camera.h delete mode 100644 source/blender/alembic/intern/abc_writer_curves.cc delete mode 100644 source/blender/alembic/intern/abc_writer_curves.h delete mode 100644 source/blender/alembic/intern/abc_writer_hair.cc delete mode 100644 source/blender/alembic/intern/abc_writer_hair.h delete mode 100644 source/blender/alembic/intern/abc_writer_mball.cc delete mode 100644 source/blender/alembic/intern/abc_writer_mball.h delete mode 100644 source/blender/alembic/intern/abc_writer_mesh.cc delete mode 100644 source/blender/alembic/intern/abc_writer_mesh.h delete mode 100644 source/blender/alembic/intern/abc_writer_nurbs.cc delete mode 100644 source/blender/alembic/intern/abc_writer_nurbs.h delete mode 100644 source/blender/alembic/intern/abc_writer_object.cc delete mode 100644 source/blender/alembic/intern/abc_writer_object.h delete mode 100644 source/blender/alembic/intern/abc_writer_points.cc delete mode 100644 source/blender/alembic/intern/abc_writer_points.h delete mode 100644 source/blender/alembic/intern/abc_writer_transform.cc delete mode 100644 source/blender/alembic/intern/abc_writer_transform.h delete mode 100644 source/blender/alembic/intern/alembic_capi.cc delete mode 100644 source/blender/avi/AVI_avi.h delete mode 100644 source/blender/avi/CMakeLists.txt delete mode 100644 source/blender/avi/intern/avi.c delete mode 100644 source/blender/avi/intern/avi_codecs.c delete mode 100644 source/blender/avi/intern/avi_endian.c delete mode 100644 source/blender/avi/intern/avi_endian.h delete mode 100644 source/blender/avi/intern/avi_intern.h delete mode 100644 source/blender/avi/intern/avi_mjpeg.c delete mode 100644 source/blender/avi/intern/avi_mjpeg.h delete mode 100644 source/blender/avi/intern/avi_options.c delete mode 100644 source/blender/avi/intern/avi_rgb.c delete mode 100644 source/blender/avi/intern/avi_rgb.h delete mode 100644 source/blender/avi/intern/avi_rgb32.c delete mode 100644 source/blender/avi/intern/avi_rgb32.h delete mode 100644 source/blender/collada/AnimationClipExporter.cpp delete mode 100644 source/blender/collada/AnimationClipExporter.h delete mode 100644 source/blender/collada/AnimationExporter.cpp delete mode 100644 source/blender/collada/AnimationExporter.h delete mode 100644 source/blender/collada/AnimationImporter.cpp delete mode 100644 source/blender/collada/AnimationImporter.h delete mode 100644 source/blender/collada/ArmatureExporter.cpp delete mode 100644 source/blender/collada/ArmatureExporter.h delete mode 100644 source/blender/collada/ArmatureImporter.cpp delete mode 100644 source/blender/collada/ArmatureImporter.h delete mode 100644 source/blender/collada/BCAnimationCurve.cpp delete mode 100644 source/blender/collada/BCAnimationCurve.h delete mode 100644 source/blender/collada/BCAnimationSampler.cpp delete mode 100644 source/blender/collada/BCAnimationSampler.h delete mode 100644 source/blender/collada/BCMath.cpp delete mode 100644 source/blender/collada/BCMath.h delete mode 100644 source/blender/collada/BCSampleData.cpp delete mode 100644 source/blender/collada/BCSampleData.h delete mode 100644 source/blender/collada/BlenderContext.cpp delete mode 100644 source/blender/collada/BlenderContext.h delete mode 100644 source/blender/collada/BlenderTypes.h delete mode 100644 source/blender/collada/CMakeLists.txt delete mode 100644 source/blender/collada/CameraExporter.cpp delete mode 100644 source/blender/collada/CameraExporter.h delete mode 100644 source/blender/collada/ControllerExporter.cpp delete mode 100644 source/blender/collada/ControllerExporter.h delete mode 100644 source/blender/collada/DocumentExporter.cpp delete mode 100644 source/blender/collada/DocumentExporter.h delete mode 100644 source/blender/collada/DocumentImporter.cpp delete mode 100644 source/blender/collada/DocumentImporter.h delete mode 100644 source/blender/collada/EffectExporter.cpp delete mode 100644 source/blender/collada/EffectExporter.h delete mode 100644 source/blender/collada/ErrorHandler.cpp delete mode 100644 source/blender/collada/ErrorHandler.h delete mode 100644 source/blender/collada/ExportSettings.cpp delete mode 100644 source/blender/collada/ExportSettings.h delete mode 100644 source/blender/collada/ExtraHandler.cpp delete mode 100644 source/blender/collada/ExtraHandler.h delete mode 100644 source/blender/collada/ExtraTags.cpp delete mode 100644 source/blender/collada/ExtraTags.h delete mode 100644 source/blender/collada/GeometryExporter.cpp delete mode 100644 source/blender/collada/GeometryExporter.h delete mode 100644 source/blender/collada/ImageExporter.cpp delete mode 100644 source/blender/collada/ImageExporter.h delete mode 100644 source/blender/collada/ImportSettings.cpp delete mode 100644 source/blender/collada/ImportSettings.h delete mode 100644 source/blender/collada/InstanceWriter.cpp delete mode 100644 source/blender/collada/InstanceWriter.h delete mode 100644 source/blender/collada/LightExporter.cpp delete mode 100644 source/blender/collada/LightExporter.h delete mode 100644 source/blender/collada/MaterialExporter.cpp delete mode 100644 source/blender/collada/MaterialExporter.h delete mode 100644 source/blender/collada/Materials.cpp delete mode 100644 source/blender/collada/Materials.h delete mode 100644 source/blender/collada/MeshImporter.cpp delete mode 100644 source/blender/collada/MeshImporter.h delete mode 100644 source/blender/collada/SceneExporter.cpp delete mode 100644 source/blender/collada/SceneExporter.h delete mode 100644 source/blender/collada/SkinInfo.cpp delete mode 100644 source/blender/collada/SkinInfo.h delete mode 100644 source/blender/collada/TransformReader.cpp delete mode 100644 source/blender/collada/TransformReader.h delete mode 100644 source/blender/collada/TransformWriter.cpp delete mode 100644 source/blender/collada/TransformWriter.h delete mode 100644 source/blender/collada/collada.cpp delete mode 100644 source/blender/collada/collada.h delete mode 100644 source/blender/collada/collada_internal.cpp delete mode 100644 source/blender/collada/collada_internal.h delete mode 100644 source/blender/collada/collada_utils.cpp delete mode 100644 source/blender/collada/collada_utils.h delete mode 100644 source/blender/collada/version.conf create mode 100644 source/blender/io/CMakeLists.txt create mode 100644 source/blender/io/alembic/ABC_alembic.h create mode 100644 source/blender/io/alembic/CMakeLists.txt create mode 100644 source/blender/io/alembic/intern/abc_customdata.cc create mode 100644 source/blender/io/alembic/intern/abc_customdata.h create mode 100644 source/blender/io/alembic/intern/abc_exporter.cc create mode 100644 source/blender/io/alembic/intern/abc_exporter.h create mode 100644 source/blender/io/alembic/intern/abc_reader_archive.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_archive.h create mode 100644 source/blender/io/alembic/intern/abc_reader_camera.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_camera.h create mode 100644 source/blender/io/alembic/intern/abc_reader_curves.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_curves.h create mode 100644 source/blender/io/alembic/intern/abc_reader_mesh.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_mesh.h create mode 100644 source/blender/io/alembic/intern/abc_reader_nurbs.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_nurbs.h create mode 100644 source/blender/io/alembic/intern/abc_reader_object.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_object.h create mode 100644 source/blender/io/alembic/intern/abc_reader_points.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_points.h create mode 100644 source/blender/io/alembic/intern/abc_reader_transform.cc create mode 100644 source/blender/io/alembic/intern/abc_reader_transform.h create mode 100644 source/blender/io/alembic/intern/abc_util.cc create mode 100644 source/blender/io/alembic/intern/abc_util.h create mode 100644 source/blender/io/alembic/intern/abc_writer_archive.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_archive.h create mode 100644 source/blender/io/alembic/intern/abc_writer_camera.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_camera.h create mode 100644 source/blender/io/alembic/intern/abc_writer_curves.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_curves.h create mode 100644 source/blender/io/alembic/intern/abc_writer_hair.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_hair.h create mode 100644 source/blender/io/alembic/intern/abc_writer_mball.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_mball.h create mode 100644 source/blender/io/alembic/intern/abc_writer_mesh.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_mesh.h create mode 100644 source/blender/io/alembic/intern/abc_writer_nurbs.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_nurbs.h create mode 100644 source/blender/io/alembic/intern/abc_writer_object.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_object.h create mode 100644 source/blender/io/alembic/intern/abc_writer_points.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_points.h create mode 100644 source/blender/io/alembic/intern/abc_writer_transform.cc create mode 100644 source/blender/io/alembic/intern/abc_writer_transform.h create mode 100644 source/blender/io/alembic/intern/alembic_capi.cc create mode 100644 source/blender/io/avi/AVI_avi.h create mode 100644 source/blender/io/avi/CMakeLists.txt create mode 100644 source/blender/io/avi/intern/avi.c create mode 100644 source/blender/io/avi/intern/avi_codecs.c create mode 100644 source/blender/io/avi/intern/avi_endian.c create mode 100644 source/blender/io/avi/intern/avi_endian.h create mode 100644 source/blender/io/avi/intern/avi_intern.h create mode 100644 source/blender/io/avi/intern/avi_mjpeg.c create mode 100644 source/blender/io/avi/intern/avi_mjpeg.h create mode 100644 source/blender/io/avi/intern/avi_options.c create mode 100644 source/blender/io/avi/intern/avi_rgb.c create mode 100644 source/blender/io/avi/intern/avi_rgb.h create mode 100644 source/blender/io/avi/intern/avi_rgb32.c create mode 100644 source/blender/io/avi/intern/avi_rgb32.h create mode 100644 source/blender/io/collada/AnimationClipExporter.cpp create mode 100644 source/blender/io/collada/AnimationClipExporter.h create mode 100644 source/blender/io/collada/AnimationExporter.cpp create mode 100644 source/blender/io/collada/AnimationExporter.h create mode 100644 source/blender/io/collada/AnimationImporter.cpp create mode 100644 source/blender/io/collada/AnimationImporter.h create mode 100644 source/blender/io/collada/ArmatureExporter.cpp create mode 100644 source/blender/io/collada/ArmatureExporter.h create mode 100644 source/blender/io/collada/ArmatureImporter.cpp create mode 100644 source/blender/io/collada/ArmatureImporter.h create mode 100644 source/blender/io/collada/BCAnimationCurve.cpp create mode 100644 source/blender/io/collada/BCAnimationCurve.h create mode 100644 source/blender/io/collada/BCAnimationSampler.cpp create mode 100644 source/blender/io/collada/BCAnimationSampler.h create mode 100644 source/blender/io/collada/BCMath.cpp create mode 100644 source/blender/io/collada/BCMath.h create mode 100644 source/blender/io/collada/BCSampleData.cpp create mode 100644 source/blender/io/collada/BCSampleData.h create mode 100644 source/blender/io/collada/BlenderContext.cpp create mode 100644 source/blender/io/collada/BlenderContext.h create mode 100644 source/blender/io/collada/BlenderTypes.h create mode 100644 source/blender/io/collada/CMakeLists.txt create mode 100644 source/blender/io/collada/CameraExporter.cpp create mode 100644 source/blender/io/collada/CameraExporter.h create mode 100644 source/blender/io/collada/ControllerExporter.cpp create mode 100644 source/blender/io/collada/ControllerExporter.h create mode 100644 source/blender/io/collada/DocumentExporter.cpp create mode 100644 source/blender/io/collada/DocumentExporter.h create mode 100644 source/blender/io/collada/DocumentImporter.cpp create mode 100644 source/blender/io/collada/DocumentImporter.h create mode 100644 source/blender/io/collada/EffectExporter.cpp create mode 100644 source/blender/io/collada/EffectExporter.h create mode 100644 source/blender/io/collada/ErrorHandler.cpp create mode 100644 source/blender/io/collada/ErrorHandler.h create mode 100644 source/blender/io/collada/ExportSettings.cpp create mode 100644 source/blender/io/collada/ExportSettings.h create mode 100644 source/blender/io/collada/ExtraHandler.cpp create mode 100644 source/blender/io/collada/ExtraHandler.h create mode 100644 source/blender/io/collada/ExtraTags.cpp create mode 100644 source/blender/io/collada/ExtraTags.h create mode 100644 source/blender/io/collada/GeometryExporter.cpp create mode 100644 source/blender/io/collada/GeometryExporter.h create mode 100644 source/blender/io/collada/ImageExporter.cpp create mode 100644 source/blender/io/collada/ImageExporter.h create mode 100644 source/blender/io/collada/ImportSettings.cpp create mode 100644 source/blender/io/collada/ImportSettings.h create mode 100644 source/blender/io/collada/InstanceWriter.cpp create mode 100644 source/blender/io/collada/InstanceWriter.h create mode 100644 source/blender/io/collada/LightExporter.cpp create mode 100644 source/blender/io/collada/LightExporter.h create mode 100644 source/blender/io/collada/MaterialExporter.cpp create mode 100644 source/blender/io/collada/MaterialExporter.h create mode 100644 source/blender/io/collada/Materials.cpp create mode 100644 source/blender/io/collada/Materials.h create mode 100644 source/blender/io/collada/MeshImporter.cpp create mode 100644 source/blender/io/collada/MeshImporter.h create mode 100644 source/blender/io/collada/SceneExporter.cpp create mode 100644 source/blender/io/collada/SceneExporter.h create mode 100644 source/blender/io/collada/SkinInfo.cpp create mode 100644 source/blender/io/collada/SkinInfo.h create mode 100644 source/blender/io/collada/TransformReader.cpp create mode 100644 source/blender/io/collada/TransformReader.h create mode 100644 source/blender/io/collada/TransformWriter.cpp create mode 100644 source/blender/io/collada/TransformWriter.h create mode 100644 source/blender/io/collada/collada.cpp create mode 100644 source/blender/io/collada/collada.h create mode 100644 source/blender/io/collada/collada_internal.cpp create mode 100644 source/blender/io/collada/collada_internal.h create mode 100644 source/blender/io/collada/collada_utils.cpp create mode 100644 source/blender/io/collada/collada_utils.h create mode 100644 source/blender/io/collada/version.conf create mode 100644 source/blender/io/usd/CMakeLists.txt create mode 100644 source/blender/io/usd/intern/abstract_hierarchy_iterator.cc create mode 100644 source/blender/io/usd/intern/abstract_hierarchy_iterator.h create mode 100644 source/blender/io/usd/intern/usd_capi.cc create mode 100644 source/blender/io/usd/intern/usd_exporter_context.h create mode 100644 source/blender/io/usd/intern/usd_hierarchy_iterator.cc create mode 100644 source/blender/io/usd/intern/usd_hierarchy_iterator.h create mode 100644 source/blender/io/usd/intern/usd_writer_abstract.cc create mode 100644 source/blender/io/usd/intern/usd_writer_abstract.h create mode 100644 source/blender/io/usd/intern/usd_writer_camera.cc create mode 100644 source/blender/io/usd/intern/usd_writer_camera.h create mode 100644 source/blender/io/usd/intern/usd_writer_hair.cc create mode 100644 source/blender/io/usd/intern/usd_writer_hair.h create mode 100644 source/blender/io/usd/intern/usd_writer_light.cc create mode 100644 source/blender/io/usd/intern/usd_writer_light.h create mode 100644 source/blender/io/usd/intern/usd_writer_mesh.cc create mode 100644 source/blender/io/usd/intern/usd_writer_mesh.h create mode 100644 source/blender/io/usd/intern/usd_writer_metaball.cc create mode 100644 source/blender/io/usd/intern/usd_writer_metaball.h create mode 100644 source/blender/io/usd/intern/usd_writer_transform.cc create mode 100644 source/blender/io/usd/intern/usd_writer_transform.h create mode 100644 source/blender/io/usd/usd.h delete mode 100644 source/blender/usd/CMakeLists.txt delete mode 100644 source/blender/usd/intern/abstract_hierarchy_iterator.cc delete mode 100644 source/blender/usd/intern/abstract_hierarchy_iterator.h delete mode 100644 source/blender/usd/intern/usd_capi.cc delete mode 100644 source/blender/usd/intern/usd_exporter_context.h delete mode 100644 source/blender/usd/intern/usd_hierarchy_iterator.cc delete mode 100644 source/blender/usd/intern/usd_hierarchy_iterator.h delete mode 100644 source/blender/usd/intern/usd_writer_abstract.cc delete mode 100644 source/blender/usd/intern/usd_writer_abstract.h delete mode 100644 source/blender/usd/intern/usd_writer_camera.cc delete mode 100644 source/blender/usd/intern/usd_writer_camera.h delete mode 100644 source/blender/usd/intern/usd_writer_hair.cc delete mode 100644 source/blender/usd/intern/usd_writer_hair.h delete mode 100644 source/blender/usd/intern/usd_writer_light.cc delete mode 100644 source/blender/usd/intern/usd_writer_light.h delete mode 100644 source/blender/usd/intern/usd_writer_mesh.cc delete mode 100644 source/blender/usd/intern/usd_writer_mesh.h delete mode 100644 source/blender/usd/intern/usd_writer_metaball.cc delete mode 100644 source/blender/usd/intern/usd_writer_metaball.h delete mode 100644 source/blender/usd/intern/usd_writer_transform.cc delete mode 100644 source/blender/usd/intern/usd_writer_transform.h delete mode 100644 source/blender/usd/usd.h diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index 4f33dcaa10d..b44b6db8804 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -112,6 +112,7 @@ add_subdirectory(nodes) add_subdirectory(modifiers) add_subdirectory(gpencil_modifiers) add_subdirectory(shader_fx) +add_subdirectory(io) add_subdirectory(makesdna) add_subdirectory(makesrna) @@ -135,29 +136,14 @@ if(WITH_IMAGE_CINEON) add_subdirectory(imbuf/intern/cineon) endif() -if(WITH_CODEC_AVI) - add_subdirectory(avi) -endif() - if(WITH_PYTHON) add_subdirectory(python) endif() -if(WITH_OPENCOLLADA) - add_subdirectory(collada) -endif() - if(WITH_FREESTYLE) add_subdirectory(freestyle) endif() -if(WITH_ALEMBIC) - add_subdirectory(alembic) -endif() - if(WIN32) add_subdirectory(blendthumb) endif() -if(WITH_USD) - add_subdirectory(usd) -endif() diff --git a/source/blender/alembic/ABC_alembic.h b/source/blender/alembic/ABC_alembic.h deleted file mode 100644 index 878dbfc2a53..00000000000 --- a/source/blender/alembic/ABC_alembic.h +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_ALEMBIC_H__ -#define __ABC_ALEMBIC_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -struct CacheReader; -struct ListBase; -struct Main; -struct Mesh; -struct Object; -struct Scene; -struct bContext; - -typedef struct AbcArchiveHandle AbcArchiveHandle; - -enum { - ABC_ARCHIVE_OGAWA = 0, - ABC_ARCHIVE_HDF5 = 1, -}; - -int ABC_get_version(void); - -struct AlembicExportParams { - double frame_start; - double frame_end; - - unsigned int frame_samples_xform; - unsigned int frame_samples_shape; - - double shutter_open; - double shutter_close; - - bool selected_only; - bool uvs; - bool normals; - bool vcolors; - bool apply_subdiv; - bool curves_as_mesh; - bool flatten_hierarchy; - bool visible_objects_only; - bool renderable_only; - bool face_sets; - bool use_subdiv_schema; - bool packuv; - bool triangulate; - bool export_hair; - bool export_particles; - - unsigned int compression_type : 1; - - /* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx - * in DNA_modifier_types.h */ - int quad_method; - int ngon_method; - - float global_scale; -}; - -/* The ABC_export and ABC_import functions both take a as_background_job - * parameter, and return a boolean. - * - * When as_background_job=true, returns false immediately after scheduling - * a background job. - * - * When as_background_job=false, performs the export synchronously, and returns - * true when the export was ok, and false if there were any errors. - */ - -bool ABC_export(struct Scene *scene, - struct bContext *C, - const char *filepath, - const struct AlembicExportParams *params, - bool as_background_job); - -bool ABC_import(struct bContext *C, - const char *filepath, - float scale, - bool is_sequence, - bool set_frame_range, - int sequence_len, - int offset, - bool validate_meshes, - bool as_background_job); - -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - struct ListBase *object_paths); - -void ABC_free_handle(AbcArchiveHandle *handle); - -void ABC_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); - -/* Either modifies current_mesh in-place or constructs a new mesh. */ -struct Mesh *ABC_read_mesh(struct CacheReader *reader, - struct Object *ob, - struct Mesh *current_mesh, - const float time, - const char **err_str, - int flags); - -bool ABC_mesh_topology_changed(struct CacheReader *reader, - struct Object *ob, - struct Mesh *existing_mesh, - const float time, - const char **err_str); - -void CacheReader_incref(struct CacheReader *reader); -void CacheReader_free(struct CacheReader *reader); - -struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle, - struct CacheReader *reader, - struct Object *object, - const char *object_path); - -#ifdef __cplusplus -} -#endif - -#endif /* __ABC_ALEMBIC_H__ */ diff --git a/source/blender/alembic/CMakeLists.txt b/source/blender/alembic/CMakeLists.txt deleted file mode 100644 index 8bb7051982a..00000000000 --- a/source/blender/alembic/CMakeLists.txt +++ /dev/null @@ -1,110 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The Original Code is Copyright (C) 2006, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -set(INC - . - ../blenkernel - ../blenlib - ../blenloader - ../bmesh - ../depsgraph - ../editors/include - ../makesdna - ../makesrna - ../windowmanager - ../../../intern/guardedalloc - ../../../intern/utfconv -) - -set(INC_SYS - ${ALEMBIC_INCLUDE_DIRS} - ${BOOST_INCLUDE_DIR} - ${HDF5_INCLUDE_DIRS} - ${OPENEXR_INCLUDE_DIRS} -) - -set(SRC - intern/abc_customdata.cc - intern/abc_exporter.cc - intern/abc_reader_archive.cc - intern/abc_reader_camera.cc - intern/abc_reader_curves.cc - intern/abc_reader_mesh.cc - intern/abc_reader_nurbs.cc - intern/abc_reader_object.cc - intern/abc_reader_points.cc - intern/abc_reader_transform.cc - intern/abc_util.cc - intern/abc_writer_archive.cc - intern/abc_writer_camera.cc - intern/abc_writer_curves.cc - intern/abc_writer_hair.cc - intern/abc_writer_mball.cc - intern/abc_writer_mesh.cc - intern/abc_writer_nurbs.cc - intern/abc_writer_object.cc - intern/abc_writer_points.cc - intern/abc_writer_transform.cc - intern/alembic_capi.cc - - ABC_alembic.h - intern/abc_customdata.h - intern/abc_exporter.h - intern/abc_reader_archive.h - intern/abc_reader_camera.h - intern/abc_reader_curves.h - intern/abc_reader_mesh.h - intern/abc_reader_nurbs.h - intern/abc_reader_object.h - intern/abc_reader_points.h - intern/abc_reader_transform.h - intern/abc_util.h - intern/abc_writer_archive.h - intern/abc_writer_camera.h - intern/abc_writer_curves.h - intern/abc_writer_hair.h - intern/abc_writer_mball.h - intern/abc_writer_mesh.h - intern/abc_writer_nurbs.h - intern/abc_writer_object.h - intern/abc_writer_points.h - intern/abc_writer_transform.h -) - -set(LIB - bf_blenkernel - bf_blenlib - - ${ALEMBIC_LIBRARIES} - ${OPENEXR_LIBRARIES} -) - -if(WITH_ALEMBIC_HDF5) - add_definitions(-DWITH_ALEMBIC_HDF5) - list(APPEND LIB - ${HDF5_LIBRARIES} - ) -endif() - -list(APPEND LIB - ${BOOST_LIBRARIES} -) - -blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/alembic/intern/abc_customdata.cc b/source/blender/alembic/intern/abc_customdata.cc deleted file mode 100644 index c5f60ac3e29..00000000000 --- a/source/blender/alembic/intern/abc_customdata.cc +++ /dev/null @@ -1,484 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_customdata.h" - -#include -#include -#include - -extern "C" { -#include "DNA_customdata_types.h" -#include "DNA_meshdata_types.h" - -#include "BLI_math_base.h" -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -} - -/* NOTE: for now only UVs and Vertex Colors are supported for streaming. - * Although Alembic only allows for a single UV layer per {I|O}Schema, and does - * not have a vertex color concept, there is a convention between DCCs to write - * such data in a way that lets other DCC know what they are for. See comments - * in the write code for the conventions. */ - -using Alembic::AbcGeom::kFacevaryingScope; -using Alembic::AbcGeom::kVertexScope; - -using Alembic::Abc::C4fArraySample; -using Alembic::Abc::UInt32ArraySample; -using Alembic::Abc::V2fArraySample; - -using Alembic::AbcGeom::OC4fGeomParam; -using Alembic::AbcGeom::OV2fGeomParam; - -static void get_uvs(const CDStreamConfig &config, - std::vector &uvs, - std::vector &uvidx, - void *cd_data) -{ - MLoopUV *mloopuv_array = static_cast(cd_data); - - if (!mloopuv_array) { - return; - } - - const int num_poly = config.totpoly; - MPoly *polygons = config.mpoly; - MLoop *mloop = config.mloop; - - if (!config.pack_uvs) { - int cnt = 0; - uvidx.resize(config.totloop); - uvs.resize(config.totloop); - - /* Iterate in reverse order to match exported polygons. */ - for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; - - for (int j = 0; j < current_poly.totloop; j++, cnt++) { - loopuv--; - - uvidx[cnt] = cnt; - uvs[cnt][0] = loopuv->uv[0]; - uvs[cnt][1] = loopuv->uv[1]; - } - } - } - else { - /* Mapping for indexed UVs, deduplicating UV coordinates at vertices. */ - std::vector> idx_map(config.totvert); - int idx_count = 0; - - for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; - MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop; - MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; - - for (int j = 0; j < current_poly.totloop; j++) { - looppoly--; - loopuv--; - - Imath::V2f uv(loopuv->uv[0], loopuv->uv[1]); - bool found_same = false; - - /* Find UV already in uvs array. */ - for (uint32_t uv_idx : idx_map[looppoly->v]) { - if (uvs[uv_idx] == uv) { - found_same = true; - uvidx.push_back(uv_idx); - break; - } - } - - /* UV doesn't exists for this vertex, add it. */ - if (!found_same) { - uint32_t uv_idx = idx_count++; - idx_map[looppoly->v].push_back(uv_idx); - uvidx.push_back(uv_idx); - uvs.push_back(uv); - } - } - } - } -} - -const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data) -{ - const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV); - - if (active_uvlayer < 0) { - return ""; - } - - void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); - - get_uvs(config, sample.uvs, sample.indices, cd_data); - - return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer); -} - -/* Convention to write UVs: - * - V2fGeomParam on the arbGeomParam - * - set scope as face varying - * - (optional due to its behavior) tag as UV using Alembic::AbcGeom::SetIsUV - */ -static void write_uv(const OCompoundProperty &prop, - const CDStreamConfig &config, - void *data, - const char *name) -{ - std::vector indices; - std::vector uvs; - - get_uvs(config, uvs, indices, data); - - if (indices.empty() || uvs.empty()) { - return; - } - - OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); - - OV2fGeomParam::Sample sample(V2fArraySample(&uvs.front(), uvs.size()), - UInt32ArraySample(&indices.front(), indices.size()), - kFacevaryingScope); - - param.set(sample); -} - -/* Convention to write Vertex Colors: - * - C3fGeomParam/C4fGeomParam on the arbGeomParam - * - set scope as vertex varying - */ -static void write_mcol(const OCompoundProperty &prop, - const CDStreamConfig &config, - void *data, - const char *name) -{ - const float cscale = 1.0f / 255.0f; - MPoly *polys = config.mpoly; - MLoop *mloops = config.mloop; - MCol *cfaces = static_cast(data); - - std::vector buffer; - std::vector indices; - - buffer.reserve(config.totvert); - indices.reserve(config.totvert); - - Imath::C4f col; - - for (int i = 0; i < config.totpoly; i++) { - MPoly *p = &polys[i]; - MCol *cface = &cfaces[p->loopstart + p->totloop]; - MLoop *mloop = &mloops[p->loopstart + p->totloop]; - - for (int j = 0; j < p->totloop; j++) { - cface--; - mloop--; - - col[0] = cface->a * cscale; - col[1] = cface->r * cscale; - col[2] = cface->g * cscale; - col[3] = cface->b * cscale; - - buffer.push_back(col); - indices.push_back(buffer.size() - 1); - } - } - - OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1); - - OC4fGeomParam::Sample sample(C4fArraySample(&buffer.front(), buffer.size()), - UInt32ArraySample(&indices.front(), indices.size()), - kVertexScope); - - param.set(sample); -} - -void write_custom_data(const OCompoundProperty &prop, - const CDStreamConfig &config, - CustomData *data, - int data_type) -{ - CustomDataType cd_data_type = static_cast(data_type); - - if (!CustomData_has_layer(data, cd_data_type)) { - return; - } - - const int active_layer = CustomData_get_active_layer(data, cd_data_type); - const int tot_layers = CustomData_number_of_layers(data, cd_data_type); - - for (int i = 0; i < tot_layers; i++) { - void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); - const char *name = CustomData_get_layer_name(data, cd_data_type, i); - - if (cd_data_type == CD_MLOOPUV) { - /* Already exported. */ - if (i == active_layer) { - continue; - } - - write_uv(prop, config, cd_data, name); - } - else if (cd_data_type == CD_MLOOPCOL) { - write_mcol(prop, config, cd_data, name); - } - } -} - -/* ************************************************************************** */ - -using Alembic::Abc::C3fArraySamplePtr; -using Alembic::Abc::C4fArraySamplePtr; -using Alembic::Abc::PropertyHeader; - -using Alembic::AbcGeom::IC3fGeomParam; -using Alembic::AbcGeom::IC4fGeomParam; -using Alembic::AbcGeom::IV2fGeomParam; - -static void read_uvs(const CDStreamConfig &config, - void *data, - const Alembic::AbcGeom::V2fArraySamplePtr &uvs, - const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) -{ - MPoly *mpolys = config.mpoly; - MLoopUV *mloopuvs = static_cast(data); - - unsigned int uv_index, loop_index, rev_loop_index; - - for (int i = 0; i < config.totpoly; i++) { - MPoly &poly = mpolys[i]; - unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1; - - for (int f = 0; f < poly.totloop; f++) { - loop_index = poly.loopstart + f; - rev_loop_index = rev_loop_offset - f; - uv_index = (*indices)[loop_index]; - const Imath::V2f &uv = (*uvs)[uv_index]; - - MLoopUV &loopuv = mloopuvs[rev_loop_index]; - loopuv.uv[0] = uv[0]; - loopuv.uv[1] = uv[1]; - } - } -} - -static size_t mcols_out_of_bounds_check(const size_t color_index, - const size_t array_size, - const std::string &iobject_full_name, - const PropertyHeader &prop_header, - bool &r_is_out_of_bounds, - bool &r_bounds_warning_given) -{ - if (color_index < array_size) { - return color_index; - } - - if (!r_bounds_warning_given) { - std::cerr << "Alembic: color index out of bounds " - "reading face colors for object " - << iobject_full_name << ", property " << prop_header.getName() << std::endl; - r_bounds_warning_given = true; - } - r_is_out_of_bounds = true; - return 0; -} - -static void read_custom_data_mcols(const std::string &iobject_full_name, - const ICompoundProperty &arbGeomParams, - const PropertyHeader &prop_header, - const CDStreamConfig &config, - const Alembic::Abc::ISampleSelector &iss) -{ - C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr(); - C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr(); - Alembic::Abc::UInt32ArraySamplePtr indices; - bool use_c3f_ptr; - bool is_facevarying; - - /* Find the correct interpretation of the data */ - if (IC3fGeomParam::matches(prop_header)) { - IC3fGeomParam color_param(arbGeomParams, prop_header.getName()); - IC3fGeomParam::Sample sample; - BLI_assert(!strcmp("rgb", color_param.getInterpretation())); - - color_param.getIndexed(sample, iss); - is_facevarying = sample.getScope() == kFacevaryingScope && - config.totloop == sample.getIndices()->size(); - - c3f_ptr = sample.getVals(); - indices = sample.getIndices(); - use_c3f_ptr = true; - } - else if (IC4fGeomParam::matches(prop_header)) { - IC4fGeomParam color_param(arbGeomParams, prop_header.getName()); - IC4fGeomParam::Sample sample; - BLI_assert(!strcmp("rgba", color_param.getInterpretation())); - - color_param.getIndexed(sample, iss); - is_facevarying = sample.getScope() == kFacevaryingScope && - config.totloop == sample.getIndices()->size(); - - c4f_ptr = sample.getVals(); - indices = sample.getIndices(); - use_c3f_ptr = false; - } - else { - /* this won't happen due to the checks in read_custom_data() */ - return; - } - BLI_assert(c3f_ptr || c4f_ptr); - - /* Read the vertex colors */ - void *cd_data = config.add_customdata_cb( - config.mesh, prop_header.getName().c_str(), CD_MLOOPCOL); - MCol *cfaces = static_cast(cd_data); - MPoly *mpolys = config.mpoly; - MLoop *mloops = config.mloop; - - size_t face_index = 0; - size_t color_index; - bool bounds_warning_given = false; - - /* The colors can go through two layers of indexing. Often the 'indices' - * array doesn't do anything (i.e. indices[n] = n), but when it does, it's - * important. Blender 2.79 writes indices incorrectly (see T53745), which - * is why we have to check for indices->size() > 0 */ - bool use_dual_indexing = is_facevarying && indices->size() > 0; - - for (int i = 0; i < config.totpoly; i++) { - MPoly *poly = &mpolys[i]; - MCol *cface = &cfaces[poly->loopstart + poly->totloop]; - MLoop *mloop = &mloops[poly->loopstart + poly->totloop]; - - for (int j = 0; j < poly->totloop; j++, face_index++) { - cface--; - mloop--; - - color_index = is_facevarying ? face_index : mloop->v; - if (use_dual_indexing) { - color_index = (*indices)[color_index]; - } - if (use_c3f_ptr) { - bool is_mcols_out_of_bounds = false; - color_index = mcols_out_of_bounds_check(color_index, - c3f_ptr->size(), - iobject_full_name, - prop_header, - is_mcols_out_of_bounds, - bounds_warning_given); - if (is_mcols_out_of_bounds) { - continue; - } - const Imath::C3f &color = (*c3f_ptr)[color_index]; - cface->a = unit_float_to_uchar_clamp(color[0]); - cface->r = unit_float_to_uchar_clamp(color[1]); - cface->g = unit_float_to_uchar_clamp(color[2]); - cface->b = 255; - } - else { - bool is_mcols_out_of_bounds = false; - color_index = mcols_out_of_bounds_check(color_index, - c4f_ptr->size(), - iobject_full_name, - prop_header, - is_mcols_out_of_bounds, - bounds_warning_given); - if (is_mcols_out_of_bounds) { - continue; - } - const Imath::C4f &color = (*c4f_ptr)[color_index]; - cface->a = unit_float_to_uchar_clamp(color[0]); - cface->r = unit_float_to_uchar_clamp(color[1]); - cface->g = unit_float_to_uchar_clamp(color[2]); - cface->b = unit_float_to_uchar_clamp(color[3]); - } - } - } -} - -static void read_custom_data_uvs(const ICompoundProperty &prop, - const PropertyHeader &prop_header, - const CDStreamConfig &config, - const Alembic::Abc::ISampleSelector &iss) -{ - IV2fGeomParam uv_param(prop, prop_header.getName()); - - if (!uv_param.isIndexed()) { - return; - } - - IV2fGeomParam::Sample sample; - uv_param.getIndexed(sample, iss); - - if (uv_param.getScope() != kFacevaryingScope) { - return; - } - - void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV); - - read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); -} - -void read_custom_data(const std::string &iobject_full_name, - const ICompoundProperty &prop, - const CDStreamConfig &config, - const Alembic::Abc::ISampleSelector &iss) -{ - if (!prop.valid()) { - return; - } - - int num_uvs = 0; - int num_colors = 0; - - const size_t num_props = prop.getNumProperties(); - - for (size_t i = 0; i < num_props; i++) { - const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i); - - /* Read UVs according to convention. */ - if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) { - if (++num_uvs > MAX_MTFACE) { - continue; - } - - read_custom_data_uvs(prop, prop_header, config, iss); - continue; - } - - /* Read vertex colors according to convention. */ - if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) { - if (++num_colors > MAX_MCOL) { - continue; - } - - read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss); - continue; - } - } -} diff --git a/source/blender/alembic/intern/abc_customdata.h b/source/blender/alembic/intern/abc_customdata.h deleted file mode 100644 index 6107e230627..00000000000 --- a/source/blender/alembic/intern/abc_customdata.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_CUSTOMDATA_H__ -#define __ABC_CUSTOMDATA_H__ - -#include -#include - -struct CustomData; -struct MLoop; -struct MLoopUV; -struct MPoly; -struct MVert; -struct Mesh; - -using Alembic::Abc::ICompoundProperty; -using Alembic::Abc::OCompoundProperty; - -struct UVSample { - std::vector uvs; - std::vector indices; -}; - -struct CDStreamConfig { - MLoop *mloop; - int totloop; - - MPoly *mpoly; - int totpoly; - - MVert *mvert; - int totvert; - - MLoopUV *mloopuv; - - CustomData *loopdata; - - bool pack_uvs; - - /* TODO(kevin): might need a better way to handle adding and/or updating - * custom data such that it updates the custom data holder and its pointers properly. */ - Mesh *mesh; - void *(*add_customdata_cb)(Mesh *mesh, const char *name, int data_type); - - float weight; - float time; - Alembic::AbcGeom::index_t index; - Alembic::AbcGeom::index_t ceil_index; - - CDStreamConfig() - : mloop(NULL), - totloop(0), - mpoly(NULL), - totpoly(0), - totvert(0), - pack_uvs(false), - mesh(NULL), - add_customdata_cb(NULL), - weight(0.0f), - time(0.0f), - index(0), - ceil_index(0) - { - } -}; - -/* Get the UVs for the main UV property on a OSchema. - * Returns the name of the UV layer. - * - * For now the active layer is used, maybe needs a better way to choose this. */ -const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); - -void write_custom_data(const OCompoundProperty &prop, - const CDStreamConfig &config, - CustomData *data, - int data_type); - -void read_custom_data(const std::string &iobject_full_name, - const ICompoundProperty &prop, - const CDStreamConfig &config, - const Alembic::Abc::ISampleSelector &iss); - -#endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/alembic/intern/abc_exporter.cc b/source/blender/alembic/intern/abc_exporter.cc deleted file mode 100644 index a58b0a29e5e..00000000000 --- a/source/blender/alembic/intern/abc_exporter.cc +++ /dev/null @@ -1,677 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_exporter.h" - -#include - -#include "abc_writer_archive.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mball.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_camera_types.h" -#include "DNA_curve_types.h" -#include "DNA_meta_types.h" -#include "DNA_mesh_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_space_types.h" /* for FILE_MAX */ -#include "DNA_fluid_types.h" - -#include "BLI_string.h" - -#ifdef WIN32 -/* needed for MSCV because of snprintf from BLI_string */ -# include "BLI_winstuff.h" -#endif - -#include "BKE_anim.h" -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_layer.h" -#include "BKE_main.h" -#include "BKE_mball.h" -#include "BKE_modifier.h" -#include "BKE_particle.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph_query.h" -} - -using Alembic::Abc::OBox3dProperty; -using Alembic::Abc::TimeSamplingPtr; - -/* ************************************************************************** */ - -ExportSettings::ExportSettings() - : scene(NULL), - view_layer(NULL), - depsgraph(NULL), - logger(), - selected_only(false), - visible_objects_only(false), - renderable_only(false), - frame_start(1), - frame_end(1), - frame_samples_xform(1), - frame_samples_shape(1), - shutter_open(0.0), - shutter_close(1.0), - global_scale(1.0f), - flatten_hierarchy(false), - export_normals(false), - export_uvs(false), - export_vcols(false), - export_face_sets(false), - export_vweigths(false), - export_hair(true), - export_particles(true), - apply_subdiv(false), - use_subdiv_schema(false), - export_child_hairs(true), - export_ogawa(true), - pack_uv(false), - triangulate(false), - quad_method(0), - ngon_method(0) -{ -} - -static bool object_is_smoke_sim(Object *ob) -{ - ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid); - - if (md) { - FluidModifierData *smd = reinterpret_cast(md); - return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain && - smd->domain->type == FLUID_DOMAIN_TYPE_GAS); - } - - return false; -} - -static bool object_type_is_exportable(Scene *scene, Object *ob) -{ - switch (ob->type) { - case OB_MESH: - if (object_is_smoke_sim(ob)) { - return false; - } - - return true; - case OB_EMPTY: - case OB_CURVE: - case OB_SURF: - case OB_CAMERA: - return true; - case OB_MBALL: - return AbcMBallWriter::isBasisBall(scene, ob); - default: - return false; - } -} - -/** - * Returns whether this object should be exported into the Alembic file. - * - * \param settings: export settings, used for options like 'selected only'. - * \param ob: the object's base in question. - * \param is_duplicated: Normally false; true when the object is instanced - * into the scene by a dupli-object (e.g. part of a dupligroup). - * This ignores selection and layer visibility, - * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. - */ -static bool export_object(const ExportSettings *const settings, - const Base *const base, - bool is_duplicated) -{ - if (!is_duplicated) { - View3D *v3d = NULL; - - /* These two tests only make sense when the object isn't being instanced - * into the scene. When it is, its exportability is determined by - * its dupli-object and the DupliObject::no_draw property. */ - if (settings->selected_only && !BASE_SELECTED(v3d, base)) { - return false; - } - // FIXME Sybren: handle these cleanly (maybe just remove code), - // now using active scene layer instead. - if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) { - return false; - } - } - - Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object); - if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { - /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the - * evaluated copy to export. This will be handled more elegantly in the new - * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids - * a BLI_assert() failure getting the evaluated mesh of this object. */ - return false; - } - - // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { - // return false; - // } - - return true; -} - -/* ************************************************************************** */ - -AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings) - : m_bmain(bmain), - m_settings(settings), - m_filename(filename), - m_trans_sampling_index(0), - m_shape_sampling_index(0), - m_writer(NULL) -{ -} - -AbcExporter::~AbcExporter() -{ - /* Free xforms map */ - m_xforms_type::iterator it_x, e_x; - for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { - delete it_x->second; - } - - /* Free shapes vector */ - for (int i = 0, e = m_shapes.size(); i != e; i++) { - delete m_shapes[i]; - } - - delete m_writer; -} - -void AbcExporter::getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples) -{ - Scene *scene = m_settings.scene; /* for use in the FPS macro */ - samples.clear(); - - unsigned int frame_offset = time_relative ? m_settings.frame_start : 0; - double time_factor = time_relative ? FPS : 1.0; - double shutter_open = m_settings.shutter_open; - double shutter_close = m_settings.shutter_close; - double time_inc = (shutter_close - shutter_open) / nr_of_samples; - - /* sample between shutter open & close */ - for (int sample = 0; sample < nr_of_samples; sample++) { - double sample_time = shutter_open + time_inc * sample; - double time = (frame_offset + sample_time) / time_factor; - - samples.push_back(time); - } -} - -Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) -{ - std::vector samples; - - if (m_settings.frame_start == m_settings.frame_end) { - return TimeSamplingPtr(new Alembic::Abc::TimeSampling()); - } - - getShutterSamples(step, true, samples); - - /* TODO(Sybren): shouldn't we use the FPS macro here? */ - Alembic::Abc::TimeSamplingType ts(static_cast(samples.size()), - 1.0 / m_settings.scene->r.frs_sec); - - return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); -} - -void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set &frames) -{ - frames.clear(); - - std::vector shutter_samples; - - getShutterSamples(nr_of_samples, false, shutter_samples); - - for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) { - for (size_t j = 0; j < nr_of_samples; j++) { - frames.insert(frame + shutter_samples[j]); - } - } -} - -void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled) -{ - std::string abc_scene_name; - - if (m_bmain->name[0] != '\0') { - char scene_file_name[FILE_MAX]; - BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX); - abc_scene_name = scene_file_name; - } - else { - abc_scene_name = "untitled"; - } - - m_writer = new ArchiveWriter( - m_filename, abc_scene_name, m_settings.scene, m_settings.export_ogawa); - - /* Create time samplings for transforms and shapes. */ - - TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform); - - m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time); - - TimeSamplingPtr shape_time; - - if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) || - (m_settings.frame_start == m_settings.frame_end)) { - shape_time = trans_time; - m_shape_sampling_index = m_trans_sampling_index; - } - else { - shape_time = createTimeSampling(m_settings.frame_samples_shape); - m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time); - } - - OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds( - m_writer->archive(), m_trans_sampling_index); - - createTransformWritersHierarchy(); - createShapeWriters(); - - /* Make a list of frames to export. */ - - std::set xform_frames; - getFrameSet(m_settings.frame_samples_xform, xform_frames); - - std::set shape_frames; - getFrameSet(m_settings.frame_samples_shape, shape_frames); - - /* Merge all frames needed. */ - std::set frames(xform_frames); - frames.insert(shape_frames.begin(), shape_frames.end()); - - /* Export all frames. */ - - std::set::const_iterator begin = frames.begin(); - std::set::const_iterator end = frames.end(); - - const float size = static_cast(frames.size()); - size_t i = 0; - - for (; begin != end; ++begin) { - *progress = (++i / size); - *do_update = 1; - - if (G.is_break) { - *was_canceled = true; - break; - } - - const double frame = *begin; - - /* 'frame' is offset by start frame, so need to cancel the offset. */ - setCurrentFrame(m_bmain, frame); - - if (shape_frames.count(frame) != 0) { - for (int i = 0, e = m_shapes.size(); i != e; i++) { - m_shapes[i]->write(); - } - } - - if (xform_frames.count(frame) == 0) { - continue; - } - - m_xforms_type::iterator xit, xe; - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - xit->second->write(); - } - - /* Save the archive 's bounding box. */ - Imath::Box3d bounds; - - for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { - Imath::Box3d box = xit->second->bounds(); - bounds.extendBy(box); - } - - archive_bounds_prop.set(bounds); - } -} - -void AbcExporter::createTransformWritersHierarchy() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - Object *ob = base->object; - - if (export_object(&m_settings, base, false)) { - switch (ob->type) { - case OB_LAMP: - case OB_LATTICE: - case OB_SPEAKER: - /* We do not export transforms for objects of these classes. */ - break; - default: - exploreTransform(base, ob, ob->parent, NULL); - } - } - } -} - -void AbcExporter::exploreTransform(Base *base, - Object *object, - Object *parent, - Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - if (object_type_is_exportable(m_settings.scene, ob)) { - createTransformWriter(ob, parent, dupliObParent); - } - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - Object *dupli_ob = NULL; - Object *dupli_parent = NULL; - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - - if (link->type == OB_DUPLICOLLECTION) { - dupli_ob = link->ob; - dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; - - exploreTransform(base, dupli_ob, dupli_parent, ob); - } - } - - free_object_duplilist(lb); - } -} - -AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, - Object *parent, - Object *dupliObParent) -{ - /* An object should not be its own parent, or we'll get infinite loops. */ - BLI_assert(ob != parent); - BLI_assert(ob != dupliObParent); - - std::string name; - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - /* check if we have already created a transform writer for this object */ - AbcTransformWriter *my_writer = getXForm(name); - if (my_writer != NULL) { - return my_writer; - } - - AbcTransformWriter *parent_writer = NULL; - Alembic::Abc::OObject alembic_parent; - - if (m_settings.flatten_hierarchy || parent == NULL) { - /* Parentless objects still have the "top object" as parent - * in Alembic. */ - alembic_parent = m_writer->archive().getTop(); - } - else { - /* Since there are so many different ways to find parents (as evident - * in the number of conditions below), we can't really look up the - * parent by name. We'll just call createTransformWriter(), which will - * return the parent's AbcTransformWriter pointer. */ - if (parent->parent) { - if (parent == dupliObParent) { - parent_writer = createTransformWriter(parent, parent->parent, NULL); - } - else { - parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); - } - } - else if (parent == dupliObParent) { - if (dupliObParent->parent == NULL) { - parent_writer = createTransformWriter(parent, NULL, NULL); - } - else { - parent_writer = createTransformWriter( - parent, dupliObParent->parent, dupliObParent->parent); - } - } - else { - parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); - } - - BLI_assert(parent_writer); - alembic_parent = parent_writer->alembicXform(); - } - - my_writer = new AbcTransformWriter( - ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); - - /* When flattening, the matrix of the dupliobject has to be added. */ - if (m_settings.flatten_hierarchy && dupliObParent) { - my_writer->m_proxy_from = dupliObParent; - } - - m_xforms[name] = my_writer; - return my_writer; -} - -void AbcExporter::createShapeWriters() -{ - for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; - base = base->next) { - exploreObject(base, base->object, NULL); - } -} - -void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent) -{ - /* If an object isn't exported itself, its duplilist shouldn't be - * exported either. */ - if (!export_object(&m_settings, base, dupliObParent != NULL)) { - return; - } - - Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); - createShapeWriter(ob, dupliObParent); - - ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); - - if (lb) { - DupliObject *link = static_cast(lb->first); - - for (; link; link = link->next) { - /* This skips things like custom bone shapes. */ - if (m_settings.renderable_only && link->no_draw) { - continue; - } - if (link->type == OB_DUPLICOLLECTION) { - exploreObject(base, link->ob, ob); - } - } - - free_object_duplilist(lb); - } -} - -void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) -{ - if (!m_settings.export_hair && !m_settings.export_particles) { - return; - } - - ParticleSystem *psys = static_cast(ob->particlesystem.first); - - for (; psys; psys = psys->next) { - if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { - continue; - } - - if (m_settings.export_hair && psys->part->type == PART_HAIR) { - m_settings.export_child_hairs = true; - m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - else if (m_settings.export_particles && - (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || - psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || - psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || - psys->part->type == PART_FLUID_SPRAYFOAM || - psys->part->type == PART_FLUID_SPRAYBUBBLE || - psys->part->type == PART_FLUID_FOAMBUBBLE || - psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { - m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); - } - } -} - -void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) -{ - if (!object_type_is_exportable(m_settings.scene, ob)) { - return; - } - - std::string name; - - if (m_settings.flatten_hierarchy) { - name = get_id_name(ob); - } - else { - name = get_object_dag_path_name(ob, dupliObParent); - } - - AbcTransformWriter *xform = getXForm(name); - - if (!xform) { - ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; - return; - } - - createParticleSystemsWriters(ob, xform); - - switch (ob->type) { - case OB_MESH: { - Mesh *me = static_cast(ob->data); - - if (!me) { - return; - } - - m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings)); - break; - } - case OB_SURF: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CURVE: { - Curve *cu = static_cast(ob->data); - - if (!cu) { - return; - } - - AbcObjectWriter *writer; - if (m_settings.curves_as_mesh) { - writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); - } - else { - writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings); - } - m_shapes.push_back(writer); - break; - } - case OB_CAMERA: { - Camera *cam = static_cast(ob->data); - - if (cam->type == CAM_PERSP) { - m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings)); - } - - break; - } - case OB_MBALL: { - MetaBall *mball = static_cast(ob->data); - if (!mball) { - return; - } - - m_shapes.push_back( - new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings)); - break; - } - } -} - -AbcTransformWriter *AbcExporter::getXForm(const std::string &name) -{ - std::map::iterator it = m_xforms.find(name); - - if (it == m_xforms.end()) { - return NULL; - } - - return it->second; -} - -void AbcExporter::setCurrentFrame(Main *bmain, double t) -{ - m_settings.scene->r.cfra = static_cast(t); - m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; - BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); -} diff --git a/source/blender/alembic/intern/abc_exporter.h b/source/blender/alembic/intern/abc_exporter.h deleted file mode 100644 index 398004d2ec5..00000000000 --- a/source/blender/alembic/intern/abc_exporter.h +++ /dev/null @@ -1,128 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_EXPORTER_H__ -#define __ABC_EXPORTER_H__ - -#include -#include -#include -#include - -#include "abc_util.h" - -class AbcObjectWriter; -class AbcTransformWriter; -class ArchiveWriter; - -struct Base; -struct Depsgraph; -struct Main; -struct Object; -struct Scene; -struct ViewLayer; - -struct ExportSettings { - ExportSettings(); - - Scene *scene; - /** Scene layer to export; all its objects will be exported, unless selected_only=true. */ - ViewLayer *view_layer; - Depsgraph *depsgraph; - SimpleLogger logger; - - bool selected_only; - bool visible_objects_only; - bool renderable_only; - - double frame_start, frame_end; - double frame_samples_xform; - double frame_samples_shape; - double shutter_open; - double shutter_close; - float global_scale; - - bool flatten_hierarchy; - - bool export_normals; - bool export_uvs; - bool export_vcols; - bool export_face_sets; - bool export_vweigths; - bool export_hair; - bool export_particles; - - bool apply_subdiv; - bool curves_as_mesh; - bool use_subdiv_schema; - bool export_child_hairs; - bool export_ogawa; - bool pack_uv; - bool triangulate; - - int quad_method; - int ngon_method; -}; - -class AbcExporter { - Main *m_bmain; - ExportSettings &m_settings; - - const char *m_filename; - - unsigned int m_trans_sampling_index, m_shape_sampling_index; - - ArchiveWriter *m_writer; - - /* mapping from name to transform writer */ - typedef std::map m_xforms_type; - m_xforms_type m_xforms; - - std::vector m_shapes; - - public: - AbcExporter(Main *bmain, const char *filename, ExportSettings &settings); - ~AbcExporter(); - - void operator()(short *do_update, float *progress, bool *was_canceled); - - protected: - void getShutterSamples(unsigned int nr_of_samples, - bool time_relative, - std::vector &samples); - void getFrameSet(unsigned int nr_of_samples, std::set &frames); - - private: - Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); - - void createTransformWritersHierarchy(); - AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); - void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent); - void exploreObject(Base *base, Object *object, Object *dupliObParent); - void createShapeWriters(); - void createShapeWriter(Object *ob, Object *dupliObParent); - void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); - - AbcTransformWriter *getXForm(const std::string &name); - - void setCurrentFrame(Main *bmain, double t); -}; - -#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_archive.cc b/source/blender/alembic/intern/abc_reader_archive.cc deleted file mode 100644 index 6ad44553701..00000000000 --- a/source/blender/alembic/intern/abc_reader_archive.cc +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_archive.h" - -extern "C" { -#include "BKE_main.h" - -#include "BLI_path_util.h" -#include "BLI_string.h" -} - -#ifdef WIN32 -# include "utfconv.h" -#endif - -#include - -using Alembic::Abc::ErrorHandler; -using Alembic::Abc::Exception; -using Alembic::Abc::IArchive; -using Alembic::Abc::kWrapExisting; - -static IArchive open_archive(const std::string &filename, - const std::vector &input_streams, - bool &is_hdf5) -{ - is_hdf5 = false; - - try { - Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams); - - return IArchive(archive_reader(filename), kWrapExisting, ErrorHandler::kThrowPolicy); - } - catch (const Exception &e) { - std::cerr << e.what() << '\n'; - -#ifdef WITH_ALEMBIC_HDF5 - try { - is_hdf5 = true; - Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr; - - return IArchive(Alembic::AbcCoreHDF5::ReadArchive(), - filename.c_str(), - ErrorHandler::kThrowPolicy, - cache_ptr); - } - catch (const Exception &) { - std::cerr << e.what() << '\n'; - return IArchive(); - } -#else - /* Inspect the file to see whether it's really a HDF5 file. */ - char header[4]; /* char(0x89) + "HDF" */ - std::ifstream the_file(filename.c_str(), std::ios::in | std::ios::binary); - if (!the_file) { - std::cerr << "Unable to open " << filename << std::endl; - } - else if (!the_file.read(header, sizeof(header))) { - std::cerr << "Unable to read from " << filename << std::endl; - } - else if (strncmp(header + 1, "HDF", 3)) { - std::cerr << filename << " has an unknown file format, unable to read." << std::endl; - } - else { - is_hdf5 = true; - std::cerr << filename << " is in the obsolete HDF5 format, unable to read." << std::endl; - } - - if (the_file.is_open()) { - the_file.close(); - } - - return IArchive(); -#endif - } - - return IArchive(); -} - -ArchiveReader::ArchiveReader(struct Main *bmain, const char *filename) -{ - char abs_filename[FILE_MAX]; - BLI_strncpy(abs_filename, filename, FILE_MAX); - BLI_path_abs(abs_filename, BKE_main_blendfile_path(bmain)); - -#ifdef WIN32 - UTF16_ENCODE(abs_filename); - std::wstring wstr(abs_filename_16); - m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary); - UTF16_UN_ENCODE(abs_filename); -#else - m_infile.open(abs_filename, std::ios::in | std::ios::binary); -#endif - - m_streams.push_back(&m_infile); - - m_archive = open_archive(abs_filename, m_streams, m_is_hdf5); - - /* We can't open an HDF5 file from a stream, so close it. */ - if (m_is_hdf5) { - m_infile.close(); - m_streams.clear(); - } -} - -bool ArchiveReader::is_hdf5() const -{ - return m_is_hdf5; -} - -bool ArchiveReader::valid() const -{ - return m_archive.valid(); -} - -Alembic::Abc::IObject ArchiveReader::getTop() -{ - return m_archive.getTop(); -} diff --git a/source/blender/alembic/intern/abc_reader_archive.h b/source/blender/alembic/intern/abc_reader_archive.h deleted file mode 100644 index bdb53bd0b8c..00000000000 --- a/source/blender/alembic/intern/abc_reader_archive.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_ARCHIVE_H__ -#define __ABC_READER_ARCHIVE_H__ - -#include - -#ifdef WITH_ALEMBIC_HDF5 -# include -#endif - -#include - -#include - -struct Main; -struct Scene; - -/* Wrappers around input and output archives. The goal is to be able to use - * streams so that unicode paths work on Windows (T49112), and to make sure that - * the stream objects remain valid as long as the archives are open. - */ - -class ArchiveReader { - Alembic::Abc::IArchive m_archive; - std::ifstream m_infile; - std::vector m_streams; - bool m_is_hdf5; - - public: - ArchiveReader(struct Main *bmain, const char *filename); - - bool valid() const; - - /** - * Returns true when either Blender is compiled with HDF5 support and - * the archive was successfully opened (valid() will also return true), - * or when Blender was built without HDF5 support but a HDF5 file was - * detected (valid() will return false). - */ - bool is_hdf5() const; - - Alembic::Abc::IObject getTop(); -}; - -#endif /* __ABC_READER_ARCHIVE_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_camera.cc b/source/blender/alembic/intern/abc_reader_camera.cc deleted file mode 100644 index ab506f32cbe..00000000000 --- a/source/blender/alembic/intern/abc_reader_camera.cc +++ /dev/null @@ -1,113 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_camera.h" -#include "abc_reader_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_camera_types.h" -#include "DNA_object_types.h" - -#include "BKE_camera.h" -#include "BKE_object.h" - -#include "BLI_math.h" -} - -using Alembic::AbcGeom::CameraSample; -using Alembic::AbcGeom::ICamera; -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::IFloatProperty; -using Alembic::AbcGeom::ISampleSelector; -using Alembic::AbcGeom::kWrapExisting; - -AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - ICamera abc_cam(m_iobject, kWrapExisting); - m_schema = abc_cam.getSchema(); - - get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); -} - -bool AbcCameraReader::valid() const -{ - return m_schema.valid(); -} - -bool AbcCameraReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::ICamera::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to Camera when importing, but not any " - "more."; - return false; - } - - if (ob->type != OB_CAMERA) { - *err_str = "Object type mismatch, Alembic object path points to Camera."; - return false; - } - - return true; -} - -void AbcCameraReader::readObjectData(Main *bmain, const ISampleSelector &sample_sel) -{ - Camera *bcam = static_cast(BKE_camera_add(bmain, m_data_name.c_str())); - - CameraSample cam_sample; - m_schema.get(cam_sample, sample_sel); - - ICompoundProperty customDataContainer = m_schema.getUserProperties(); - - if (customDataContainer.valid() && customDataContainer.getPropertyHeader("stereoDistance") && - customDataContainer.getPropertyHeader("eyeSeparation")) { - IFloatProperty convergence_plane(customDataContainer, "stereoDistance"); - IFloatProperty eye_separation(customDataContainer, "eyeSeparation"); - - bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel); - bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel); - } - - const float lens = static_cast(cam_sample.getFocalLength()); - const float apperture_x = static_cast(cam_sample.getHorizontalAperture()); - const float apperture_y = static_cast(cam_sample.getVerticalAperture()); - const float h_film_offset = static_cast(cam_sample.getHorizontalFilmOffset()); - const float v_film_offset = static_cast(cam_sample.getVerticalFilmOffset()); - const float film_aspect = apperture_x / apperture_y; - - bcam->lens = lens; - bcam->sensor_x = apperture_x * 10; - bcam->sensor_y = apperture_y * 10; - bcam->shiftx = h_film_offset / apperture_x; - bcam->shifty = v_film_offset / apperture_y / film_aspect; - bcam->clip_start = max_ff(0.1f, static_cast(cam_sample.getNearClippingPlane())); - bcam->clip_end = static_cast(cam_sample.getFarClippingPlane()); - bcam->dof.focus_distance = static_cast(cam_sample.getFocusDistance()); - bcam->dof.aperture_fstop = static_cast(cam_sample.getFStop()); - - m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); - m_object->data = bcam; -} diff --git a/source/blender/alembic/intern/abc_reader_camera.h b/source/blender/alembic/intern/abc_reader_camera.h deleted file mode 100644 index 1d9763b0454..00000000000 --- a/source/blender/alembic/intern/abc_reader_camera.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_CAMERA_H__ -#define __ABC_READER_CAMERA_H__ - -#include "abc_reader_object.h" - -class AbcCameraReader : public AbcObjectReader { - Alembic::AbcGeom::ICameraSchema m_schema; - - public: - AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const; - - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); -}; - -#endif /* __ABC_READER_CAMERA_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_curves.cc b/source/blender/alembic/intern/abc_reader_curves.cc deleted file mode 100644 index 1be164c7c94..00000000000 --- a/source/blender/alembic/intern/abc_reader_curves.cc +++ /dev/null @@ -1,354 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_curves.h" -#include "abc_reader_transform.h" -#include "abc_util.h" - -#include - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" - -#include "BKE_curve.h" -#include "BKE_mesh.h" -#include "BKE_object.h" -} - -using Alembic::Abc::FloatArraySamplePtr; -using Alembic::Abc::Int32ArraySamplePtr; -using Alembic::Abc::P3fArraySamplePtr; -using Alembic::Abc::PropertyHeader; -using Alembic::Abc::UcharArraySamplePtr; - -using Alembic::AbcGeom::CurvePeriodicity; -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::ICurves; -using Alembic::AbcGeom::ICurvesSchema; -using Alembic::AbcGeom::IFloatGeomParam; -using Alembic::AbcGeom::IInt16Property; -using Alembic::AbcGeom::ISampleSelector; -using Alembic::AbcGeom::kWrapExisting; - -AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - ICurves abc_curves(object, kWrapExisting); - m_curves_schema = abc_curves.getSchema(); - - get_min_max_time(m_iobject, m_curves_schema, m_min_time, m_max_time); -} - -bool AbcCurveReader::valid() const -{ - return m_curves_schema.valid(); -} - -bool AbcCurveReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::ICurves::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to Curves when importing, but not any " - "more."; - return false; - } - - if (ob->type != OB_CURVE) { - *err_str = "Object type mismatch, Alembic object path points to Curves."; - return false; - } - - return true; -} - -void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) -{ - Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); - - cu->flag |= CU_DEFORM_FILL | CU_3D; - cu->actvert = CU_ACT_NONE; - cu->resolu = 1; - - ICompoundProperty user_props = m_curves_schema.getUserProperties(); - if (user_props) { - const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); - if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) { - IInt16Property resolu(user_props, header->getName()); - cu->resolu = resolu.getValue(sample_sel); - } - } - - m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); - m_object->data = cu; - - read_curve_sample(cu, m_curves_schema, sample_sel); - - if (has_animations(m_curves_schema, m_settings)) { - addCacheModifier(); - } -} - -void AbcCurveReader::read_curve_sample(Curve *cu, - const ICurvesSchema &schema, - const ISampleSelector &sample_sel) -{ - ICurvesSchema::Sample smp; - try { - smp = schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return; - } - - const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); - const P3fArraySamplePtr positions = smp.getPositions(); - const FloatArraySamplePtr weights = smp.getPositionWeights(); - const FloatArraySamplePtr knots = smp.getKnots(); - const CurvePeriodicity periodicity = smp.getWrap(); - const UcharArraySamplePtr orders = smp.getOrders(); - - const IFloatGeomParam widths_param = schema.getWidthsParam(); - FloatArraySamplePtr radiuses; - - if (widths_param.valid()) { - IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); - radiuses = wsample.getVals(); - } - - int knot_offset = 0; - - size_t idx = 0; - for (size_t i = 0; i < num_vertices->size(); i++) { - const int num_verts = (*num_vertices)[i]; - - Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), "abc_getnurb")); - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - nu->pntsu = num_verts; - nu->pntsv = 1; - nu->flag |= CU_SMOOTH; - - switch (smp.getType()) { - case Alembic::AbcGeom::kCubic: - nu->orderu = 4; - break; - case Alembic::AbcGeom::kVariableOrder: - if (orders && orders->size() > i) { - nu->orderu = static_cast((*orders)[i]); - break; - } - ATTR_FALLTHROUGH; - case Alembic::AbcGeom::kLinear: - default: - nu->orderu = 2; - } - - if (periodicity == Alembic::AbcGeom::kNonPeriodic) { - nu->flagu |= CU_NURB_ENDPOINT; - } - else if (periodicity == Alembic::AbcGeom::kPeriodic) { - nu->flagu |= CU_NURB_CYCLIC; - - /* Check the number of points which overlap, we don't have - * overlapping points in Blender, but other software do use them to - * indicate that a curve is actually cyclic. Usually the number of - * overlapping points is equal to the order/degree of the curve. - */ - - const int start = idx; - const int end = idx + num_verts; - int overlap = 0; - - for (int j = start, k = end - nu->orderu; j < nu->orderu; j++, k++) { - const Imath::V3f &p1 = (*positions)[j]; - const Imath::V3f &p2 = (*positions)[k]; - - if (p1 != p2) { - break; - } - - overlap++; - } - - /* TODO: Special case, need to figure out how it coincides with knots. */ - if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { - overlap = 1; - } - - /* There is no real cycles. */ - if (overlap == 0) { - nu->flagu &= ~CU_NURB_CYCLIC; - nu->flagu |= CU_NURB_ENDPOINT; - } - - nu->pntsu -= overlap; - } - - const bool do_weights = (weights != NULL) && (weights->size() > 1); - float weight = 1.0f; - - const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1); - float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f; - - nu->type = CU_NURBS; - - nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb")); - BPoint *bp = nu->bp; - - for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { - const Imath::V3f &pos = (*positions)[idx]; - - if (do_radius) { - radius = (*radiuses)[idx]; - } - - if (do_weights) { - weight = (*weights)[idx]; - } - - copy_zup_from_yup(bp->vec, pos.getValue()); - bp->vec[3] = weight; - bp->f1 = SELECT; - bp->radius = radius; - bp->weight = 1.0f; - } - - if (knots && knots->size() != 0) { - nu->knotsu = static_cast( - MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu")); - - /* TODO: second check is temporary, for until the check for cycles is rock solid. */ - if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) { - /* Skip first and last knots. */ - for (size_t i = 1; i < knots->size() - 1; i++) { - nu->knotsu[i - 1] = (*knots)[knot_offset + i]; - } - } - else { - /* TODO: figure out how to use the knots array from other - * software in this case. */ - BKE_nurb_knot_calc_u(nu); - } - - knot_offset += knots->size(); - } - else { - BKE_nurb_knot_calc_u(nu); - } - - BLI_addtail(BKE_curve_nurbs_get(cu), nu); - } -} - -/* NOTE: Alembic only stores data about control points, but the Mesh - * passed from the cache modifier contains the displist, which has more data - * than the control points, so to avoid corrupting the displist we modify the - * object directly and create a new Mesh from that. Also we might need to - * create new or delete existing NURBS in the curve. - */ -Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int /*read_flag*/, - const char **err_str) -{ - ICurvesSchema::Sample sample; - - try { - sample = m_curves_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - *err_str = "Error reading curve sample; more detail on the console"; - printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_curves_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return existing_mesh; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); - - int vertex_idx = 0; - int curve_idx; - Curve *curve = static_cast(m_object->data); - - const int curve_count = BLI_listbase_count(&curve->nurb); - bool same_topology = curve_count == num_vertices->size(); - - if (same_topology) { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int num_in_alembic = (*num_vertices)[curve_idx]; - const int num_in_blender = nurbs->pntsu; - - if (num_in_alembic != num_in_blender) { - same_topology = false; - break; - } - } - } - - if (!same_topology) { - BKE_nurbList_free(&curve->nurb); - read_curve_sample(curve, m_curves_schema, sample_sel); - } - else { - Nurb *nurbs = static_cast(curve->nurb.first); - for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { - const int totpoint = (*num_vertices)[curve_idx]; - - if (nurbs->bp) { - BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_zup_from_yup(point->vec, pos.getValue()); - } - } - else if (nurbs->bezt) { - BezTriple *bezier = nurbs->bezt; - - for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { - const Imath::V3f &pos = (*positions)[vertex_idx]; - copy_zup_from_yup(bezier->vec[1], pos.getValue()); - } - } - } - } - - return BKE_mesh_new_nomain_from_curve(m_object); -} diff --git a/source/blender/alembic/intern/abc_reader_curves.h b/source/blender/alembic/intern/abc_reader_curves.h deleted file mode 100644 index 1e4f28edc51..00000000000 --- a/source/blender/alembic/intern/abc_reader_curves.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_CURVES_H__ -#define __ABC_READER_CURVES_H__ - -#include "abc_reader_object.h" -#include "abc_reader_mesh.h" - -struct Curve; - -#define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" - -class AbcCurveReader : public AbcObjectReader { - Alembic::AbcGeom::ICurvesSchema m_curves_schema; - - public: - AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const; - - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char **err_str); - - void read_curve_sample(Curve *cu, - const Alembic::AbcGeom::ICurvesSchema &schema, - const Alembic::Abc::ISampleSelector &sample_selector); -}; - -#endif /* __ABC_READER_CURVES_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_mesh.cc b/source/blender/alembic/intern/abc_reader_mesh.cc deleted file mode 100644 index a4e412695c3..00000000000 --- a/source/blender/alembic/intern/abc_reader_mesh.cc +++ /dev/null @@ -1,889 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_mesh.h" -#include "abc_reader_transform.h" -#include "abc_util.h" - -#include - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" - -#include "BLI_math_geom.h" - -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_object.h" -} - -using Alembic::Abc::Int32ArraySamplePtr; -using Alembic::Abc::P3fArraySamplePtr; - -using Alembic::AbcGeom::IFaceSet; -using Alembic::AbcGeom::IFaceSetSchema; -using Alembic::AbcGeom::IN3fGeomParam; -using Alembic::AbcGeom::IObject; -using Alembic::AbcGeom::IPolyMesh; -using Alembic::AbcGeom::IPolyMeshSchema; -using Alembic::AbcGeom::ISampleSelector; -using Alembic::AbcGeom::ISubD; -using Alembic::AbcGeom::ISubDSchema; -using Alembic::AbcGeom::IV2fGeomParam; -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::N3fArraySample; -using Alembic::AbcGeom::N3fArraySamplePtr; -using Alembic::AbcGeom::UInt32ArraySamplePtr; -using Alembic::AbcGeom::V2fArraySamplePtr; - -/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ - -/* Some helpers for mesh generation */ -namespace utils { - -static void build_mat_map(const Main *bmain, std::map &mat_map) -{ - Material *material = static_cast(bmain->materials.first); - - for (; material; material = static_cast(material->id.next)) { - mat_map[material->id.name + 2] = material; - } -} - -static void assign_materials(Main *bmain, - Object *ob, - const std::map &mat_index_map) -{ - bool can_assign = true; - std::map::const_iterator it = mat_index_map.begin(); - - int matcount = 0; - for (; it != mat_index_map.end(); ++it, matcount++) { - if (!BKE_object_material_slot_add(bmain, ob)) { - can_assign = false; - break; - } - } - - /* TODO(kevin): use global map? */ - std::map mat_map; - build_mat_map(bmain, mat_map); - - std::map::iterator mat_iter; - - if (can_assign) { - it = mat_index_map.begin(); - - for (; it != mat_index_map.end(); ++it) { - std::string mat_name = it->first; - mat_iter = mat_map.find(mat_name.c_str()); - - Material *assigned_mat; - - if (mat_iter == mat_map.end()) { - assigned_mat = BKE_material_add(bmain, mat_name.c_str()); - mat_map[mat_name] = assigned_mat; - } - else { - assigned_mat = mat_iter->second; - } - - BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA); - } - } -} - -} /* namespace utils */ - -struct AbcMeshData { - Int32ArraySamplePtr face_indices; - Int32ArraySamplePtr face_counts; - - P3fArraySamplePtr positions; - P3fArraySamplePtr ceil_positions; - - V2fArraySamplePtr uvs; - UInt32ArraySamplePtr uvs_indices; -}; - -static void read_mverts_interp(MVert *mverts, - const P3fArraySamplePtr &positions, - const P3fArraySamplePtr &ceil_positions, - const float weight) -{ - float tmp[3]; - for (int i = 0; i < positions->size(); i++) { - MVert &mvert = mverts[i]; - const Imath::V3f &floor_pos = (*positions)[i]; - const Imath::V3f &ceil_pos = (*ceil_positions)[i]; - - interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight); - copy_zup_from_yup(mvert.co, tmp); - - mvert.bweight = 0; - } -} - -static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data) -{ - MVert *mverts = config.mvert; - const P3fArraySamplePtr &positions = mesh_data.positions; - - if (config.weight != 0.0f && mesh_data.ceil_positions != NULL && - mesh_data.ceil_positions->size() == positions->size()) { - read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight); - return; - } - - read_mverts(mverts, positions, nullptr); -} - -void read_mverts(MVert *mverts, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals) -{ - for (int i = 0; i < positions->size(); i++) { - MVert &mvert = mverts[i]; - Imath::V3f pos_in = (*positions)[i]; - - copy_zup_from_yup(mvert.co, pos_in.getValue()); - - mvert.bweight = 0; - - if (normals) { - Imath::V3f nor_in = (*normals)[i]; - - short no[3]; - normal_float_to_short_v3(no, nor_in.getValue()); - - copy_zup_from_yup(mvert.no, no); - } - } -} - -static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) -{ - MPoly *mpolys = config.mpoly; - MLoop *mloops = config.mloop; - MLoopUV *mloopuvs = config.mloopuv; - - const Int32ArraySamplePtr &face_indices = mesh_data.face_indices; - const Int32ArraySamplePtr &face_counts = mesh_data.face_counts; - const V2fArraySamplePtr &uvs = mesh_data.uvs; - const size_t uvs_size = uvs == nullptr ? 0 : uvs->size(); - - const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; - - const bool do_uvs = (mloopuvs && uvs && uvs_indices) && - (uvs_indices->size() == face_indices->size()); - unsigned int loop_index = 0; - unsigned int rev_loop_index = 0; - unsigned int uv_index = 0; - - for (int i = 0; i < face_counts->size(); i++) { - const int face_size = (*face_counts)[i]; - - MPoly &poly = mpolys[i]; - poly.loopstart = loop_index; - poly.totloop = face_size; - - /* Polygons are always assumed to be smooth-shaded. If the Alembic mesh should be flat-shaded, - * this is encoded in custom loop normals. See T71246. */ - poly.flag |= ME_SMOOTH; - - /* NOTE: Alembic data is stored in the reverse order. */ - rev_loop_index = loop_index + (face_size - 1); - - for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) { - MLoop &loop = mloops[rev_loop_index]; - loop.v = (*face_indices)[loop_index]; - - if (do_uvs) { - MLoopUV &loopuv = mloopuvs[rev_loop_index]; - - uv_index = (*uvs_indices)[loop_index]; - - /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */ - if (uv_index >= uvs_size) { - continue; - } - - loopuv.uv[0] = (*uvs)[uv_index][0]; - loopuv.uv[1] = (*uvs)[uv_index][1]; - } - } - } - - BKE_mesh_calc_edges(config.mesh, false, false); -} - -static void process_no_normals(CDStreamConfig &config) -{ - /* Absense of normals in the Alembic mesh is interpreted as 'smooth'. */ - BKE_mesh_calc_normals(config.mesh); -} - -static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr) -{ - size_t loop_count = loop_normals_ptr->size(); - - if (loop_count == 0) { - process_no_normals(config); - return; - } - - float(*lnors)[3] = static_cast( - MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals")); - - Mesh *mesh = config.mesh; - MPoly *mpoly = mesh->mpoly; - const N3fArraySample &loop_normals = *loop_normals_ptr; - int abc_index = 0; - for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) { - /* As usual, ABC orders the loops in reverse. */ - for (int j = mpoly->totloop - 1; j >= 0; j--, abc_index++) { - int blender_index = mpoly->loopstart + j; - copy_zup_from_yup(lnors[blender_index], loop_normals[abc_index].getValue()); - } - } - - mesh->flag |= ME_AUTOSMOOTH; - BKE_mesh_set_custom_normals(mesh, lnors); - - MEM_freeN(lnors); -} - -static void process_vertex_normals(CDStreamConfig &config, - const N3fArraySamplePtr vertex_normals_ptr) -{ - size_t normals_count = vertex_normals_ptr->size(); - if (normals_count == 0) { - process_no_normals(config); - return; - } - - float(*vnors)[3] = static_cast( - MEM_malloc_arrayN(normals_count, sizeof(float[3]), "ABC::VertexNormals")); - - const N3fArraySample &vertex_normals = *vertex_normals_ptr; - for (int index = 0; index < normals_count; index++) { - copy_zup_from_yup(vnors[index], vertex_normals[index].getValue()); - } - - config.mesh->flag |= ME_AUTOSMOOTH; - BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors); - MEM_freeN(vnors); -} - -static void process_normals(CDStreamConfig &config, - const IN3fGeomParam &normals, - const ISampleSelector &selector) -{ - if (!normals.valid()) { - process_no_normals(config); - return; - } - - IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector); - Alembic::AbcGeom::GeometryScope scope = normals.getScope(); - - switch (scope) { - case Alembic::AbcGeom::kFacevaryingScope: // 'Vertex Normals' in Houdini. - process_loop_normals(config, normsamp.getVals()); - break; - case Alembic::AbcGeom::kVertexScope: - case Alembic::AbcGeom::kVaryingScope: // 'Point Normals' in Houdini. - process_vertex_normals(config, normsamp.getVals()); - break; - case Alembic::AbcGeom::kConstantScope: - case Alembic::AbcGeom::kUniformScope: - case Alembic::AbcGeom::kUnknownScope: - process_no_normals(config); - break; - } -} - -ABC_INLINE void read_uvs_params(CDStreamConfig &config, - AbcMeshData &abc_data, - const IV2fGeomParam &uv, - const ISampleSelector &selector) -{ - if (!uv.valid()) { - return; - } - - IV2fGeomParam::Sample uvsamp; - uv.getIndexed(uvsamp, selector); - - abc_data.uvs = uvsamp.getVals(); - abc_data.uvs_indices = uvsamp.getIndices(); - - if (abc_data.uvs_indices->size() == config.totloop) { - std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); - - /* According to the convention, primary UVs should have had their name - * set using Alembic::Abc::SetSourceName, but you can't expect everyone - * to follow it! :) */ - if (name.empty()) { - name = uv.getName(); - } - - void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); - config.mloopuv = static_cast(cd_ptr); - } -} - -static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) -{ - CustomDataType cd_data_type = static_cast(data_type); - void *cd_ptr; - CustomData *loopdata; - int numloops; - - /* unsupported custom data type -- don't do anything. */ - if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { - return NULL; - } - - loopdata = &mesh->ldata; - cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); - if (cd_ptr != NULL) { - /* layer already exists, so just return it. */ - return cd_ptr; - } - - /* Create a new layer. */ - numloops = mesh->totloop; - cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, NULL, numloops, name); - return cd_ptr; -} - -static void get_weight_and_index(CDStreamConfig &config, - Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling, - size_t samples_number) -{ - Alembic::AbcGeom::index_t i0, i1; - - config.weight = get_weight_and_index(config.time, time_sampling, samples_number, i0, i1); - - config.index = i0; - config.ceil_index = i1; -} - -static void read_mesh_sample(const std::string &iobject_full_name, - ImportSettings *settings, - const IPolyMeshSchema &schema, - const ISampleSelector &selector, - CDStreamConfig &config) -{ - const IPolyMeshSchema::Sample sample = schema.getValue(selector); - - AbcMeshData abc_mesh_data; - abc_mesh_data.face_counts = sample.getFaceCounts(); - abc_mesh_data.face_indices = sample.getFaceIndices(); - abc_mesh_data.positions = sample.getPositions(); - - get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples()); - - if (config.weight != 0.0f) { - Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample; - schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index)); - abc_mesh_data.ceil_positions = ceil_sample.getPositions(); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { - read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { - read_mverts(config, abc_mesh_data); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { - read_mpolys(config, abc_mesh_data); - process_normals(config, schema.getNormalsParam(), selector); - } - - if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { - read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); - } -} - -CDStreamConfig get_config(Mesh *mesh) -{ - CDStreamConfig config; - - BLI_assert(mesh->mvert || mesh->totvert == 0); - - config.mesh = mesh; - config.mvert = mesh->mvert; - config.mloop = mesh->mloop; - config.mpoly = mesh->mpoly; - config.totloop = mesh->totloop; - config.totpoly = mesh->totpoly; - config.loopdata = &mesh->ldata; - config.add_customdata_cb = add_customdata_cb; - - return config; -} - -/* ************************************************************************** */ - -AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; - - IPolyMesh ipoly_mesh(m_iobject, kWrapExisting); - m_schema = ipoly_mesh.getSchema(); - - get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); -} - -bool AbcMeshReader::valid() const -{ - return m_schema.valid(); -} - -void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) -{ - Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - - m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); - m_object->data = mesh; - - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL); - if (read_mesh != mesh) { - /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */ - /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ - short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); - mesh->flag |= autosmooth; - } - - if (m_settings->validate_meshes) { - BKE_mesh_validate(mesh, false, false); - } - - readFaceSetsSample(bmain, mesh, sample_sel); - - if (has_animations(m_schema, m_settings)) { - addCacheModifier(); - } -} - -bool AbcMeshReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not " - "any more."; - return false; - } - - if (ob->type != OB_MESH) { - *err_str = "Object type mismatch, Alembic object path points to PolyMesh."; - return false; - } - - return true; -} - -bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel) -{ - IPolyMeshSchema::Sample sample; - try { - sample = m_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - // A similar error in read_mesh() would just return existing_mesh. - return false; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - return positions->size() != existing_mesh->totvert || - face_counts->size() != existing_mesh->totpoly || - face_indices->size() != existing_mesh->totloop; -} - -Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int read_flag, - const char **err_str) -{ - IPolyMeshSchema::Sample sample; - try { - sample = m_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - if (err_str != nullptr) { - *err_str = "Error reading mesh sample; more detail on the console"; - } - printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return existing_mesh; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - Mesh *new_mesh = NULL; - - /* Only read point data when streaming meshes, unless we need to create new ones. */ - ImportSettings settings; - settings.read_flag |= read_flag; - - if (topology_changed(existing_mesh, sample_sel)) { - new_mesh = BKE_mesh_new_nomain_from_template( - existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size()); - - settings.read_flag |= MOD_MESHSEQ_READ_ALL; - } - else { - /* If the face count changed (e.g. by triangulation), only read points. - * This prevents crash from T49813. - * TODO(kevin): perhaps find a better way to do this? */ - if (face_counts->size() != existing_mesh->totpoly || - face_indices->size() != existing_mesh->totloop) { - settings.read_flag = MOD_MESHSEQ_READ_VERT; - - if (err_str) { - *err_str = - "Topology has changed, perhaps by triangulating the" - " mesh. Only vertices will be read!"; - } - } - } - - CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); - config.time = sample_sel.getRequestedTime(); - - read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config); - - if (new_mesh) { - /* Here we assume that the number of materials doesn't change, i.e. that - * the material slots that were created when the object was loaded from - * Alembic are still valid now. */ - size_t num_polys = new_mesh->totpoly; - if (num_polys > 0) { - std::map mat_map; - assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map); - } - - return new_mesh; - } - - return existing_mesh; -} - -void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel, - MPoly *mpoly, - int totpoly, - std::map &r_mat_map) -{ - std::vector face_sets; - m_schema.getFaceSetNames(face_sets); - - if (face_sets.empty()) { - return; - } - - int current_mat = 0; - - for (int i = 0; i < face_sets.size(); i++) { - const std::string &grp_name = face_sets[i]; - - if (r_mat_map.find(grp_name) == r_mat_map.end()) { - r_mat_map[grp_name] = 1 + current_mat++; - } - - const int assigned_mat = r_mat_map[grp_name]; - - const IFaceSet faceset = m_schema.getFaceSet(grp_name); - - if (!faceset.valid()) { - std::cerr << " Face set " << grp_name << " invalid for " << m_object_name << "\n"; - continue; - } - - const IFaceSetSchema face_schem = faceset.getSchema(); - const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); - const Int32ArraySamplePtr group_faces = face_sample.getFaces(); - const size_t num_group_faces = group_faces->size(); - - for (size_t l = 0; l < num_group_faces; l++) { - size_t pos = (*group_faces)[l]; - - if (pos >= totpoly) { - std::cerr << "Faceset overflow on " << faceset.getName() << '\n'; - break; - } - - MPoly &poly = mpoly[pos]; - poly.mat_nr = assigned_mat - 1; - } - } -} - -void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel) -{ - std::map mat_map; - assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map); - utils::assign_materials(bmain, m_object, mat_map); -} - -/* ************************************************************************** */ - -ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) -{ - for (int i = 0, e = totedge; i < e; i++) { - MEdge &edge = edges[i]; - - if (edge.v1 == v1 && edge.v2 == v2) { - return &edge; - } - } - - return NULL; -} - -static void read_subd_sample(const std::string &iobject_full_name, - ImportSettings *settings, - const ISubDSchema &schema, - const ISampleSelector &selector, - CDStreamConfig &config) -{ - const ISubDSchema::Sample sample = schema.getValue(selector); - - AbcMeshData abc_mesh_data; - abc_mesh_data.face_counts = sample.getFaceCounts(); - abc_mesh_data.face_indices = sample.getFaceIndices(); - abc_mesh_data.positions = sample.getPositions(); - - get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples()); - - if (config.weight != 0.0f) { - Alembic::AbcGeom::ISubDSchema::Sample ceil_sample; - schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index)); - abc_mesh_data.ceil_positions = ceil_sample.getPositions(); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { - read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { - read_mverts(config, abc_mesh_data); - } - - if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { - /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision - * mesh. Currently we don't add a subdivision modifier when we load such data. This code is - * assuming that the subdivided surface should be smooth. */ - read_mpolys(config, abc_mesh_data); - process_no_normals(config); - } - - if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { - read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); - } -} - -/* ************************************************************************** */ - -AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; - - ISubD isubd_mesh(m_iobject, kWrapExisting); - m_schema = isubd_mesh.getSchema(); - - get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); -} - -bool AbcSubDReader::valid() const -{ - return m_schema.valid(); -} - -bool AbcSubDReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to SubD when importing, but not any " - "more."; - return false; - } - - if (ob->type != OB_MESH) { - *err_str = "Object type mismatch, Alembic object path points to SubD."; - return false; - } - - return true; -} - -void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) -{ - Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - - m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); - m_object->data = mesh; - - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL); - if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); - } - - ISubDSchema::Sample sample; - try { - sample = m_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return; - } - - Int32ArraySamplePtr indices = sample.getCreaseIndices(); - Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); - - if (indices && sharpnesses) { - MEdge *edges = mesh->medge; - int totedge = mesh->totedge; - - for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) { - int v1 = (*indices)[i]; - int v2 = (*indices)[i + 1]; - - if (v2 < v1) { - /* It appears to be common to store edges with the smallest index first, in which case this - * prevents us from doing the second search below. */ - std::swap(v1, v2); - } - - MEdge *edge = find_edge(edges, totedge, v1, v2); - if (edge == NULL) { - edge = find_edge(edges, totedge, v2, v1); - } - - if (edge) { - edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]); - } - } - - mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; - } - - if (m_settings->validate_meshes) { - BKE_mesh_validate(mesh, false, false); - } - - if (has_animations(m_schema, m_settings)) { - addCacheModifier(); - } -} - -Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int read_flag, - const char **err_str) -{ - ISubDSchema::Sample sample; - try { - sample = m_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - if (err_str != nullptr) { - *err_str = "Error reading mesh sample; more detail on the console"; - } - printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return existing_mesh; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); - const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); - - Mesh *new_mesh = NULL; - - ImportSettings settings; - settings.read_flag |= read_flag; - - if (existing_mesh->totvert != positions->size()) { - new_mesh = BKE_mesh_new_nomain_from_template( - existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size()); - - settings.read_flag |= MOD_MESHSEQ_READ_ALL; - } - else { - /* If the face count changed (e.g. by triangulation), only read points. - * This prevents crash from T49813. - * TODO(kevin): perhaps find a better way to do this? */ - if (face_counts->size() != existing_mesh->totpoly || - face_indices->size() != existing_mesh->totloop) { - settings.read_flag = MOD_MESHSEQ_READ_VERT; - - if (err_str) { - *err_str = - "Topology has changed, perhaps by triangulating the" - " mesh. Only vertices will be read!"; - } - } - } - - /* Only read point data when streaming meshes, unless we need to create new ones. */ - CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); - config.time = sample_sel.getRequestedTime(); - read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config); - - return config.mesh; -} diff --git a/source/blender/alembic/intern/abc_reader_mesh.h b/source/blender/alembic/intern/abc_reader_mesh.h deleted file mode 100644 index bc95c7ec134..00000000000 --- a/source/blender/alembic/intern/abc_reader_mesh.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_MESH_H__ -#define __ABC_READER_MESH_H__ - -#include "abc_customdata.h" -#include "abc_reader_object.h" - -struct Mesh; - -class AbcMeshReader : public AbcObjectReader { - Alembic::AbcGeom::IPolyMeshSchema m_schema; - - CDStreamConfig m_mesh_data; - - public: - AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const override; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; - - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char **err_str) override; - bool topology_changed(Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel) override; - - private: - void readFaceSetsSample(Main *bmain, - Mesh *mesh, - const Alembic::AbcGeom::ISampleSelector &sample_sel); - - void assign_facesets_to_mpoly(const Alembic::Abc::ISampleSelector &sample_sel, - MPoly *mpoly, - int totpoly, - std::map &r_mat_map); -}; - -class AbcSubDReader : public AbcObjectReader { - Alembic::AbcGeom::ISubDSchema m_schema; - - CDStreamConfig m_mesh_data; - - public: - AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char **err_str); -}; - -void read_mverts(MVert *mverts, - const Alembic::AbcGeom::P3fArraySamplePtr positions, - const Alembic::AbcGeom::N3fArraySamplePtr normals); - -CDStreamConfig get_config(struct Mesh *mesh); - -#endif /* __ABC_READER_MESH_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_nurbs.cc b/source/blender/alembic/intern/abc_reader_nurbs.cc deleted file mode 100644 index 0ada10baba5..00000000000 --- a/source/blender/alembic/intern/abc_reader_nurbs.cc +++ /dev/null @@ -1,225 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_nurbs.h" -#include "abc_reader_transform.h" -#include "abc_util.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" -#include "BLI_string.h" - -#include "BKE_curve.h" -#include "BKE_object.h" -} - -using Alembic::AbcGeom::FloatArraySamplePtr; -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::MetaData; -using Alembic::AbcGeom::P3fArraySamplePtr; - -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::INuPatch; -using Alembic::AbcGeom::INuPatchSchema; -using Alembic::AbcGeom::IObject; - -AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - getNurbsPatches(m_iobject); - get_min_max_time(m_iobject, m_schemas[0].first, m_min_time, m_max_time); -} - -bool AbcNurbsReader::valid() const -{ - if (m_schemas.empty()) { - return false; - } - - std::vector>::const_iterator it; - for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { - const INuPatchSchema &schema = it->first; - - if (!schema.valid()) { - return false; - } - } - - return true; -} - -static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) -{ - if (!knots || knots->size() == 0) { - return false; - } - - /* Skip first and last knots, as they are used for padding. */ - const size_t num_knots = knots->size() - 2; - nu_knots = static_cast(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu")); - - for (size_t i = 0; i < num_knots; i++) { - nu_knots[i] = (*knots)[i + 1]; - } - - return true; -} - -void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) -{ - Curve *cu = static_cast(BKE_curve_add(bmain, "abc_curve", OB_SURF)); - cu->actvert = CU_ACT_NONE; - - std::vector>::iterator it; - - for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { - Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), "abc_getnurb")); - nu->flag = CU_SMOOTH; - nu->type = CU_NURBS; - nu->resolu = cu->resolu; - nu->resolv = cu->resolv; - - const INuPatchSchema &schema = it->first; - INuPatchSchema::Sample smp; - try { - smp = schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading nurbs sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return; - } - - nu->orderu = smp.getUOrder() - 1; - nu->orderv = smp.getVOrder() - 1; - nu->pntsu = smp.getNumU(); - nu->pntsv = smp.getNumV(); - - /* Read positions and weights. */ - - const P3fArraySamplePtr positions = smp.getPositions(); - const FloatArraySamplePtr weights = smp.getPositionWeights(); - - const size_t num_points = positions->size(); - - nu->bp = static_cast(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype")); - - BPoint *bp = nu->bp; - float posw_in = 1.0f; - - for (int i = 0; i < num_points; i++, bp++) { - const Imath::V3f &pos_in = (*positions)[i]; - - if (weights) { - posw_in = (*weights)[i]; - } - - copy_zup_from_yup(bp->vec, pos_in.getValue()); - bp->vec[3] = posw_in; - bp->f1 = SELECT; - bp->radius = 1.0f; - bp->weight = 1.0f; - } - - /* Read knots. */ - - if (!set_knots(smp.getUKnot(), nu->knotsu)) { - BKE_nurb_knot_calc_u(nu); - } - - if (!set_knots(smp.getVKnot(), nu->knotsv)) { - BKE_nurb_knot_calc_v(nu); - } - - /* Read flags. */ - - ICompoundProperty user_props = schema.getUserProperties(); - - if (has_property(user_props, "enpoint_u")) { - nu->flagu |= CU_NURB_ENDPOINT; - } - - if (has_property(user_props, "enpoint_v")) { - nu->flagv |= CU_NURB_ENDPOINT; - } - - if (has_property(user_props, "cyclic_u")) { - nu->flagu |= CU_NURB_CYCLIC; - } - - if (has_property(user_props, "cyclic_v")) { - nu->flagv |= CU_NURB_CYCLIC; - } - - BLI_addtail(BKE_curve_nurbs_get(cu), nu); - } - - BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); - - m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); - m_object->data = cu; -} - -void AbcNurbsReader::getNurbsPatches(const IObject &obj) -{ - if (!obj.valid()) { - return; - } - - const int num_children = obj.getNumChildren(); - - if (num_children == 0) { - INuPatch abc_nurb(obj, kWrapExisting); - INuPatchSchema schem = abc_nurb.getSchema(); - m_schemas.push_back(std::pair(schem, obj)); - return; - } - - for (int i = 0; i < num_children; i++) { - bool ok = true; - IObject child(obj, obj.getChildHeader(i).getName()); - - if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) { - ok = false; - } - - if (!child.valid()) { - continue; - } - - const MetaData &md = child.getMetaData(); - - if (INuPatch::matches(md) && ok) { - INuPatch abc_nurb(child, kWrapExisting); - INuPatchSchema schem = abc_nurb.getSchema(); - m_schemas.push_back(std::pair(schem, child)); - } - - getNurbsPatches(child); - } -} diff --git a/source/blender/alembic/intern/abc_reader_nurbs.h b/source/blender/alembic/intern/abc_reader_nurbs.h deleted file mode 100644 index f4284c136fb..00000000000 --- a/source/blender/alembic/intern/abc_reader_nurbs.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_NURBS_H__ -#define __ABC_READER_NURBS_H__ - -#include "abc_reader_object.h" - -class AbcNurbsReader : public AbcObjectReader { - std::vector> m_schemas; - - public: - AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); - - private: - void getNurbsPatches(const Alembic::Abc::IObject &obj); -}; - -#endif /* __ABC_READER_NURBS_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_object.cc b/source/blender/alembic/intern/abc_reader_object.cc deleted file mode 100644 index 3e7f87d78cc..00000000000 --- a/source/blender/alembic/intern/abc_reader_object.cc +++ /dev/null @@ -1,333 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_object.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_cachefile_types.h" -#include "DNA_constraint_types.h" -#include "DNA_modifier_types.h" -#include "DNA_space_types.h" /* for FILE_MAX */ - -#include "BKE_constraint.h" -#include "BKE_lib_id.h" -#include "BKE_modifier.h" -#include "BKE_object.h" - -#include "BLI_utildefines.h" -#include "BLI_listbase.h" -#include "BLI_math_geom.h" -#include "BLI_string.h" -} - -using Alembic::AbcGeom::IObject; -using Alembic::AbcGeom::IXform; -using Alembic::AbcGeom::IXformSchema; - -AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings) - : m_name(""), - m_object_name(""), - m_data_name(""), - m_object(NULL), - m_iobject(object), - m_settings(&settings), - m_min_time(std::numeric_limits::max()), - m_max_time(std::numeric_limits::min()), - m_refcount(0), - parent_reader(NULL) -{ - m_name = object.getFullName(); - std::vector parts; - split(m_name, '/', parts); - - if (parts.size() >= 2) { - m_object_name = parts[parts.size() - 2]; - m_data_name = parts[parts.size() - 1]; - } - else { - m_object_name = m_data_name = parts[parts.size() - 1]; - } - - determine_inherits_xform(); -} - -/* Determine whether we can inherit our parent's XForm */ -void AbcObjectReader::determine_inherits_xform() -{ - m_inherits_xform = false; - - IXform ixform = xform(); - if (!ixform) { - return; - } - - const IXformSchema &schema(ixform.getSchema()); - if (!schema.valid()) { - std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema." - << std::endl; - return; - } - - m_inherits_xform = schema.getInheritsXforms(); - - IObject ixform_parent = ixform.getParent(); - if (!ixform_parent.getParent()) { - /* The archive top object certainly is not a transform itself, so handle - * it as "no parent". */ - m_inherits_xform = false; - } - else { - m_inherits_xform = ixform_parent && m_inherits_xform; - } -} - -AbcObjectReader::~AbcObjectReader() -{ -} - -const IObject &AbcObjectReader::iobject() const -{ - return m_iobject; -} - -Object *AbcObjectReader::object() const -{ - return m_object; -} - -void AbcObjectReader::object(Object *ob) -{ - m_object = ob; -} - -static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1, const float weight) -{ - float mat0[4][4], mat1[4][4], ret[4][4]; - - /* Cannot use Imath::M44d::getValue() since this returns a pointer to - * doubles and interp_m4_m4m4 expects pointers to floats. So need to convert - * the matrices manually. - */ - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - mat0[i][j] = static_cast(m0[i][j]); - } - } - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - mat1[i][j] = static_cast(m1[i][j]); - } - } - - interp_m4_m4m4(ret, mat0, mat1, weight); - - Imath::M44d m; - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - m[i][j] = ret[i][j]; - } - } - - return m; -} - -Imath::M44d get_matrix(const IXformSchema &schema, const float time) -{ - Alembic::AbcGeom::index_t i0, i1; - Alembic::AbcGeom::XformSample s0, s1; - - const float weight = get_weight_and_index( - time, schema.getTimeSampling(), schema.getNumSamples(), i0, i1); - - schema.get(s0, Alembic::AbcGeom::ISampleSelector(i0)); - - if (i0 != i1) { - schema.get(s1, Alembic::AbcGeom::ISampleSelector(i1)); - return blend_matrices(s0.getMatrix(), s1.getMatrix(), weight); - } - - return s0.getMatrix(); -} - -struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &UNUSED(sample_sel), - int UNUSED(read_flag), - const char **UNUSED(err_str)) -{ - return existing_mesh; -} - -bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/, - const Alembic::Abc::ISampleSelector & /*sample_sel*/) -{ - /* The default implementation of read_mesh() just returns the original mesh, so never changes the - * topology. */ - return false; -} - -void AbcObjectReader::setupObjectTransform(const float time) -{ - bool is_constant = false; - float transform_from_alembic[4][4]; - - /* If the parent is a camera, apply the inverse rotation to make up for the from-Maya rotation. - * This assumes that the parent object also was imported from Alembic. */ - if (m_object->parent != nullptr && m_object->parent->type == OB_CAMERA) { - axis_angle_to_mat4_single(m_object->parentinv, 'X', -M_PI_2); - } - - this->read_matrix(transform_from_alembic, time, m_settings->scale, is_constant); - - /* Apply the matrix to the object. */ - BKE_object_apply_mat4(m_object, transform_from_alembic, true, false); - BKE_object_to_mat4(m_object, m_object->obmat); - - if (!is_constant) { - bConstraint *con = BKE_constraint_add_for_object( - m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); - bTransformCacheConstraint *data = static_cast(con->data); - BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); - - data->cache_file = m_settings->cache_file; - id_us_plus(&data->cache_file->id); - } -} - -Alembic::AbcGeom::IXform AbcObjectReader::xform() -{ - /* Check that we have an empty object (locator, bone head/tail...). */ - if (IXform::matches(m_iobject.getMetaData())) { - try { - return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading object transform for '%s': %s\n", - m_iobject.getFullName().c_str(), - ex.what()); - return IXform(); - } - } - - /* Check that we have an object with actual data, in which case the - * parent Alembic object should contain the transform. */ - IObject abc_parent = m_iobject.getParent(); - - /* The archive's top object can be recognised by not having a parent. */ - if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) { - try { - return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting); - } - catch (Alembic::Util::Exception &ex) { - printf("Alembic: error reading object transform for '%s': %s\n", - abc_parent.getFullName().c_str(), - ex.what()); - return IXform(); - } - } - - /* This can happen in certain cases. For example, MeshLab exports - * point clouds without parent XForm. */ - return IXform(); -} - -void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */, - const float time, - const float scale, - bool &is_constant) -{ - IXform ixform = xform(); - if (!ixform) { - unit_m4(r_mat); - is_constant = true; - return; - } - - const IXformSchema &schema(ixform.getSchema()); - if (!schema.valid()) { - std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema." - << std::endl; - return; - } - - const Imath::M44d matrix = get_matrix(schema, time); - convert_matrix_datatype(matrix, r_mat); - copy_m44_axis_swap(r_mat, r_mat, ABC_ZUP_FROM_YUP); - - /* Convert from Maya to Blender camera orientation. Children of this camera - * will have the opposite transform as their Parent Inverse matrix. - * See AbcObjectReader::setupObjectTransform(). */ - if (m_object->type == OB_CAMERA) { - float camera_rotation[4][4]; - axis_angle_to_mat4_single(camera_rotation, 'X', M_PI_2); - mul_m4_m4m4(r_mat, r_mat, camera_rotation); - } - - if (!m_inherits_xform) { - /* Only apply scaling to root objects, parenting will propagate it. */ - float scale_mat[4][4]; - scale_m4_fl(scale_mat, scale); - mul_m4_m4m4(r_mat, scale_mat, r_mat); - } - - is_constant = schema.isConstant(); -} - -void AbcObjectReader::addCacheModifier() -{ - ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); - BLI_addtail(&m_object->modifiers, md); - - MeshSeqCacheModifierData *mcmd = reinterpret_cast(md); - - mcmd->cache_file = m_settings->cache_file; - id_us_plus(&mcmd->cache_file->id); - - BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX); -} - -chrono_t AbcObjectReader::minTime() const -{ - return m_min_time; -} - -chrono_t AbcObjectReader::maxTime() const -{ - return m_max_time; -} - -int AbcObjectReader::refcount() const -{ - return m_refcount; -} - -void AbcObjectReader::incref() -{ - m_refcount++; -} - -void AbcObjectReader::decref() -{ - m_refcount--; - BLI_assert(m_refcount >= 0); -} diff --git a/source/blender/alembic/intern/abc_reader_object.h b/source/blender/alembic/intern/abc_reader_object.h deleted file mode 100644 index 94923df2df9..00000000000 --- a/source/blender/alembic/intern/abc_reader_object.h +++ /dev/null @@ -1,171 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_OBJECT_H__ -#define __ABC_READER_OBJECT_H__ - -#include -#include - -extern "C" { -#include "DNA_ID.h" -} - -struct CacheFile; -struct Main; -struct Mesh; -struct Object; - -using Alembic::AbcCoreAbstract::chrono_t; - -struct ImportSettings { - bool do_convert_mat; - float conversion_mat[4][4]; - - int from_up; - int from_forward; - float scale; - bool is_sequence; - bool set_frame_range; - - /* Length and frame offset of file sequences. */ - int sequence_len; - int sequence_offset; - - /* From MeshSeqCacheModifierData.read_flag */ - int read_flag; - - bool validate_meshes; - - CacheFile *cache_file; - - ImportSettings() - : do_convert_mat(false), - from_up(0), - from_forward(0), - scale(1.0f), - is_sequence(false), - set_frame_range(false), - sequence_len(1), - sequence_offset(0), - read_flag(0), - validate_meshes(false), - cache_file(NULL) - { - } -}; - -template static bool has_animations(Schema &schema, ImportSettings *settings) -{ - return settings->is_sequence || !schema.isConstant(); -} - -class AbcObjectReader { - protected: - std::string m_name; - std::string m_object_name; - std::string m_data_name; - Object *m_object; - Alembic::Abc::IObject m_iobject; - - ImportSettings *m_settings; - - chrono_t m_min_time; - chrono_t m_max_time; - - /* Use reference counting since the same reader may be used by multiple - * modifiers and/or constraints. */ - int m_refcount; - - bool m_inherits_xform; - - public: - AbcObjectReader *parent_reader; - - public: - explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - virtual ~AbcObjectReader(); - - const Alembic::Abc::IObject &iobject() const; - - typedef std::vector ptr_vector; - - /** - * Returns the transform of this object. This can be the Alembic object - * itself (in case of an Empty) or it can be the parent Alembic object. - */ - virtual Alembic::AbcGeom::IXform xform(); - - Object *object() const; - void object(Object *ob); - - const std::string &name() const - { - return m_name; - } - const std::string &object_name() const - { - return m_object_name; - } - const std::string &data_name() const - { - return m_data_name; - } - bool inherits_xform() const - { - return m_inherits_xform; - } - - virtual bool valid() const = 0; - virtual bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const = 0; - - virtual void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) = 0; - - virtual struct Mesh *read_mesh(struct Mesh *mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char **err_str); - virtual bool topology_changed(Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel); - - /** Reads the object matrix and sets up an object transform if animated. */ - void setupObjectTransform(const float time); - - void addCacheModifier(); - - chrono_t minTime() const; - chrono_t maxTime() const; - - int refcount() const; - void incref(); - void decref(); - - void read_matrix(float r_mat[4][4], const float time, const float scale, bool &is_constant); - - protected: - void determine_inherits_xform(); -}; - -Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time); - -#endif /* __ABC_READER_OBJECT_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_points.cc b/source/blender/alembic/intern/abc_reader_points.cc deleted file mode 100644 index e4dc345f868..00000000000 --- a/source/blender/alembic/intern/abc_reader_points.cc +++ /dev/null @@ -1,157 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_points.h" -#include "abc_reader_mesh.h" -#include "abc_reader_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" - -#include "BKE_customdata.h" -#include "BKE_mesh.h" -#include "BKE_object.h" -} - -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::N3fArraySamplePtr; -using Alembic::AbcGeom::P3fArraySamplePtr; - -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::IN3fArrayProperty; -using Alembic::AbcGeom::IPoints; -using Alembic::AbcGeom::IPointsSchema; -using Alembic::AbcGeom::ISampleSelector; - -AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - IPoints ipoints(m_iobject, kWrapExisting); - m_schema = ipoints.getSchema(); - get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); -} - -bool AbcPointsReader::valid() const -{ - return m_schema.valid(); -} - -bool AbcPointsReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::IPoints::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to Points when importing, but not any " - "more."; - return false; - } - - if (ob->type != OB_MESH) { - *err_str = "Object type mismatch, Alembic object path points to Points."; - return false; - } - - return true; -} - -void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) -{ - Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, NULL); - - if (read_mesh != mesh) { - BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); - } - - if (m_settings->validate_meshes) { - BKE_mesh_validate(mesh, false, false); - } - - m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); - m_object->data = mesh; - - if (has_animations(m_schema, m_settings)) { - addCacheModifier(); - } -} - -void read_points_sample(const IPointsSchema &schema, - const ISampleSelector &selector, - CDStreamConfig &config) -{ - Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector); - - const P3fArraySamplePtr &positions = sample.getPositions(); - - ICompoundProperty prop = schema.getArbGeomParams(); - N3fArraySamplePtr vnormals; - - if (has_property(prop, "N")) { - const Alembic::Util::uint32_t itime = static_cast( - selector.getRequestedTime()); - const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", itime); - - if (normals_prop) { - vnormals = normals_prop.getValue(selector); - } - } - - read_mverts(config.mvert, positions, vnormals); -} - -struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh, - const ISampleSelector &sample_sel, - int /*read_flag*/, - const char **err_str) -{ - IPointsSchema::Sample sample; - try { - sample = m_schema.getValue(sample_sel); - } - catch (Alembic::Util::Exception &ex) { - *err_str = "Error reading points sample; more detail on the console"; - printf("Alembic: error reading points sample for '%s/%s' at time %f: %s\n", - m_iobject.getFullName().c_str(), - m_schema.getName().c_str(), - sample_sel.getRequestedTime(), - ex.what()); - return existing_mesh; - } - - const P3fArraySamplePtr &positions = sample.getPositions(); - - Mesh *new_mesh = NULL; - - if (existing_mesh->totvert != positions->size()) { - new_mesh = BKE_mesh_new_nomain(positions->size(), 0, 0, 0, 0); - } - - CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); - read_points_sample(m_schema, sample_sel, config); - - return new_mesh ? new_mesh : existing_mesh; -} diff --git a/source/blender/alembic/intern/abc_reader_points.h b/source/blender/alembic/intern/abc_reader_points.h deleted file mode 100644 index 31ad6c4589b..00000000000 --- a/source/blender/alembic/intern/abc_reader_points.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_POINTS_H__ -#define __ABC_READER_POINTS_H__ - -#include "abc_reader_object.h" -#include "abc_customdata.h" - -class AbcPointsReader : public AbcObjectReader { - Alembic::AbcGeom::IPointsSchema m_schema; - Alembic::AbcGeom::IPointsSchema::Sample m_sample; - - public: - AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const; - - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); - - struct Mesh *read_mesh(struct Mesh *existing_mesh, - const Alembic::Abc::ISampleSelector &sample_sel, - int read_flag, - const char **err_str); -}; - -void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, - const Alembic::AbcGeom::ISampleSelector &selector, - CDStreamConfig &config); - -#endif /* __ABC_READER_POINTS_H__ */ diff --git a/source/blender/alembic/intern/abc_reader_transform.cc b/source/blender/alembic/intern/abc_reader_transform.cc deleted file mode 100644 index ce569a9ccb5..00000000000 --- a/source/blender/alembic/intern/abc_reader_transform.cc +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_reader_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_object_types.h" - -#include "BLI_utildefines.h" - -#include "BKE_object.h" -} - -using Alembic::Abc::ISampleSelector; - -AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) - : AbcObjectReader(object, settings) -{ - /* Empties have no data. It makes the import of Alembic files easier to - * understand when we name the empty after its name in Alembic. */ - m_object_name = object.getName(); - - Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting); - m_schema = xform.getSchema(); - - get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); -} - -bool AbcEmptyReader::valid() const -{ - return m_schema.valid(); -} - -bool AbcEmptyReader::accepts_object_type( - const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const -{ - if (!Alembic::AbcGeom::IXform::matches(alembic_header)) { - *err_str = - "Object type mismatch, Alembic object path pointed to XForm when importing, but not any " - "more."; - return false; - } - - if (ob->type != OB_EMPTY) { - *err_str = "Object type mismatch, Alembic object path points to XForm."; - return false; - } - - return true; -} - -void AbcEmptyReader::readObjectData(Main *bmain, const ISampleSelector &UNUSED(sample_sel)) -{ - m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str()); - m_object->data = NULL; -} diff --git a/source/blender/alembic/intern/abc_reader_transform.h b/source/blender/alembic/intern/abc_reader_transform.h deleted file mode 100644 index 6b4d23c1884..00000000000 --- a/source/blender/alembic/intern/abc_reader_transform.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_READER_TRANSFORM_H__ -#define __ABC_READER_TRANSFORM_H__ - -#include "abc_reader_object.h" - -#include - -class AbcEmptyReader : public AbcObjectReader { - Alembic::AbcGeom::IXformSchema m_schema; - - public: - AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - - bool valid() const; - bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, - const Object *const ob, - const char **err_str) const; - - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); -}; - -#endif /* __ABC_READER_TRANSFORM_H__ */ diff --git a/source/blender/alembic/intern/abc_util.cc b/source/blender/alembic/intern/abc_util.cc deleted file mode 100644 index b26ef8b3b76..00000000000 --- a/source/blender/alembic/intern/abc_util.cc +++ /dev/null @@ -1,393 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_util.h" - -#include "abc_reader_camera.h" -#include "abc_reader_curves.h" -#include "abc_reader_mesh.h" -#include "abc_reader_nurbs.h" -#include "abc_reader_points.h" -#include "abc_reader_transform.h" - -#include - -#include - -extern "C" { -#include "DNA_object_types.h" - -#include "BLI_math_geom.h" - -#include "PIL_time.h" -} - -std::string get_id_name(const Object *const ob) -{ - if (!ob) { - return ""; - } - - return get_id_name(&ob->id); -} - -std::string get_id_name(const ID *const id) -{ - std::string name(id->name + 2); - std::replace(name.begin(), name.end(), ' ', '_'); - std::replace(name.begin(), name.end(), '.', '_'); - std::replace(name.begin(), name.end(), ':', '_'); - - return name; -} - -/** - * \brief get_object_dag_path_name returns the name under which the object - * will be exported in the Alembic file. It is of the form - * "[../grandparent/]parent/object" if dupli_parent is NULL, or - * "dupli_parent/[../grandparent/]parent/object" otherwise. - * \param ob: - * \param dupli_parent: - * \return - */ -std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent) -{ - std::string name = get_id_name(ob); - - Object *p = ob->parent; - - while (p) { - name = get_id_name(p) + "/" + name; - p = p->parent; - } - - if (dupli_parent && (ob != dupli_parent)) { - name = get_id_name(dupli_parent) + "/" + name; - } - - return name; -} - -Imath::M44d convert_matrix_datatype(float mat[4][4]) -{ - Imath::M44d m; - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - m[i][j] = mat[i][j]; - } - } - - return m; -} - -void convert_matrix_datatype(const Imath::M44d &xform, float r_mat[4][4]) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - r_mat[i][j] = static_cast(xform[i][j]); - } - } -} - -void split(const std::string &s, const char delim, std::vector &tokens) -{ - tokens.clear(); - - std::stringstream ss(s); - std::string item; - - while (std::getline(ss, item, delim)) { - if (!item.empty()) { - tokens.push_back(item); - } - } -} - -void create_swapped_rotation_matrix(float rot_x_mat[3][3], - float rot_y_mat[3][3], - float rot_z_mat[3][3], - const float euler[3], - AbcAxisSwapMode mode) -{ - const float rx = euler[0]; - float ry; - float rz; - - /* Apply transformation */ - switch (mode) { - case ABC_ZUP_FROM_YUP: - ry = -euler[2]; - rz = euler[1]; - break; - case ABC_YUP_FROM_ZUP: - ry = euler[2]; - rz = -euler[1]; - break; - default: - ry = 0.0f; - rz = 0.0f; - BLI_assert(false); - break; - } - - unit_m3(rot_x_mat); - unit_m3(rot_y_mat); - unit_m3(rot_z_mat); - - rot_x_mat[1][1] = cos(rx); - rot_x_mat[2][1] = -sin(rx); - rot_x_mat[1][2] = sin(rx); - rot_x_mat[2][2] = cos(rx); - - rot_y_mat[2][2] = cos(ry); - rot_y_mat[0][2] = -sin(ry); - rot_y_mat[2][0] = sin(ry); - rot_y_mat[0][0] = cos(ry); - - rot_z_mat[0][0] = cos(rz); - rot_z_mat[1][0] = -sin(rz); - rot_z_mat[0][1] = sin(rz); - rot_z_mat[1][1] = cos(rz); -} - -/* Convert matrix from Z=up to Y=up or vice versa. - * Use yup_mat = zup_mat for in-place conversion. */ -void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode) -{ - float dst_rot[3][3], src_rot[3][3], dst_scale_mat[4][4]; - float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; - float src_trans[3], dst_scale[3], src_scale[3], euler[3]; - - zero_v3(src_trans); - zero_v3(dst_scale); - zero_v3(src_scale); - zero_v3(euler); - unit_m3(src_rot); - unit_m3(dst_rot); - unit_m4(dst_scale_mat); - - /* TODO(Sybren): This code assumes there is no sheer component and no - * homogeneous scaling component, which is not always true when writing - * non-hierarchical (e.g. flat) objects (e.g. when parent has non-uniform - * scale and the child rotates). This is currently not taken into account - * when axis-swapping. */ - - /* Extract translation, rotation, and scale form matrix. */ - mat4_to_loc_rot_size(src_trans, src_rot, src_scale, src_mat); - - /* Get euler angles from rotation matrix. */ - mat3_to_eulO(euler, ROT_MODE_XZY, src_rot); - - /* Create X, Y, Z rotation matrices from euler angles. */ - create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, mode); - - /* Concatenate rotation matrices. */ - mul_m3_m3m3(dst_rot, dst_rot, rot_z_mat); - mul_m3_m3m3(dst_rot, dst_rot, rot_y_mat); - mul_m3_m3m3(dst_rot, dst_rot, rot_x_mat); - - mat3_to_eulO(euler, ROT_MODE_XZY, dst_rot); - - /* Start construction of dst_mat from rotation matrix */ - unit_m4(dst_mat); - copy_m4_m3(dst_mat, dst_rot); - - /* Apply translation */ - switch (mode) { - case ABC_ZUP_FROM_YUP: - copy_zup_from_yup(dst_mat[3], src_trans); - break; - case ABC_YUP_FROM_ZUP: - copy_yup_from_zup(dst_mat[3], src_trans); - break; - default: - BLI_assert(false); - } - - /* Apply scale matrix. Swaps y and z, but does not - * negate like translation does. */ - dst_scale[0] = src_scale[0]; - dst_scale[1] = src_scale[2]; - dst_scale[2] = src_scale[1]; - - size_to_mat4(dst_scale_mat, dst_scale); - mul_m4_m4m4(dst_mat, dst_mat, dst_scale_mat); -} - -/* Recompute transform matrix of object in new coordinate system - * (from Z-Up to Y-Up). */ -void create_transform_matrix(Object *obj, - float r_yup_mat[4][4], - AbcMatrixMode mode, - Object *proxy_from) -{ - float zup_mat[4][4]; - - /* get local or world matrix. */ - if (mode == ABC_MATRIX_LOCAL && obj->parent) { - /* Note that this produces another matrix than the local matrix, due to - * constraints and modifiers as well as the obj->parentinv matrix. */ - invert_m4_m4(obj->parent->imat, obj->parent->obmat); - mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat); - } - else { - copy_m4_m4(zup_mat, obj->obmat); - } - - if (proxy_from) { - mul_m4_m4m4(zup_mat, proxy_from->obmat, zup_mat); - } - - copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); -} - -bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name) -{ - if (!prop.valid()) { - return false; - } - - return prop.getPropertyHeader(name) != NULL; -} - -typedef std::pair index_time_pair_t; - -float get_weight_and_index(float time, - const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, - int samples_number, - Alembic::AbcGeom::index_t &i0, - Alembic::AbcGeom::index_t &i1) -{ - samples_number = std::max(samples_number, 1); - - index_time_pair_t t0 = time_sampling->getFloorIndex(time, samples_number); - i0 = i1 = t0.first; - - if (samples_number == 1 || (fabs(time - t0.second) < 0.0001f)) { - return 0.0f; - } - - index_time_pair_t t1 = time_sampling->getCeilIndex(time, samples_number); - i1 = t1.first; - - if (i0 == i1) { - return 0.0f; - } - - const float bias = (time - t0.second) / (t1.second - t0.second); - - if (fabs(1.0f - bias) < 0.0001f) { - i0 = i1; - return 0.0f; - } - - return bias; -} - -//#define USE_NURBS - -AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings) -{ - AbcObjectReader *reader = NULL; - - const Alembic::AbcGeom::MetaData &md = object.getMetaData(); - - if (Alembic::AbcGeom::IXform::matches(md)) { - reader = new AbcEmptyReader(object, settings); - } - else if (Alembic::AbcGeom::IPolyMesh::matches(md)) { - reader = new AbcMeshReader(object, settings); - } - else if (Alembic::AbcGeom::ISubD::matches(md)) { - reader = new AbcSubDReader(object, settings); - } - else if (Alembic::AbcGeom::INuPatch::matches(md)) { -#ifdef USE_NURBS - /* TODO(kevin): importing cyclic NURBS from other software crashes - * at the moment. This is due to the fact that NURBS in other - * software have duplicated points which causes buffer overflows in - * Blender. Need to figure out exactly how these points are - * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). - * Until this is fixed, disabling NURBS reading. */ - reader = new AbcNurbsReader(child, settings); -#endif - } - else if (Alembic::AbcGeom::ICamera::matches(md)) { - reader = new AbcCameraReader(object, settings); - } - else if (Alembic::AbcGeom::IPoints::matches(md)) { - reader = new AbcPointsReader(object, settings); - } - else if (Alembic::AbcMaterial::IMaterial::matches(md)) { - /* Pass for now. */ - } - else if (Alembic::AbcGeom::ILight::matches(md)) { - /* Pass for now. */ - } - else if (Alembic::AbcGeom::IFaceSet::matches(md)) { - /* Pass, those are handled in the mesh reader. */ - } - else if (Alembic::AbcGeom::ICurves::matches(md)) { - reader = new AbcCurveReader(object, settings); - } - else { - std::cerr << "Alembic: unknown how to handle objects of schema '" << md.get("schemaObjTitle") - << "', skipping object '" << object.getFullName() << "'" << std::endl; - } - - return reader; -} - -/* ********************** */ - -ScopeTimer::ScopeTimer(const char *message) - : m_message(message), m_start(PIL_check_seconds_timer()) -{ -} - -ScopeTimer::~ScopeTimer() -{ - fprintf(stderr, "%s: %fs\n", m_message, PIL_check_seconds_timer() - m_start); -} - -/* ********************** */ - -std::string SimpleLogger::str() const -{ - return m_stream.str(); -} - -void SimpleLogger::clear() -{ - m_stream.clear(); - m_stream.str(""); -} - -std::ostringstream &SimpleLogger::stream() -{ - return m_stream; -} - -std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger) -{ - os << logger.str(); - return os; -} diff --git a/source/blender/alembic/intern/abc_util.h b/source/blender/alembic/intern/abc_util.h deleted file mode 100644 index 0b3462c2132..00000000000 --- a/source/blender/alembic/intern/abc_util.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_UTIL_H__ -#define __ABC_UTIL_H__ - -#include -#include - -#ifdef _MSC_VER -# define ABC_INLINE static __forceinline -#else -# define ABC_INLINE static inline -#endif - -/** - * \brief The CacheReader struct is only used for anonymous pointers, - * to interface between C and C++ code. This library only creates - * pointers to AbcObjectReader (or subclasses thereof). - */ -struct CacheReader { - int unused; -}; - -using Alembic::Abc::chrono_t; - -class AbcObjectReader; -struct ImportSettings; - -struct ID; -struct Object; - -std::string get_id_name(const ID *const id); -std::string get_id_name(const Object *const ob); -std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent); - -/* Convert from float to Alembic matrix representations. Does NOT convert from Z-up to Y-up. */ -Imath::M44d convert_matrix_datatype(float mat[4][4]); -/* Convert from Alembic to float matrix representations. Does NOT convert from Y-up to Z-up. */ -void convert_matrix_datatype(const Imath::M44d &xform, float r_mat[4][4]); - -typedef enum { - ABC_MATRIX_WORLD = 1, - ABC_MATRIX_LOCAL = 2, -} AbcMatrixMode; -void create_transform_matrix(Object *obj, - float r_transform_mat[4][4], - AbcMatrixMode mode, - Object *proxy_from); - -void split(const std::string &s, const char delim, std::vector &tokens); - -template bool begins_with(const TContainer &input, const TContainer &match) -{ - return input.size() >= match.size() && std::equal(match.begin(), match.end(), input.begin()); -} - -template -void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max) -{ - const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling(); - - if (!schema.isConstant()) { - const size_t num_samps = schema.getNumSamples(); - - if (num_samps > 0) { - const chrono_t min_time = time_samp->getSampleTime(0); - min = std::min(min, min_time); - - const chrono_t max_time = time_samp->getSampleTime(num_samps - 1); - max = std::max(max, max_time); - } - } -} - -template -void get_min_max_time(const Alembic::AbcGeom::IObject &object, - const Schema &schema, - chrono_t &min, - chrono_t &max) -{ - get_min_max_time_ex(schema, min, max); - - const Alembic::AbcGeom::IObject &parent = object.getParent(); - if (parent.valid() && Alembic::AbcGeom::IXform::matches(parent.getMetaData())) { - Alembic::AbcGeom::IXform xform(parent, Alembic::AbcGeom::kWrapExisting); - get_min_max_time_ex(xform.getSchema(), min, max); - } -} - -bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name); - -float get_weight_and_index(float time, - const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, - int samples_number, - Alembic::AbcGeom::index_t &i0, - Alembic::AbcGeom::index_t &i1); - -AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings); - -/* ************************** */ - -/* TODO(kevin): for now keeping these transformations hardcoded to make sure - * everything works properly, and also because Alembic is almost exclusively - * used in Y-up software, but eventually they'll be set by the user in the UI - * like other importers/exporters do, to support other axis. */ - -/* Copy from Y-up to Z-up. */ - -ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) -{ - const float old_yup1 = yup[1]; /* in case zup == yup */ - zup[0] = yup[0]; - zup[1] = -yup[2]; - zup[2] = old_yup1; -} - -ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) -{ - const short old_yup1 = yup[1]; /* in case zup == yup */ - zup[0] = yup[0]; - zup[1] = -yup[2]; - zup[2] = old_yup1; -} - -/* Copy from Z-up to Y-up. */ - -ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) -{ - const float old_zup1 = zup[1]; /* in case yup == zup */ - yup[0] = zup[0]; - yup[1] = zup[2]; - yup[2] = -old_zup1; -} - -ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3]) -{ - const short old_zup1 = zup[1]; /* in case yup == zup */ - yup[0] = zup[0]; - yup[1] = zup[2]; - yup[2] = -old_zup1; -} - -/* Names are given in (dst, src) order, just like - * the parameters of copy_m44_axis_swap() */ -typedef enum { - ABC_ZUP_FROM_YUP = 1, - ABC_YUP_FROM_ZUP = 2, -} AbcAxisSwapMode; - -/* Create a rotation matrix for each axis from euler angles. - * Euler angles are swapped to change coordinate system. */ -void create_swapped_rotation_matrix(float rot_x_mat[3][3], - float rot_y_mat[3][3], - float rot_z_mat[3][3], - const float euler[3], - AbcAxisSwapMode mode); - -void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode); - -/* *************************** */ - -#undef ABC_DEBUG_TIME - -class ScopeTimer { - const char *m_message; - double m_start; - - public: - ScopeTimer(const char *message); - ~ScopeTimer(); -}; - -#ifdef ABC_DEBUG_TIME -# define SCOPE_TIMER(message) ScopeTimer prof(message) -#else -# define SCOPE_TIMER(message) -#endif - -/* *************************** */ - -/** - * Utility class whose purpose is to more easily log related information. An - * instance of the SimpleLogger can be created in any context, and will hold a - * copy of all the strings passed to its output stream. - * - * Different instances of the class may be accessed from different threads, - * although accessing the same instance from different threads will lead to race - * conditions. - */ -class SimpleLogger { - std::ostringstream m_stream; - - public: - /** - * Return a copy of the string contained in the SimpleLogger's stream. - */ - std::string str() const; - - /** - * Remove the bits set on the SimpleLogger's stream and clear its string. - */ - void clear(); - - /** - * Return a reference to the SimpleLogger's stream, in order to e.g. push - * content into it. - */ - std::ostringstream &stream(); -}; - -#define ABC_LOG(logger) logger.stream() - -/** - * Pass the content of the logger's stream to the specified std::ostream. - */ -std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); - -#endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_archive.cc b/source/blender/alembic/intern/abc_writer_archive.cc deleted file mode 100644 index af18d480a18..00000000000 --- a/source/blender/alembic/intern/abc_writer_archive.cc +++ /dev/null @@ -1,114 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_archive.h" -extern "C" { -#include "BKE_blender_version.h" - -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "DNA_scene_types.h" -} - -#ifdef WIN32 -# include "utfconv.h" -#endif - -#include - -using Alembic::Abc::ErrorHandler; -using Alembic::Abc::kWrapExisting; -using Alembic::Abc::OArchive; - -/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to - * have a version supporting streams. */ -static OArchive create_archive(std::ostream *ostream, - const std::string &filename, - const std::string &scene_name, - double scene_fps, - bool ogawa) -{ - Alembic::Abc::MetaData abc_metadata; - - abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); - abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name); - abc_metadata.set("blender_version", versionstr); - abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); - - time_t raw_time; - time(&raw_time); - char buffer[128]; - -#if defined _WIN32 || defined _WIN64 - ctime_s(buffer, 128, &raw_time); -#else - ctime_r(&raw_time, buffer); -#endif - - const std::size_t buffer_len = strlen(buffer); - if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { - buffer[buffer_len - 1] = '\0'; - } - - abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); - - ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; - -#ifdef WITH_ALEMBIC_HDF5 - if (!ogawa) { - return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, abc_metadata, policy); - } -#else - static_cast(filename); - static_cast(ogawa); -#endif - - Alembic::AbcCoreOgawa::WriteArchive archive_writer; - return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy); -} - -ArchiveWriter::ArchiveWriter(const char *filename, - const std::string &abc_scene_name, - const Scene *scene, - bool do_ogawa) -{ - /* Use stream to support unicode character paths on Windows. */ - if (do_ogawa) { -#ifdef WIN32 - UTF16_ENCODE(filename); - std::wstring wstr(filename_16); - m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary); - UTF16_UN_ENCODE(filename); -#else - m_outfile.open(filename, std::ios::out | std::ios::binary); -#endif - } - - m_archive = create_archive(&m_outfile, filename, abc_scene_name, FPS, do_ogawa); -} - -OArchive &ArchiveWriter::archive() -{ - return m_archive; -} diff --git a/source/blender/alembic/intern/abc_writer_archive.h b/source/blender/alembic/intern/abc_writer_archive.h deleted file mode 100644 index e261e60990a..00000000000 --- a/source/blender/alembic/intern/abc_writer_archive.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_ARCHIVE_H__ -#define __ABC_WRITER_ARCHIVE_H__ - -#include - -#ifdef WITH_ALEMBIC_HDF5 -# include -#endif - -#include - -#include - -struct Main; -struct Scene; - -/* Wrappers around input and output archives. The goal is to be able to use - * streams so that unicode paths work on Windows (T49112), and to make sure that - * the stream objects remain valid as long as the archives are open. - */ - -class ArchiveWriter { - std::ofstream m_outfile; - Alembic::Abc::OArchive m_archive; - - public: - ArchiveWriter(const char *filename, - const std::string &abc_scene_name, - const Scene *scene, - bool do_ogawa); - - Alembic::Abc::OArchive &archive(); -}; - -#endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_camera.cc b/source/blender/alembic/intern/abc_writer_camera.cc deleted file mode 100644 index e705e5ba911..00000000000 --- a/source/blender/alembic/intern/abc_writer_camera.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_camera.h" -#include "abc_writer_transform.h" - -extern "C" { -#include "DNA_camera_types.h" -#include "DNA_object_types.h" -} - -using Alembic::AbcGeom::OCamera; -using Alembic::AbcGeom::OFloatProperty; - -AbcCameraWriter::AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - OCamera camera(parent->alembicXform(), m_name, m_time_sampling); - m_camera_schema = camera.getSchema(); - - m_custom_data_container = m_camera_schema.getUserProperties(); - m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); - m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); -} - -void AbcCameraWriter::do_write() -{ - Camera *cam = static_cast(m_object->data); - - m_stereo_distance.set(cam->stereo.convergence_distance); - m_eye_separation.set(cam->stereo.interocular_distance); - - const double apperture_x = cam->sensor_x / 10.0; - const double apperture_y = cam->sensor_y / 10.0; - const double film_aspect = apperture_x / apperture_y; - - m_camera_sample.setFocalLength(cam->lens); - m_camera_sample.setHorizontalAperture(apperture_x); - m_camera_sample.setVerticalAperture(apperture_y); - m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); - m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); - m_camera_sample.setNearClippingPlane(cam->clip_start); - m_camera_sample.setFarClippingPlane(cam->clip_end); - - if (cam->dof.focus_object) { - Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0], - m_object->loc[1] - cam->dof.focus_object->loc[1], - m_object->loc[2] - cam->dof.focus_object->loc[2]); - m_camera_sample.setFocusDistance(v.length()); - } - else { - m_camera_sample.setFocusDistance(cam->dof.focus_distance); - } - - /* Blender camera does not have an fstop param, so try to find a custom prop - * instead. */ - m_camera_sample.setFStop(cam->dof.aperture_fstop); - - m_camera_sample.setLensSqueezeRatio(1.0); - m_camera_schema.set(m_camera_sample); -} diff --git a/source/blender/alembic/intern/abc_writer_camera.h b/source/blender/alembic/intern/abc_writer_camera.h deleted file mode 100644 index 3b515911a48..00000000000 --- a/source/blender/alembic/intern/abc_writer_camera.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_CAMERA_H__ -#define __ABC_WRITER_CAMERA_H__ - -#include "abc_writer_object.h" - -/* ************************************************************************** */ - -class AbcCameraWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCameraSchema m_camera_schema; - Alembic::AbcGeom::CameraSample m_camera_sample; - Alembic::AbcGeom::OCompoundProperty m_custom_data_container; - Alembic::AbcGeom::OFloatProperty m_stereo_distance; - Alembic::AbcGeom::OFloatProperty m_eye_separation; - - public: - AbcCameraWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - private: - virtual void do_write(); -}; - -#endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_curves.cc b/source/blender/alembic/intern/abc_writer_curves.cc deleted file mode 100644 index 3ab9b365a72..00000000000 --- a/source/blender/alembic/intern/abc_writer_curves.cc +++ /dev/null @@ -1,189 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_curves.h" -#include "abc_reader_curves.h" -#include "abc_writer_transform.h" - -extern "C" { -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BKE_curve.h" -#include "BKE_mesh.h" -#include "BKE_object.h" -} - -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::OCurves; -using Alembic::AbcGeom::OCurvesSchema; -using Alembic::AbcGeom::OInt16Property; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OV2fGeomParam; - -AbcCurveWriter::AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - OCurves curves(parent->alembicXform(), m_name, m_time_sampling); - m_schema = curves.getSchema(); - - Curve *cu = static_cast(m_object->data); - OCompoundProperty user_props = m_schema.getUserProperties(); - OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); - user_prop_resolu.set(cu->resolu); -} - -void AbcCurveWriter::do_write() -{ - Curve *curve = static_cast(m_object->data); - - std::vector verts; - std::vector vert_counts; - std::vector widths; - std::vector weights; - std::vector knots; - std::vector orders; - Imath::V3f temp_vert; - - Alembic::AbcGeom::BasisType curve_basis; - Alembic::AbcGeom::CurveType curve_type; - Alembic::AbcGeom::CurvePeriodicity periodicity; - - Nurb *nurbs = static_cast(curve->nurb.first); - for (; nurbs; nurbs = nurbs->next) { - if (nurbs->bp) { - curve_basis = Alembic::AbcGeom::kNoBasis; - curve_type = Alembic::AbcGeom::kVariableOrder; - - const int totpoint = nurbs->pntsu * nurbs->pntsv; - - const BPoint *point = nurbs->bp; - - for (int i = 0; i < totpoint; i++, point++) { - copy_yup_from_zup(temp_vert.getValue(), point->vec); - verts.push_back(temp_vert); - weights.push_back(point->vec[3]); - widths.push_back(point->radius); - } - } - else if (nurbs->bezt) { - curve_basis = Alembic::AbcGeom::kBezierBasis; - curve_type = Alembic::AbcGeom::kCubic; - - const int totpoint = nurbs->pntsu; - - const BezTriple *bezier = nurbs->bezt; - - /* TODO(kevin): store info about handles, Alembic doesn't have this. */ - for (int i = 0; i < totpoint; i++, bezier++) { - copy_yup_from_zup(temp_vert.getValue(), bezier->vec[1]); - verts.push_back(temp_vert); - widths.push_back(bezier->radius); - } - } - - if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { - periodicity = Alembic::AbcGeom::kNonPeriodic; - } - else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { - periodicity = Alembic::AbcGeom::kPeriodic; - - /* Duplicate the start points to indicate that the curve is actually - * cyclic since other software need those. - */ - - for (int i = 0; i < nurbs->orderu; i++) { - verts.push_back(verts[i]); - } - } - - if (nurbs->knotsu != NULL) { - const size_t num_knots = KNOTSU(nurbs); - - /* Add an extra knot at the beginning and end of the array since most apps - * require/expect them. */ - knots.resize(num_knots + 2); - - for (int i = 0; i < num_knots; i++) { - knots[i + 1] = nurbs->knotsu[i]; - } - - if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { - knots[0] = nurbs->knotsu[0]; - knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; - } - else { - knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); - knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - - nurbs->knotsu[num_knots - 2]); - } - } - - orders.push_back(nurbs->orderu); - vert_counts.push_back(verts.size()); - } - - Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; - width_sample.setVals(widths); - - m_sample = OCurvesSchema::Sample(verts, - vert_counts, - curve_type, - periodicity, - width_sample, - OV2fGeomParam::Sample(), /* UVs */ - ON3fGeomParam::Sample(), /* normals */ - curve_basis, - weights, - orders, - knots); - - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); -} - -AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) -{ -} - -Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, - Object *ob_eval, - bool &r_needsfree) -{ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); - if (mesh_eval != NULL) { - /* Mesh_eval only exists when generative modifiers are in use. */ - r_needsfree = false; - return mesh_eval; - } - - r_needsfree = true; - return BKE_mesh_new_nomain_from_curve(ob_eval); -} diff --git a/source/blender/alembic/intern/abc_writer_curves.h b/source/blender/alembic/intern/abc_writer_curves.h deleted file mode 100644 index d6d8c0a7f11..00000000000 --- a/source/blender/alembic/intern/abc_writer_curves.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_CURVES_H__ -#define __ABC_WRITER_CURVES_H__ - -#include "abc_writer_object.h" -#include "abc_writer_mesh.h" - -class AbcCurveWriter : public AbcObjectWriter { - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; - - public: - AbcCurveWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - protected: - void do_write(); -}; - -class AbcCurveMeshWriter : public AbcGenericMeshWriter { - public: - AbcCurveMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); -}; - -#endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_hair.cc b/source/blender/alembic/intern/abc_writer_hair.cc deleted file mode 100644 index bbba03ed7f4..00000000000 --- a/source/blender/alembic/intern/abc_writer_hair.cc +++ /dev/null @@ -1,292 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_hair.h" -#include "abc_writer_transform.h" -#include "abc_util.h" - -#include - -extern "C" { -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" - -#include "BLI_math_geom.h" - -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_particle.h" -} - -using Alembic::Abc::P3fArraySamplePtr; - -using Alembic::AbcGeom::OCurves; -using Alembic::AbcGeom::OCurvesSchema; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OV2fGeomParam; - -/* ************************************************************************** */ - -AbcHairWriter::AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false) -{ - m_psys = psys; - - OCurves curves(parent->alembicXform(), psys->name, m_time_sampling); - m_schema = curves.getSchema(); -} - -void AbcHairWriter::do_write() -{ - if (!m_psys) { - return; - } - Mesh *mesh = mesh_get_eval_final( - m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH); - BKE_mesh_tessface_ensure(mesh); - - std::vector verts; - std::vector hvertices; - std::vector uv_values; - std::vector norm_values; - - if (m_psys->pathcache) { - ParticleSettings *part = m_psys->part; - bool export_children = m_settings.export_child_hairs && m_psys->childcache && - part->childtype != 0; - - if (!export_children || part->draw & PART_DRAW_PARENT) { - write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices); - } - - if (export_children) { - write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices); - } - } - - Alembic::Abc::P3fArraySample iPos(verts); - m_sample = OCurvesSchema::Sample(iPos, hvertices); - m_sample.setBasis(Alembic::AbcGeom::kNoBasis); - m_sample.setType(Alembic::AbcGeom::kLinear); - m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); - - if (!uv_values.empty()) { - OV2fGeomParam::Sample uv_smp; - uv_smp.setVals(uv_values); - m_sample.setUVs(uv_smp); - } - - if (!norm_values.empty()) { - ON3fGeomParam::Sample norm_smp; - norm_smp.setVals(norm_values); - m_sample.setNormals(norm_smp); - } - - m_sample.setSelfBounds(bounds()); - m_schema.set(m_sample); -} - -void AbcHairWriter::write_hair_sample(Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices) -{ - /* Get untransformed vertices, there's a xform under the hair. */ - float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); - - MTFace *mtface = mesh->mtface; - MFace *mface = mesh->mface; - MVert *mverts = mesh->mvert; - - if ((!mtface || !mface) && !m_uv_warning_shown) { - std::fprintf(stderr, - "Warning, no UV set found for underlying geometry of %s.\n", - m_object->id.name + 2); - m_uv_warning_shown = true; - } - - ParticleData *pa = m_psys->particles; - int k; - - ParticleCacheKey **cache = m_psys->pathcache; - ParticleCacheKey *path; - float normal[3]; - Imath::V3f tmp_nor; - - for (int p = 0; p < m_psys->totpart; p++, pa++) { - /* underlying info for faces-only emission */ - path = cache[p]; - - /* Write UV and normal vectors */ - if (part->from == PART_FROM_FACE && mtface) { - const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; - - if (num < mesh->totface) { - /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */ - MFace *face = mface == NULL ? NULL : &mface[num]; - MTFace *tface = mtface + num; - - if (mface) { - float r_uv[2], mapfw[4], vec[3]; - - psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); - uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); - - psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL); - - copy_yup_from_zup(tmp_nor.getValue(), normal); - norm_values.push_back(tmp_nor); - } - } - else { - std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface); - } - } - else if (part->from == PART_FROM_VERT && mtface) { - /* vertex id */ - const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; - - /* iterate over all faces to find a corresponding underlying UV */ - for (int n = 0; n < mesh->totface; n++) { - MFace *face = &mface[n]; - MTFace *tface = mtface + n; - unsigned int vtx[4]; - vtx[0] = face->v1; - vtx[1] = face->v2; - vtx[2] = face->v3; - vtx[3] = face->v4; - bool found = false; - - for (int o = 0; o < 4; o++) { - if (o > 2 && vtx[o] == 0) { - break; - } - - if (vtx[o] == num) { - uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); - - MVert *mv = mverts + vtx[o]; - - normal_short_to_float_v3(normal, mv->no); - copy_yup_from_zup(tmp_nor.getValue(), normal); - norm_values.push_back(tmp_nor); - found = true; - break; - } - } - - if (found) { - break; - } - } - } - - int steps = path->segments + 1; - hvertices.push_back(steps); - - for (k = 0; k < steps; k++, path++) { - float vert[3]; - copy_v3_v3(vert, path->co); - mul_m4_v3(inv_mat, vert); - - /* Convert Z-up to Y-up. */ - verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); - } - } -} - -void AbcHairWriter::write_hair_child_sample(Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices) -{ - /* Get untransformed vertices, there's a xform under the hair. */ - float inv_mat[4][4]; - invert_m4_m4_safe(inv_mat, m_object->obmat); - - MTFace *mtface = mesh->mtface; - MVert *mverts = mesh->mvert; - - ParticleCacheKey **cache = m_psys->childcache; - ParticleCacheKey *path; - - ChildParticle *pc = m_psys->child; - - for (int p = 0; p < m_psys->totchild; p++, pc++) { - path = cache[p]; - - if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) { - const int num = pc->num; - if (num < 0) { - ABC_LOG(m_settings.logger) - << "Warning, child particle of hair system " << m_psys->name - << " has unknown face index of geometry of " << (m_object->id.name + 2) - << ", skipping child hair." << std::endl; - continue; - } - - MFace *face = &mesh->mface[num]; - MTFace *tface = mtface + num; - - float r_uv[2], tmpnor[3], mapfw[4], vec[3]; - - psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); - uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); - - psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL); - - /* Convert Z-up to Y-up. */ - norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); - } - else { - if (uv_values.size()) { - uv_values.push_back(uv_values[pc->parent]); - } - if (norm_values.size()) { - norm_values.push_back(norm_values[pc->parent]); - } - } - - int steps = path->segments + 1; - hvertices.push_back(steps); - - for (int k = 0; k < steps; k++) { - float vert[3]; - copy_v3_v3(vert, path->co); - mul_m4_v3(inv_mat, vert); - - /* Convert Z-up to Y-up. */ - verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); - - path++; - } - } -} diff --git a/source/blender/alembic/intern/abc_writer_hair.h b/source/blender/alembic/intern/abc_writer_hair.h deleted file mode 100644 index 67d1b7b3d23..00000000000 --- a/source/blender/alembic/intern/abc_writer_hair.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_HAIR_H__ -#define __ABC_WRITER_HAIR_H__ - -#include "abc_writer_object.h" - -struct ParticleSettings; -struct ParticleSystem; - -class AbcHairWriter : public AbcObjectWriter { - ParticleSystem *m_psys; - - Alembic::AbcGeom::OCurvesSchema m_schema; - Alembic::AbcGeom::OCurvesSchema::Sample m_sample; - - bool m_uv_warning_shown; - - public: - AbcHairWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); - - private: - virtual void do_write(); - - void write_hair_sample(struct Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices); - - void write_hair_child_sample(struct Mesh *mesh, - ParticleSettings *part, - std::vector &verts, - std::vector &norm_values, - std::vector &uv_values, - std::vector &hvertices); -}; - -#endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_mball.cc b/source/blender/alembic/intern/abc_writer_mball.cc deleted file mode 100644 index cc0775bd537..00000000000 --- a/source/blender/alembic/intern/abc_writer_mball.cc +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_mball.h" -#include "abc_writer_mesh.h" - -extern "C" { -#include "DNA_meta_types.h" -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" - -#include "BKE_displist.h" -#include "BKE_lib_id.h" -#include "BKE_mball.h" -#include "BKE_mesh.h" -#include "BKE_object.h" - -#include "BLI_utildefines.h" -} - -AbcMBallWriter::AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain) -{ - m_is_animated = isAnimated(); -} - -AbcMBallWriter::~AbcMBallWriter() -{ -} - -bool AbcMBallWriter::isAnimated() const -{ - return true; -} - -Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) -{ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); - if (mesh_eval != NULL) { - /* Mesh_eval only exists when generative modifiers are in use. */ - r_needsfree = false; - return mesh_eval; - } - r_needsfree = true; - - /* The approach below is copied from BKE_mesh_new_from_object() */ - Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2); - BLI_assert(tmpmesh != NULL); - - /* BKE_mesh_add gives us a user count we don't need */ - id_us_min(&tmpmesh->id); - - ListBase disp = {NULL, NULL}; - /* TODO(sergey): This is gonna to work for until Depsgraph - * only contains for_render flag. As soon as CoW is - * implemented, this is to be rethought. - */ - BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp); - BKE_mesh_from_metaball(&disp, tmpmesh); - BKE_displist_free(&disp); - - BKE_mesh_texspace_copy_from_object(tmpmesh, m_object); - - return tmpmesh; -} - -void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh) -{ - BKE_id_free(m_bmain, mesh); -} - -bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) -{ - Object *basis_ob = BKE_mball_basis_find(scene, ob); - return ob == basis_ob; -} diff --git a/source/blender/alembic/intern/abc_writer_mball.h b/source/blender/alembic/intern/abc_writer_mball.h deleted file mode 100644 index c752472c86d..00000000000 --- a/source/blender/alembic/intern/abc_writer_mball.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_MBALL_H__ -#define __ABC_WRITER_MBALL_H__ - -#include "abc_writer_object.h" -#include "abc_writer_mesh.h" - -struct Main; -struct Object; - -/* AbcMBallWriter converts the metaballs to meshes at every frame, - * and defers to AbcGenericMeshWriter to perform the writing - * to the Alembic file. Only the basis balls are exported, as this - * results in the entire shape as one mesh. */ -class AbcMBallWriter : public AbcGenericMeshWriter { - Main *m_bmain; - - public: - explicit AbcMBallWriter(Main *bmain, - Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMBallWriter(); - - static bool isBasisBall(Scene *scene, Object *ob); - - protected: - Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; - void freeEvaluatedMesh(struct Mesh *mesh) override; - - private: - bool isAnimated() const override; -}; - -#endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_mesh.cc b/source/blender/alembic/intern/abc_writer_mesh.cc deleted file mode 100644 index b55d2473f99..00000000000 --- a/source/blender/alembic/intern/abc_writer_mesh.cc +++ /dev/null @@ -1,592 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_mesh.h" -#include "abc_writer_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_fluidsim_types.h" - -#include "BKE_animsys.h" -#include "BKE_key.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_modifier.h" - -#include "bmesh.h" -#include "bmesh_tools.h" - -#include "DEG_depsgraph_query.h" -} - -using Alembic::Abc::FloatArraySample; -using Alembic::Abc::Int32ArraySample; -using Alembic::Abc::V2fArraySample; -using Alembic::Abc::V3fArraySample; - -using Alembic::AbcGeom::kFacevaryingScope; -using Alembic::AbcGeom::OBoolProperty; -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::OFaceSet; -using Alembic::AbcGeom::OFaceSetSchema; -using Alembic::AbcGeom::ON3fGeomParam; -using Alembic::AbcGeom::OPolyMesh; -using Alembic::AbcGeom::OPolyMeshSchema; -using Alembic::AbcGeom::OSubD; -using Alembic::AbcGeom::OSubDSchema; -using Alembic::AbcGeom::OV2fGeomParam; -using Alembic::AbcGeom::UInt32ArraySample; - -/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ - -static void get_vertices(struct Mesh *mesh, std::vector &points) -{ - points.clear(); - points.resize(mesh->totvert); - - MVert *verts = mesh->mvert; - - for (int i = 0, e = mesh->totvert; i < e; i++) { - copy_yup_from_zup(points[i].getValue(), verts[i].co); - } -} - -static void get_topology(struct Mesh *mesh, - std::vector &poly_verts, - std::vector &loop_counts, - bool &r_has_flat_shaded_poly) -{ - const int num_poly = mesh->totpoly; - const int num_loops = mesh->totloop; - MLoop *mloop = mesh->mloop; - MPoly *mpoly = mesh->mpoly; - r_has_flat_shaded_poly = false; - - poly_verts.clear(); - loop_counts.clear(); - poly_verts.reserve(num_loops); - loop_counts.reserve(num_poly); - - /* NOTE: data needs to be written in the reverse order. */ - for (int i = 0; i < num_poly; i++) { - MPoly &poly = mpoly[i]; - loop_counts.push_back(poly.totloop); - - r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; - - MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); - - for (int j = 0; j < poly.totloop; j++, loop--) { - poly_verts.push_back(loop->v); - } - } -} - -static void get_creases(struct Mesh *mesh, - std::vector &indices, - std::vector &lengths, - std::vector &sharpnesses) -{ - const float factor = 1.0f / 255.0f; - - indices.clear(); - lengths.clear(); - sharpnesses.clear(); - - MEdge *edge = mesh->medge; - - for (int i = 0, e = mesh->totedge; i < e; i++) { - const float sharpness = static_cast(edge[i].crease) * factor; - - if (sharpness != 0.0f) { - indices.push_back(edge[i].v1); - indices.push_back(edge[i].v2); - sharpnesses.push_back(sharpness); - } - } - - lengths.resize(sharpnesses.size(), 2); -} - -static void get_loop_normals(struct Mesh *mesh, - std::vector &normals, - bool has_flat_shaded_poly) -{ - normals.clear(); - - /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export - * normals at all. This is also done by other software, see T71246. */ - if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL)) { - return; - } - - BKE_mesh_calc_normals_split(mesh); - const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); - BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); - - normals.resize(mesh->totloop); - - /* NOTE: data needs to be written in the reverse order. */ - int abc_index = 0; - MPoly *mp = mesh->mpoly; - for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { - for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { - int blender_index = mp->loopstart + j; - copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); - } - } -} - -/* *************** Modifiers *************** */ - -/* check if the mesh is a subsurf, ignoring disabled modifiers and - * displace if it's after subsurf. */ -static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = static_cast(ob->modifiers.last); - - for (; md; md = md->prev) { - if (!modifier_isEnabled(scene, md, eModifierMode_Render)) { - continue; - } - - if (md->type == eModifierType_Subsurf) { - SubsurfModifierData *smd = reinterpret_cast(md); - - if (smd->subdivType == ME_CC_SUBSURF) { - return md; - } - } - - /* mesh is not a subsurf. break */ - if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { - return NULL; - } - } - - return NULL; -} - -static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) -{ - ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim); - - if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) { - FluidsimModifierData *fsmd = reinterpret_cast(md); - - if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { - return md; - } - } - - return NULL; -} - -/* ************************************************************************** */ - -AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_is_animated = isAnimated(); - m_subsurf_mod = NULL; - m_is_subd = false; - - /* If the object is static, use the default static time sampling. */ - if (!m_is_animated) { - time_sampling = 0; - } - - if (!m_settings.apply_subdiv) { - m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object); - m_is_subd = (m_subsurf_mod != NULL); - } - - m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL); - - while (parent->alembicXform().getChildHeader(m_name)) { - m_name.append("_"); - } - - if (m_settings.use_subdiv_schema && m_is_subd) { - OSubD subd(parent->alembicXform(), m_name, m_time_sampling); - m_subdiv_schema = subd.getSchema(); - } - else { - OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); - m_mesh_schema = mesh.getSchema(); - - OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); - OBoolProperty type(typeContainer, "meshtype"); - type.set(m_is_subd); - } -} - -AbcGenericMeshWriter::~AbcGenericMeshWriter() -{ - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } -} - -bool AbcGenericMeshWriter::isAnimated() const -{ - if (BKE_animdata_id_is_animated(static_cast(m_object->data))) { - return true; - } - if (BKE_key_from_object(m_object) != NULL) { - return true; - } - - /* Test modifiers. */ - ModifierData *md = static_cast(m_object->modifiers.first); - while (md) { - - if (md->type != eModifierType_Subsurf) { - return true; - } - - md = md->next; - } - - return false; -} - -void AbcGenericMeshWriter::setIsAnimated(bool is_animated) -{ - m_is_animated = is_animated; -} - -void AbcGenericMeshWriter::do_write() -{ - /* We have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; - } - - bool needsfree; - struct Mesh *mesh = getFinalMesh(needsfree); - - try { - if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { - writeSubD(mesh); - } - else { - writeMesh(mesh); - } - - if (needsfree) { - freeEvaluatedMesh(mesh); - } - } - catch (...) { - if (needsfree) { - freeEvaluatedMesh(mesh); - } - throw; - } -} - -void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh) -{ - BKE_id_free(NULL, mesh); -} - -void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) -{ - std::vector points, normals; - std::vector poly_verts, loop_counts; - std::vector velocities; - bool has_flat_shaded_poly = false; - - get_vertices(mesh, points); - get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly); - - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_mesh_schema); - } - - m_mesh_sample = OPolyMeshSchema::Sample( - V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); - - UVSample sample; - if (m_first_frame && m_settings.export_uvs) { - const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); - - if (!sample.indices.empty() && !sample.uvs.empty()) { - OV2fGeomParam::Sample uv_sample; - uv_sample.setVals(V2fArraySample(sample.uvs)); - uv_sample.setIndices(UInt32ArraySample(sample.indices)); - uv_sample.setScope(kFacevaryingScope); - - m_mesh_schema.setUVSourceName(name); - m_mesh_sample.setUVs(uv_sample); - } - - write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); - } - - if (m_settings.export_normals) { - get_loop_normals(mesh, normals, has_flat_shaded_poly); - - ON3fGeomParam::Sample normals_sample; - if (!normals.empty()) { - normals_sample.setScope(kFacevaryingScope); - normals_sample.setVals(V3fArraySample(normals)); - } - - m_mesh_sample.setNormals(normals_sample); - } - - if (m_is_liquid) { - getVelocities(mesh, velocities); - m_mesh_sample.setVelocities(V3fArraySample(velocities)); - } - - m_mesh_sample.setSelfBounds(bounds()); - - m_mesh_schema.set(m_mesh_sample); - - writeArbGeoParams(mesh); -} - -void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) -{ - std::vector crease_sharpness; - std::vector points; - std::vector poly_verts, loop_counts; - std::vector crease_indices, crease_lengths; - bool has_flat_poly = false; - - get_vertices(mesh, points); - get_topology(mesh, poly_verts, loop_counts, has_flat_poly); - get_creases(mesh, crease_indices, crease_lengths, crease_sharpness); - - if (m_first_frame && m_settings.export_face_sets) { - writeFaceSets(mesh, m_subdiv_schema); - } - - m_subdiv_sample = OSubDSchema::Sample( - V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); - - UVSample sample; - if (m_first_frame && m_settings.export_uvs) { - const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); - - if (!sample.indices.empty() && !sample.uvs.empty()) { - OV2fGeomParam::Sample uv_sample; - uv_sample.setVals(V2fArraySample(sample.uvs)); - uv_sample.setIndices(UInt32ArraySample(sample.indices)); - uv_sample.setScope(kFacevaryingScope); - - m_subdiv_schema.setUVSourceName(name); - m_subdiv_sample.setUVs(uv_sample); - } - - write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); - } - - if (!crease_indices.empty()) { - m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); - m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); - m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); - } - - m_subdiv_sample.setSelfBounds(bounds()); - m_subdiv_schema.set(m_subdiv_sample); - - writeArbGeoParams(mesh); -} - -template void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema) -{ - std::map> geo_groups; - getGeoGroups(me, geo_groups); - - std::map>::iterator it; - for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { - OFaceSet face_set = schema.createFaceSet(it->first); - OFaceSetSchema::Sample samp; - samp.setFaces(Int32ArraySample(it->second)); - face_set.getSchema().set(samp); - } -} - -Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree) -{ - /* We don't want subdivided mesh data */ - if (m_subsurf_mod) { - m_subsurf_mod->mode |= eModifierMode_DisableTemporary; - } - - r_needsfree = false; - - Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph); - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree); - - if (m_subsurf_mod) { - m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; - } - - if (m_settings.triangulate) { - const bool tag_only = false; - const int quad_method = m_settings.quad_method; - const int ngon_method = m_settings.ngon_method; - - struct BMeshCreateParams bmcp = {false}; - struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; - BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); - - BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL); - - Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); - BM_mesh_free(bm); - - if (r_needsfree) { - BKE_id_free(NULL, mesh); - } - - mesh = result; - r_needsfree = true; - } - - m_custom_data_config.pack_uvs = m_settings.pack_uv; - m_custom_data_config.mpoly = mesh->mpoly; - m_custom_data_config.mloop = mesh->mloop; - m_custom_data_config.totpoly = mesh->totpoly; - m_custom_data_config.totloop = mesh->totloop; - m_custom_data_config.totvert = mesh->totvert; - - return mesh; -} - -void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me) -{ - if (m_is_liquid) { - /* We don't need anything more for liquid meshes. */ - return; - } - - if (m_first_frame && m_settings.export_vcols) { - if (m_subdiv_schema.valid()) { - write_custom_data( - m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } - else { - write_custom_data( - m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); - } - } -} - -void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector &vels) -{ - const int totverts = mesh->totvert; - - vels.clear(); - vels.resize(totverts); - - ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object); - FluidsimModifierData *fmd = reinterpret_cast(md); - FluidsimSettings *fss = fmd->fss; - - if (fss->meshVelocities) { - float *mesh_vels = reinterpret_cast(fss->meshVelocities); - - for (int i = 0; i < totverts; i++) { - copy_yup_from_zup(vels[i].getValue(), mesh_vels); - mesh_vels += 3; - } - } - else { - std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); - } -} - -void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, - std::map> &geo_groups) -{ - const int num_poly = mesh->totpoly; - MPoly *polygons = mesh->mpoly; - - for (int i = 0; i < num_poly; i++) { - MPoly ¤t_poly = polygons[i]; - short mnr = current_poly.mat_nr; - - Material *mat = BKE_object_material_get(m_object, mnr + 1); - - if (!mat) { - continue; - } - - std::string name = get_id_name(&mat->id); - - if (geo_groups.find(name) == geo_groups.end()) { - std::vector faceArray; - geo_groups[name] = faceArray; - } - - geo_groups[name].push_back(i); - } - - if (geo_groups.size() == 0) { - Material *mat = BKE_object_material_get(m_object, 1); - - std::string name = (mat) ? get_id_name(&mat->id) : "default"; - - std::vector faceArray; - - for (int i = 0, e = mesh->totface; i < e; i++) { - faceArray.push_back(i); - } - - geo_groups[name] = faceArray; - } -} - -AbcMeshWriter::AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcGenericMeshWriter(ob, parent, time_sampling, settings) -{ -} - -AbcMeshWriter::~AbcMeshWriter() -{ -} - -Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, - Object *ob_eval, - bool &UNUSED(r_needsfree)) -{ - return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); -} diff --git a/source/blender/alembic/intern/abc_writer_mesh.h b/source/blender/alembic/intern/abc_writer_mesh.h deleted file mode 100644 index 9152a370e4f..00000000000 --- a/source/blender/alembic/intern/abc_writer_mesh.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_MESH_H__ -#define __ABC_WRITER_MESH_H__ - -#include "abc_customdata.h" -#include "abc_writer_object.h" - -struct Mesh; -struct ModifierData; - -/* Writer for Alembic meshes. Does not assume the object is a mesh object. */ -class AbcGenericMeshWriter : public AbcObjectWriter { - protected: - Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; - Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; - - Alembic::AbcGeom::OSubDSchema m_subdiv_schema; - Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; - - Alembic::Abc::OArrayProperty m_mat_indices; - - bool m_is_animated; - ModifierData *m_subsurf_mod; - - CDStreamConfig m_custom_data_config; - - bool m_is_liquid; - bool m_is_subd; - - public: - AbcGenericMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcGenericMeshWriter(); - void setIsAnimated(bool is_animated); - - protected: - virtual void do_write(); - virtual bool isAnimated() const; - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0; - virtual void freeEvaluatedMesh(struct Mesh *mesh); - - Mesh *getFinalMesh(bool &r_needsfree); - - void writeMesh(struct Mesh *mesh); - void writeSubD(struct Mesh *mesh); - - void writeArbGeoParams(struct Mesh *mesh); - void getGeoGroups(struct Mesh *mesh, std::map> &geoGroups); - - /* fluid surfaces support */ - void getVelocities(struct Mesh *mesh, std::vector &vels); - - template void writeFaceSets(struct Mesh *mesh, Schema &schema); -}; - -class AbcMeshWriter : public AbcGenericMeshWriter { - public: - AbcMeshWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - ~AbcMeshWriter(); - - protected: - virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; -}; - -#endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_nurbs.cc b/source/blender/alembic/intern/abc_writer_nurbs.cc deleted file mode 100644 index 9796eaf54c3..00000000000 --- a/source/blender/alembic/intern/abc_writer_nurbs.cc +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_nurbs.h" -#include "abc_writer_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_curve_types.h" -#include "DNA_object_types.h" - -#include "BLI_listbase.h" - -#include "BKE_curve.h" -} - -using Alembic::AbcGeom::FloatArraySample; -using Alembic::AbcGeom::OBoolProperty; -using Alembic::AbcGeom::OCompoundProperty; -using Alembic::AbcGeom::ONuPatch; -using Alembic::AbcGeom::ONuPatchSchema; - -AbcNurbsWriter::AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_is_animated = isAnimated(); - - /* if the object is static, use the default static time sampling */ - if (!m_is_animated) { - m_time_sampling = 0; - } - - Curve *curve = static_cast(m_object->data); - size_t numNurbs = BLI_listbase_count(&curve->nurb); - - for (size_t i = 0; i < numNurbs; i++) { - std::stringstream str; - str << m_name << '_' << i; - - while (parent->alembicXform().getChildHeader(str.str())) { - str << "_"; - } - - ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); - m_nurbs_schema.push_back(nurbs.getSchema()); - } -} - -bool AbcNurbsWriter::isAnimated() const -{ - /* check if object has shape keys */ - Curve *cu = static_cast(m_object->data); - return (cu->key != NULL); -} - -static void get_knots(std::vector &knots, const int num_knots, float *nu_knots) -{ - if (num_knots <= 1) { - return; - } - - /* Add an extra knot at the beginning and end of the array since most apps - * require/expect them. */ - knots.reserve(num_knots + 2); - - knots.push_back(0.0f); - - for (int i = 0; i < num_knots; i++) { - knots.push_back(nu_knots[i]); - } - - knots[0] = 2.0f * knots[1] - knots[2]; - knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); -} - -void AbcNurbsWriter::do_write() -{ - /* we have already stored a sample for this object. */ - if (!m_first_frame && !m_is_animated) { - return; - } - - if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { - return; - } - - Curve *curve = static_cast(m_object->data); - ListBase *nulb; - - if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) { - nulb = &m_object->runtime.curve_cache->deformed_nurbs; - } - else { - nulb = BKE_curve_nurbs_get(curve); - } - - size_t count = 0; - for (Nurb *nu = static_cast(nulb->first); nu; nu = nu->next, count++) { - std::vector knotsU; - get_knots(knotsU, KNOTSU(nu), nu->knotsu); - - std::vector knotsV; - get_knots(knotsV, KNOTSV(nu), nu->knotsv); - - const int size = nu->pntsu * nu->pntsv; - std::vector positions(size); - std::vector weights(size); - - const BPoint *bp = nu->bp; - - for (int i = 0; i < size; i++, bp++) { - copy_yup_from_zup(positions[i].getValue(), bp->vec); - weights[i] = bp->vec[3]; - } - - ONuPatchSchema::Sample sample; - sample.setUOrder(nu->orderu + 1); - sample.setVOrder(nu->orderv + 1); - sample.setPositions(positions); - sample.setPositionWeights(weights); - sample.setUKnot(FloatArraySample(knotsU)); - sample.setVKnot(FloatArraySample(knotsV)); - sample.setNu(nu->pntsu); - sample.setNv(nu->pntsv); - - /* TODO(kevin): to accommodate other software we should duplicate control - * points to indicate that a NURBS is cyclic. */ - OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); - - if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { - OBoolProperty prop(user_props, "endpoint_u"); - prop.set(true); - } - - if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { - OBoolProperty prop(user_props, "endpoint_v"); - prop.set(true); - } - - if ((nu->flagu & CU_NURB_CYCLIC) != 0) { - OBoolProperty prop(user_props, "cyclic_u"); - prop.set(true); - } - - if ((nu->flagv & CU_NURB_CYCLIC) != 0) { - OBoolProperty prop(user_props, "cyclic_v"); - prop.set(true); - } - - m_nurbs_schema[count].set(sample); - } -} diff --git a/source/blender/alembic/intern/abc_writer_nurbs.h b/source/blender/alembic/intern/abc_writer_nurbs.h deleted file mode 100644 index c6a3c399b66..00000000000 --- a/source/blender/alembic/intern/abc_writer_nurbs.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_NURBS_H__ -#define __ABC_WRITER_NURBS_H__ - -#include "abc_writer_object.h" - -class AbcNurbsWriter : public AbcObjectWriter { - std::vector m_nurbs_schema; - bool m_is_animated; - - public: - AbcNurbsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings); - - private: - virtual void do_write(); - - bool isAnimated() const; -}; - -#endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_object.cc b/source/blender/alembic/intern/abc_writer_object.cc deleted file mode 100644 index 75dc93bd08e..00000000000 --- a/source/blender/alembic/intern/abc_writer_object.cc +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_object.h" - -extern "C" { -#include "DNA_object_types.h" - -#include "BKE_object.h" -} - -AbcObjectWriter::AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent) - : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) -{ - m_name = get_id_name(m_object) + "Shape"; - - if (parent) { - parent->addChild(this); - } -} - -AbcObjectWriter::~AbcObjectWriter() -{ -} - -void AbcObjectWriter::addChild(AbcObjectWriter *child) -{ - m_children.push_back(child); -} - -Imath::Box3d AbcObjectWriter::bounds() -{ - BoundBox *bb = BKE_object_boundbox_get(this->m_object); - - if (!bb) { - if (this->m_object->type != OB_CAMERA) { - ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; - } - - return Imath::Box3d(); - } - - /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ - this->m_bounds.min.x = bb->vec[0][0]; - this->m_bounds.min.y = bb->vec[0][2]; - this->m_bounds.min.z = -bb->vec[6][1]; - - this->m_bounds.max.x = bb->vec[6][0]; - this->m_bounds.max.y = bb->vec[6][2]; - this->m_bounds.max.z = -bb->vec[0][1]; - - return this->m_bounds; -} - -void AbcObjectWriter::write() -{ - do_write(); - m_first_frame = false; -} diff --git a/source/blender/alembic/intern/abc_writer_object.h b/source/blender/alembic/intern/abc_writer_object.h deleted file mode 100644 index c3511566372..00000000000 --- a/source/blender/alembic/intern/abc_writer_object.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_OBJECT_H__ -#define __ABC_WRITER_OBJECT_H__ - -#include -#include - -#include "abc_exporter.h" - -extern "C" { -#include "DNA_ID.h" -} - -class AbcTransformWriter; - -struct Main; -struct Object; - -class AbcObjectWriter { - protected: - Object *m_object; - ExportSettings &m_settings; - - uint32_t m_time_sampling; - - Imath::Box3d m_bounds; - std::vector m_children; - - std::vector> m_props; - - bool m_first_frame; - std::string m_name; - - public: - AbcObjectWriter(Object *ob, - uint32_t time_sampling, - ExportSettings &settings, - AbcObjectWriter *parent = NULL); - - virtual ~AbcObjectWriter(); - - void addChild(AbcObjectWriter *child); - - virtual Imath::Box3d bounds(); - - void write(); - - private: - virtual void do_write() = 0; -}; - -#endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_points.cc b/source/blender/alembic/intern/abc_writer_points.cc deleted file mode 100644 index cc4abe8ec4b..00000000000 --- a/source/blender/alembic/intern/abc_writer_points.cc +++ /dev/null @@ -1,123 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_points.h" -#include "abc_writer_mesh.h" -#include "abc_writer_transform.h" -#include "abc_util.h" - -extern "C" { -#include "DNA_object_types.h" -#include "DNA_particle_types.h" - -#include "BKE_lattice.h" -#include "BKE_particle.h" - -#include "BLI_math.h" - -#include "DEG_depsgraph_query.h" -} - -using Alembic::AbcGeom::kVertexScope; -using Alembic::AbcGeom::OPoints; -using Alembic::AbcGeom::OPointsSchema; - -/* ************************************************************************** */ - -AbcPointsWriter::AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys) - : AbcObjectWriter(ob, time_sampling, settings, parent) -{ - m_psys = psys; - - OPoints points(parent->alembicXform(), psys->name, m_time_sampling); - m_schema = points.getSchema(); -} - -void AbcPointsWriter::do_write() -{ - if (!m_psys) { - return; - } - - std::vector points; - std::vector velocities; - std::vector widths; - std::vector ids; - - ParticleKey state; - - ParticleSimulationData sim; - sim.depsgraph = m_settings.depsgraph; - sim.scene = m_settings.scene; - sim.ob = m_object; - sim.psys = m_psys; - - m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); - - uint64_t index = 0; - for (int p = 0; p < m_psys->totpart; p++) { - float pos[3], vel[3]; - - if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { - continue; - } - - state.time = DEG_get_ctime(m_settings.depsgraph); - - if (psys_get_particle_state(&sim, p, &state, 0) == 0) { - continue; - } - - /* location */ - mul_v3_m4v3(pos, m_object->imat, state.co); - - /* velocity */ - sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); - - /* Convert Z-up to Y-up. */ - points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); - velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); - widths.push_back(m_psys->particles[p].size); - ids.push_back(index++); - } - - if (m_psys->lattice_deform_data) { - end_latt_deform(m_psys->lattice_deform_data); - m_psys->lattice_deform_data = NULL; - } - - Alembic::Abc::P3fArraySample psample(points); - Alembic::Abc::UInt64ArraySample idsample(ids); - Alembic::Abc::V3fArraySample vsample(velocities); - Alembic::Abc::FloatArraySample wsample_array(widths); - Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); - - m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); - m_sample.setSelfBounds(bounds()); - - m_schema.set(m_sample); -} diff --git a/source/blender/alembic/intern/abc_writer_points.h b/source/blender/alembic/intern/abc_writer_points.h deleted file mode 100644 index 77dd10c4b26..00000000000 --- a/source/blender/alembic/intern/abc_writer_points.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2016 Kévin Dietrich. - * All rights reserved. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_POINTS_H__ -#define __ABC_WRITER_POINTS_H__ - -#include "abc_writer_object.h" -#include "abc_customdata.h" - -struct ParticleSystem; - -/* ************************************************************************** */ - -class AbcPointsWriter : public AbcObjectWriter { - Alembic::AbcGeom::OPointsSchema m_schema; - Alembic::AbcGeom::OPointsSchema::Sample m_sample; - ParticleSystem *m_psys; - - public: - AbcPointsWriter(Object *ob, - AbcTransformWriter *parent, - uint32_t time_sampling, - ExportSettings &settings, - ParticleSystem *psys); - - void do_write(); -}; - -#endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/alembic/intern/abc_writer_transform.cc b/source/blender/alembic/intern/abc_writer_transform.cc deleted file mode 100644 index d7bcc46d96f..00000000000 --- a/source/blender/alembic/intern/abc_writer_transform.cc +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "abc_writer_transform.h" -#include "abc_util.h" - -#include - -extern "C" { -#include "DNA_object_types.h" - -#include "BLI_math.h" - -#include "DEG_depsgraph_query.h" -} - -using Alembic::AbcGeom::OObject; -using Alembic::AbcGeom::OXform; - -AbcTransformWriter::AbcTransformWriter(Object *ob, - const OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings) - : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL) -{ - m_is_animated = hasAnimation(m_object); - - if (!m_is_animated) { - time_sampling = 0; - } - - m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); - m_schema = m_xform.getSchema(); - - /* Blender objects can't have a parent without inheriting the transform. */ - m_inherits_xform = parent != NULL; -} - -void AbcTransformWriter::do_write() -{ - Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); - - if (m_first_frame) { - m_visibility = Alembic::AbcGeom::CreateVisibilityProperty( - m_xform, m_xform.getSchema().getTimeSampling()); - } - - m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT)); - - if (!m_first_frame && !m_is_animated) { - return; - } - - float yup_mat[4][4]; - create_transform_matrix( - ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from); - - /* If the parent is a camera, undo its to-Maya rotation (see below). */ - bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr; - if (!is_root_object && ob_eval->parent->type == OB_CAMERA) { - float rot_mat[4][4]; - axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2); - mul_m4_m4m4(yup_mat, rot_mat, yup_mat); - } - - /* If the object is a camera, apply an extra rotation to Maya camera orientation. */ - if (ob_eval->type == OB_CAMERA) { - float rot_mat[4][4]; - axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); - mul_m4_m4m4(yup_mat, yup_mat, rot_mat); - } - - if (is_root_object) { - /* Only apply scaling to root objects, parenting will propagate it. */ - float scale_mat[4][4]; - scale_m4_fl(scale_mat, m_settings.global_scale); - scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ - mul_m4_m4m4(yup_mat, yup_mat, scale_mat); - yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */ - } - - m_matrix = convert_matrix_datatype(yup_mat); - m_sample.setMatrix(m_matrix); - m_sample.setInheritsXforms(m_inherits_xform); - m_schema.set(m_sample); -} - -Imath::Box3d AbcTransformWriter::bounds() -{ - Imath::Box3d bounds; - - for (int i = 0; i < m_children.size(); i++) { - Imath::Box3d box(m_children[i]->bounds()); - bounds.extendBy(box); - } - - return Imath::transform(bounds, m_matrix); -} - -bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const -{ - return true; -} diff --git a/source/blender/alembic/intern/abc_writer_transform.h b/source/blender/alembic/intern/abc_writer_transform.h deleted file mode 100644 index 4397b220761..00000000000 --- a/source/blender/alembic/intern/abc_writer_transform.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#ifndef __ABC_WRITER_TRANSFORM_H__ -#define __ABC_WRITER_TRANSFORM_H__ - -#include "abc_writer_object.h" - -#include - -class AbcTransformWriter : public AbcObjectWriter { - Alembic::AbcGeom::OXform m_xform; - Alembic::AbcGeom::OXformSchema m_schema; - Alembic::AbcGeom::XformSample m_sample; - Alembic::AbcGeom::OVisibilityProperty m_visibility; - Alembic::Abc::M44d m_matrix; - - bool m_is_animated; - bool m_inherits_xform; - - public: - Object *m_proxy_from; - - public: - AbcTransformWriter(Object *ob, - const Alembic::AbcGeom::OObject &abc_parent, - AbcTransformWriter *parent, - unsigned int time_sampling, - ExportSettings &settings); - - Alembic::AbcGeom::OXform &alembicXform() - { - return m_xform; - } - virtual Imath::Box3d bounds(); - - private: - virtual void do_write(); - - bool hasAnimation(Object *ob) const; -}; - -#endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/alembic/intern/alembic_capi.cc b/source/blender/alembic/intern/alembic_capi.cc deleted file mode 100644 index c6f9e284d53..00000000000 --- a/source/blender/alembic/intern/alembic_capi.cc +++ /dev/null @@ -1,1052 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup balembic - */ - -#include "../ABC_alembic.h" - -#include - -#include "abc_reader_archive.h" -#include "abc_reader_camera.h" -#include "abc_reader_curves.h" -#include "abc_reader_mesh.h" -#include "abc_reader_nurbs.h" -#include "abc_reader_points.h" -#include "abc_reader_transform.h" -#include "abc_util.h" -#include "abc_writer_camera.h" -#include "abc_writer_curves.h" -#include "abc_writer_hair.h" -#include "abc_writer_mesh.h" -#include "abc_writer_nurbs.h" -#include "abc_writer_points.h" -#include "abc_writer_transform.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_cachefile_types.h" -#include "DNA_curve_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "BKE_cachefile.h" -#include "BKE_context.h" -#include "BKE_curve.h" -#include "BKE_global.h" -#include "BKE_layer.h" -#include "BKE_lib_id.h" -#include "BKE_scene.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - -/* SpaceType struct has a member called 'new' which obviously conflicts with C++ - * so temporarily redefining the new keyword to make it compile. */ -#define new extern_new -#include "BKE_screen.h" -#undef new - -#include "BLI_fileops.h" -#include "BLI_ghash.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "WM_api.h" -#include "WM_types.h" -} - -using Alembic::Abc::Int32ArraySamplePtr; -using Alembic::Abc::ObjectHeader; - -using Alembic::AbcGeom::kWrapExisting; -using Alembic::AbcGeom::MetaData; -using Alembic::AbcGeom::P3fArraySamplePtr; - -using Alembic::AbcGeom::ICamera; -using Alembic::AbcGeom::ICompoundProperty; -using Alembic::AbcGeom::ICurves; -using Alembic::AbcGeom::ICurvesSchema; -using Alembic::AbcGeom::IFaceSet; -using Alembic::AbcGeom::ILight; -using Alembic::AbcGeom::IN3fArrayProperty; -using Alembic::AbcGeom::IN3fGeomParam; -using Alembic::AbcGeom::INuPatch; -using Alembic::AbcGeom::IObject; -using Alembic::AbcGeom::IPoints; -using Alembic::AbcGeom::IPointsSchema; -using Alembic::AbcGeom::IPolyMesh; -using Alembic::AbcGeom::IPolyMeshSchema; -using Alembic::AbcGeom::ISampleSelector; -using Alembic::AbcGeom::ISubD; -using Alembic::AbcGeom::IV2fGeomParam; -using Alembic::AbcGeom::IXform; -using Alembic::AbcGeom::IXformSchema; -using Alembic::AbcGeom::N3fArraySamplePtr; -using Alembic::AbcGeom::V3fArraySamplePtr; -using Alembic::AbcGeom::XformSample; - -using Alembic::AbcMaterial::IMaterial; - -struct AbcArchiveHandle { - int unused; -}; - -ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) -{ - return reinterpret_cast(handle); -} - -ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) -{ - return reinterpret_cast(archive); -} - -//#define USE_NURBS - -/* NOTE: this function is similar to visit_objects below, need to keep them in - * sync. */ -static bool gather_objects_paths(const IObject &object, ListBase *object_paths) -{ - if (!object.valid()) { - return false; - } - - size_t children_claiming_this_object = 0; - size_t num_children = object.getNumChildren(); - - for (size_t i = 0; i < num_children; i++) { - bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths); - children_claiming_this_object += child_claims_this_object ? 1 : 0; - } - - const MetaData &md = object.getMetaData(); - bool get_path = false; - bool parent_is_part_of_this_object = false; - - if (!object.getParent()) { - /* The root itself is not an object we should import. */ - } - else if (IXform::matches(md)) { - if (has_property(object.getProperties(), "locator")) { - get_path = true; - } - else { - get_path = children_claiming_this_object == 0; - } - - /* Transforms are never "data" for their parent. */ - parent_is_part_of_this_object = false; - } - else { - /* These types are "data" for their parent. */ - get_path = IPolyMesh::matches(md) || ISubD::matches(md) || -#ifdef USE_NURBS - INuPatch::matches(md) || -#endif - ICamera::matches(md) || IPoints::matches(md) || ICurves::matches(md); - parent_is_part_of_this_object = get_path; - } - - if (get_path) { - void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"); - AlembicObjectPath *abc_path = static_cast(abc_path_void); - - BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path)); - BLI_addtail(object_paths, abc_path); - } - - return parent_is_part_of_this_object; -} - -AbcArchiveHandle *ABC_create_handle(struct Main *bmain, - const char *filename, - ListBase *object_paths) -{ - ArchiveReader *archive = new ArchiveReader(bmain, filename); - - if (!archive->valid()) { - delete archive; - return NULL; - } - - if (object_paths) { - gather_objects_paths(archive->getTop(), object_paths); - } - - return handle_from_archive(archive); -} - -void ABC_free_handle(AbcArchiveHandle *handle) -{ - delete archive_from_handle(handle); -} - -int ABC_get_version() -{ - return ALEMBIC_LIBRARY_VERSION; -} - -static void find_iobject(const IObject &object, IObject &ret, const std::string &path) -{ - if (!object.valid()) { - return; - } - - std::vector tokens; - split(path, '/', tokens); - - IObject tmp = object; - - std::vector::iterator iter; - for (iter = tokens.begin(); iter != tokens.end(); ++iter) { - IObject child = tmp.getChild(*iter); - tmp = child; - } - - ret = tmp; -} - -struct ExportJobData { - ViewLayer *view_layer; - Main *bmain; - wmWindowManager *wm; - - char filename[1024]; - ExportSettings settings; - - short *stop; - short *do_update; - float *progress; - - bool was_canceled; - bool export_ok; -}; - -static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) -{ - ExportJobData *data = static_cast(customdata); - - data->stop = stop; - data->do_update = do_update; - data->progress = progress; - - /* XXX annoying hack: needed to prevent data corruption when changing - * scene frame in separate threads - */ - G.is_rendering = true; - WM_set_locked_interface(data->wm, true); - G.is_break = false; - - DEG_graph_build_from_view_layer( - data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer); - BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain); - - try { - AbcExporter exporter(data->bmain, data->filename, data->settings); - - Scene *scene = data->settings.scene; /* for the CFRA macro */ - const int orig_frame = CFRA; - - data->was_canceled = false; - exporter(do_update, progress, &data->was_canceled); - - if (CFRA != orig_frame) { - CFRA = orig_frame; - - BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain); - } - - data->export_ok = !data->was_canceled; - } - catch (const std::exception &e) { - ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; - } - catch (...) { - ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; - } -} - -static void export_endjob(void *customdata) -{ - ExportJobData *data = static_cast(customdata); - - DEG_graph_free(data->settings.depsgraph); - - if (data->was_canceled && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); - } - - std::string log = data->settings.logger.str(); - if (!log.empty()) { - std::cerr << log; - WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more..."); - } - - G.is_rendering = false; - WM_set_locked_interface(data->wm, false); -} - -bool ABC_export(Scene *scene, - bContext *C, - const char *filepath, - const struct AlembicExportParams *params, - bool as_background_job) -{ - ExportJobData *job = static_cast( - MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); - - job->view_layer = CTX_data_view_layer(C); - job->bmain = CTX_data_main(C); - job->wm = CTX_wm_manager(C); - job->export_ok = false; - BLI_strncpy(job->filename, filepath, 1024); - - /* Alright, alright, alright.... - * - * ExportJobData contains an ExportSettings containing a SimpleLogger. - * - * Since ExportJobData is a C-style struct dynamically allocated with - * MEM_mallocN (see above), its constructor is never called, therefore the - * ExportSettings constructor is not called which implies that the - * SimpleLogger one is not called either. SimpleLogger in turn does not call - * the constructor of its data members which ultimately means that its - * std::ostringstream member has a NULL pointer. To be able to properly use - * the stream's operator<<, the pointer needs to be set, therefore we have - * to properly construct everything. And this is done using the placement - * new operator as here below. It seems hackish, but I'm too lazy to - * do bigger refactor and maybe there is a better way which does not involve - * hardcore refactoring. */ - new (&job->settings) ExportSettings(); - job->settings.scene = scene; - job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER); - - /* TODO(Sybren): for now we only export the active scene layer. - * Later in the 2.8 development process this may be replaced by using - * a specific collection for Alembic I/O, which can then be toggled - * between "real" objects and cached Alembic files. */ - job->settings.view_layer = job->view_layer; - - job->settings.frame_start = params->frame_start; - job->settings.frame_end = params->frame_end; - job->settings.frame_samples_xform = params->frame_samples_xform; - job->settings.frame_samples_shape = params->frame_samples_shape; - job->settings.shutter_open = params->shutter_open; - job->settings.shutter_close = params->shutter_close; - - /* TODO(Sybren): For now this is ignored, until we can get selection - * detection working through Base pointers (instead of ob->flags). */ - job->settings.selected_only = params->selected_only; - - job->settings.export_face_sets = params->face_sets; - job->settings.export_normals = params->normals; - job->settings.export_uvs = params->uvs; - job->settings.export_vcols = params->vcolors; - job->settings.export_hair = params->export_hair; - job->settings.export_particles = params->export_particles; - job->settings.apply_subdiv = params->apply_subdiv; - job->settings.curves_as_mesh = params->curves_as_mesh; - job->settings.flatten_hierarchy = params->flatten_hierarchy; - - /* TODO(Sybren): visible_layer & renderable only is ignored for now, - * to be replaced with collections later in the 2.8 dev process - * (also see note above). */ - job->settings.visible_objects_only = params->visible_objects_only; - job->settings.renderable_only = params->renderable_only; - - job->settings.use_subdiv_schema = params->use_subdiv_schema; - job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA); - job->settings.pack_uv = params->packuv; - job->settings.global_scale = params->global_scale; - job->settings.triangulate = params->triangulate; - job->settings.quad_method = params->quad_method; - job->settings.ngon_method = params->ngon_method; - - if (job->settings.frame_start > job->settings.frame_end) { - std::swap(job->settings.frame_start, job->settings.frame_end); - } - - bool export_ok = false; - if (as_background_job) { - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->settings.scene, - "Alembic Export", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); - - /* setup job */ - WM_jobs_customdata_set(wm_job, job, MEM_freeN); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); - - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ - short stop = 0, do_update = 0; - float progress = 0.f; - - export_startjob(job, &stop, &do_update, &progress); - export_endjob(job); - export_ok = job->export_ok; - - MEM_freeN(job); - } - - return export_ok; -} - -/* ********************** Import file ********************** */ - -/** - * Generates an AbcObjectReader for this Alembic object and its children. - * - * \param object: The Alembic IObject to visit. - * \param readers: The created AbcObjectReader * will be appended to this vector. - * \param settings: Import settings, not used directly but passed to the - * AbcObjectReader subclass constructors. - * \param r_assign_as_parent: Return parameter, contains a list of reader - * pointers, whose parent pointer should still be set. - * This is filled when this call to visit_object() didn't create - * a reader that should be the parent. - * \return A pair of boolean and reader pointer. The boolean indicates whether - * this IObject claims its parent as part of the same object - * (for example an IPolyMesh object would claim its parent, as the mesh - * is interpreted as the object's data, and the parent IXform as its - * Blender object). The pointer is the AbcObjectReader that represents - * the IObject parameter. - * - * NOTE: this function is similar to gather_object_paths above, need to keep - * them in sync. */ -static std::pair visit_object( - const IObject &object, - AbcObjectReader::ptr_vector &readers, - ImportSettings &settings, - AbcObjectReader::ptr_vector &r_assign_as_parent) -{ - const std::string &full_name = object.getFullName(); - - if (!object.valid()) { - std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n"; - return std::make_pair(false, static_cast(NULL)); - } - - /* The interpretation of data by the children determine the role of this - * object. This is especially important for Xform objects, as they can be - * either part of a Blender object or a Blender object (Empty) themselves. - */ - size_t children_claiming_this_object = 0; - size_t num_children = object.getNumChildren(); - AbcObjectReader::ptr_vector claiming_child_readers; - AbcObjectReader::ptr_vector nonclaiming_child_readers; - AbcObjectReader::ptr_vector assign_as_parent; - for (size_t i = 0; i < num_children; i++) { - const IObject ichild = object.getChild(i); - - /* TODO: When we only support C++11, use std::tie() instead. */ - std::pair child_result; - child_result = visit_object(ichild, readers, settings, assign_as_parent); - - bool child_claims_this_object = child_result.first; - AbcObjectReader *child_reader = child_result.second; - - if (child_reader == NULL) { - BLI_assert(!child_claims_this_object); - } - else { - if (child_claims_this_object) { - claiming_child_readers.push_back(child_reader); - } - else { - nonclaiming_child_readers.push_back(child_reader); - } - } - - children_claiming_this_object += child_claims_this_object ? 1 : 0; - } - BLI_assert(children_claiming_this_object == claiming_child_readers.size()); - - AbcObjectReader *reader = NULL; - const MetaData &md = object.getMetaData(); - bool parent_is_part_of_this_object = false; - - if (!object.getParent()) { - /* The root itself is not an object we should import. */ - } - else if (IXform::matches(md)) { - bool create_empty; - - /* An xform can either be a Blender Object (if it contains a mesh, for - * example), but it can also be an Empty. Its correct translation to - * Blender's data model depends on its children. */ - - /* Check whether or not this object is a Maya locator, which is - * similar to empties used as parent object in Blender. */ - if (has_property(object.getProperties(), "locator")) { - create_empty = true; - } - else { - create_empty = claiming_child_readers.empty(); - } - - if (create_empty) { - reader = new AbcEmptyReader(object, settings); - } - } - else if (IPolyMesh::matches(md)) { - reader = new AbcMeshReader(object, settings); - parent_is_part_of_this_object = true; - } - else if (ISubD::matches(md)) { - reader = new AbcSubDReader(object, settings); - parent_is_part_of_this_object = true; - } - else if (INuPatch::matches(md)) { -#ifdef USE_NURBS - /* TODO(kevin): importing cyclic NURBS from other software crashes - * at the moment. This is due to the fact that NURBS in other - * software have duplicated points which causes buffer overflows in - * Blender. Need to figure out exactly how these points are - * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). - * Until this is fixed, disabling NURBS reading. */ - reader = new AbcNurbsReader(object, settings); - parent_is_part_of_this_object = true; -#endif - } - else if (ICamera::matches(md)) { - reader = new AbcCameraReader(object, settings); - parent_is_part_of_this_object = true; - } - else if (IPoints::matches(md)) { - reader = new AbcPointsReader(object, settings); - parent_is_part_of_this_object = true; - } - else if (IMaterial::matches(md)) { - /* Pass for now. */ - } - else if (ILight::matches(md)) { - /* Pass for now. */ - } - else if (IFaceSet::matches(md)) { - /* Pass, those are handled in the mesh reader. */ - } - else if (ICurves::matches(md)) { - reader = new AbcCurveReader(object, settings); - parent_is_part_of_this_object = true; - } - else { - std::cerr << "Alembic object " << full_name << " is of unsupported schema type '" - << object.getMetaData().get("schemaObjTitle") << "'" << std::endl; - } - - if (reader) { - /* We have created a reader, which should imply that this object is - * not claimed as part of any child Alembic object. */ - BLI_assert(claiming_child_readers.empty()); - - readers.push_back(reader); - reader->incref(); - - AlembicObjectPath *abc_path = static_cast( - MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); - BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path)); - BLI_addtail(&settings.cache_file->object_paths, abc_path); - - /* We can now assign this reader as parent for our children. */ - if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) { - for (AbcObjectReader *child_reader : nonclaiming_child_readers) { - child_reader->parent_reader = reader; - } - for (AbcObjectReader *child_reader : assign_as_parent) { - child_reader->parent_reader = reader; - } - } - } - else if (object.getParent()) { - if (claiming_child_readers.size() > 0) { - /* The first claiming child will serve just fine as parent to - * our non-claiming children. Since all claiming children share - * the same XForm, it doesn't really matter which one we pick. */ - AbcObjectReader *claiming_child = claiming_child_readers[0]; - for (AbcObjectReader *child_reader : nonclaiming_child_readers) { - child_reader->parent_reader = claiming_child; - } - for (AbcObjectReader *child_reader : assign_as_parent) { - child_reader->parent_reader = claiming_child; - } - /* Claiming children should have our parent set as their parent. */ - for (AbcObjectReader *child_reader : claiming_child_readers) { - r_assign_as_parent.push_back(child_reader); - } - } - else { - /* This object isn't claimed by any child, and didn't produce - * a reader. Odd situation, could be the top Alembic object, or - * an unsupported Alembic schema. Delegate to our parent. */ - for (AbcObjectReader *child_reader : claiming_child_readers) { - r_assign_as_parent.push_back(child_reader); - } - for (AbcObjectReader *child_reader : nonclaiming_child_readers) { - r_assign_as_parent.push_back(child_reader); - } - for (AbcObjectReader *child_reader : assign_as_parent) { - r_assign_as_parent.push_back(child_reader); - } - } - } - - return std::make_pair(parent_is_part_of_this_object, reader); -} - -enum { - ABC_NO_ERROR = 0, - ABC_ARCHIVE_FAIL, - ABC_UNSUPPORTED_HDF5, -}; - -struct ImportJobData { - Main *bmain; - Scene *scene; - ViewLayer *view_layer; - wmWindowManager *wm; - - char filename[1024]; - ImportSettings settings; - - ArchiveReader *archive; - std::vector readers; - - short *stop; - short *do_update; - float *progress; - - char error_code; - bool was_cancelled; - bool import_ok; -}; - -static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) -{ - SCOPE_TIMER("Alembic import, objects reading and creation"); - - ImportJobData *data = static_cast(user_data); - - data->stop = stop; - data->do_update = do_update; - data->progress = progress; - - WM_set_locked_interface(data->wm, true); - - ArchiveReader *archive = new ArchiveReader(data->bmain, data->filename); - - if (!archive->valid()) { -#ifndef WITH_ALEMBIC_HDF5 - data->error_code = archive->is_hdf5() ? ABC_UNSUPPORTED_HDF5 : ABC_ARCHIVE_FAIL; -#else - data->error_code = ABC_ARCHIVE_FAIL; -#endif - delete archive; - return; - } - - CacheFile *cache_file = static_cast( - BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); - - /* Decrement the ID ref-count because it is going to be incremented for each - * modifier and constraint that it will be attached to, so since currently - * it is not used by anyone, its use count will off by one. */ - id_us_min(&cache_file->id); - - cache_file->is_sequence = data->settings.is_sequence; - cache_file->scale = data->settings.scale; - STRNCPY(cache_file->filepath, data->filename); - - data->archive = archive; - data->settings.cache_file = cache_file; - - *data->do_update = true; - *data->progress = 0.05f; - - /* Parse Alembic Archive. */ - AbcObjectReader::ptr_vector assign_as_parent; - visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent); - - /* There shouldn't be any orphans. */ - BLI_assert(assign_as_parent.size() == 0); - - if (G.is_break) { - data->was_cancelled = true; - return; - } - - *data->do_update = true; - *data->progress = 0.1f; - - /* Create objects and set scene frame range. */ - - const float size = static_cast(data->readers.size()); - size_t i = 0; - - chrono_t min_time = std::numeric_limits::max(); - chrono_t max_time = std::numeric_limits::min(); - - ISampleSelector sample_sel(0.0f); - std::vector::iterator iter; - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - AbcObjectReader *reader = *iter; - - if (reader->valid()) { - reader->readObjectData(data->bmain, sample_sel); - - min_time = std::min(min_time, reader->minTime()); - max_time = std::max(max_time, reader->maxTime()); - } - else { - std::cerr << "Object " << reader->name() << " in Alembic file " << data->filename - << " is invalid.\n"; - } - - *data->progress = 0.1f + 0.3f * (++i / size); - *data->do_update = true; - - if (G.is_break) { - data->was_cancelled = true; - return; - } - } - - if (data->settings.set_frame_range) { - Scene *scene = data->scene; - - if (data->settings.is_sequence) { - SFRA = data->settings.sequence_offset; - EFRA = SFRA + (data->settings.sequence_len - 1); - CFRA = SFRA; - } - else if (min_time < max_time) { - SFRA = static_cast(round(min_time * FPS)); - EFRA = static_cast(round(max_time * FPS)); - CFRA = SFRA; - } - } - - /* Setup parenthood. */ - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - const AbcObjectReader *reader = *iter; - const AbcObjectReader *parent_reader = reader->parent_reader; - Object *ob = reader->object(); - - if (parent_reader == NULL || !reader->inherits_xform()) { - ob->parent = NULL; - } - else { - ob->parent = parent_reader->object(); - } - } - - /* Setup transformations and constraints. */ - i = 0; - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - AbcObjectReader *reader = *iter; - reader->setupObjectTransform(0.0f); - - *data->progress = 0.7f + 0.3f * (++i / size); - *data->do_update = true; - - if (G.is_break) { - data->was_cancelled = true; - return; - } - } -} - -static void import_endjob(void *user_data) -{ - SCOPE_TIMER("Alembic import, cleanup"); - - ImportJobData *data = static_cast(user_data); - - std::vector::iterator iter; - - /* Delete objects on cancellation. */ - if (data->was_cancelled) { - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - Object *ob = (*iter)->object(); - - /* It's possible that cancellation occurred between the creation of - * the reader and the creation of the Blender object. */ - if (ob == NULL) { - continue; - } - - BKE_id_free_us(data->bmain, ob); - } - } - else { - /* Add object to scene. */ - Base *base; - LayerCollection *lc; - ViewLayer *view_layer = data->view_layer; - - BKE_view_layer_base_deselect_all(view_layer); - - lc = BKE_layer_collection_get_active(view_layer); - - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - Object *ob = (*iter)->object(); - - BKE_collection_object_add(data->bmain, lc->collection, ob); - - base = BKE_view_layer_base_find(view_layer, ob); - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); - - DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); - DEG_id_tag_update_ex(data->bmain, - &ob->id, - ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | - ID_RECALC_BASE_FLAGS); - } - - DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS); - DEG_relations_tag_update(data->bmain); - } - - for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { - AbcObjectReader *reader = *iter; - reader->decref(); - - if (reader->refcount() == 0) { - delete reader; - } - } - - WM_set_locked_interface(data->wm, false); - - switch (data->error_code) { - default: - case ABC_NO_ERROR: - data->import_ok = !data->was_cancelled; - break; - case ABC_ARCHIVE_FAIL: - WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); - break; - case ABC_UNSUPPORTED_HDF5: - WM_report(RPT_ERROR, "Alembic archive in obsolete HDF5 format is not supported."); - break; - } - - WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); -} - -static void import_freejob(void *user_data) -{ - ImportJobData *data = static_cast(user_data); - delete data->archive; - delete data; -} - -bool ABC_import(bContext *C, - const char *filepath, - float scale, - bool is_sequence, - bool set_frame_range, - int sequence_len, - int offset, - bool validate_meshes, - bool as_background_job) -{ - /* Using new here since MEM_* funcs do not call ctor to properly initialize - * data. */ - ImportJobData *job = new ImportJobData(); - job->bmain = CTX_data_main(C); - job->scene = CTX_data_scene(C); - job->view_layer = CTX_data_view_layer(C); - job->wm = CTX_wm_manager(C); - job->import_ok = false; - BLI_strncpy(job->filename, filepath, 1024); - - job->settings.scale = scale; - job->settings.is_sequence = is_sequence; - job->settings.set_frame_range = set_frame_range; - job->settings.sequence_len = sequence_len; - job->settings.sequence_offset = offset; - job->settings.validate_meshes = validate_meshes; - job->error_code = ABC_NO_ERROR; - job->was_cancelled = false; - job->archive = NULL; - - G.is_break = false; - - bool import_ok = false; - if (as_background_job) { - wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), - CTX_wm_window(C), - job->scene, - "Alembic Import", - WM_JOB_PROGRESS, - WM_JOB_TYPE_ALEMBIC); - - /* setup job */ - WM_jobs_customdata_set(wm_job, job, import_freejob); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); - - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - /* Fake a job context, so that we don't need NULL pointer checks while importing. */ - short stop = 0, do_update = 0; - float progress = 0.f; - - import_startjob(job, &stop, &do_update, &progress); - import_endjob(job); - import_ok = job->import_ok; - - import_freejob(job); - } - - return import_ok; -} - -/* ************************************************************************** */ - -void ABC_get_transform(CacheReader *reader, float r_mat[4][4], float time, float scale) -{ - if (!reader) { - return; - } - - AbcObjectReader *abc_reader = reinterpret_cast(reader); - - bool is_constant = false; - abc_reader->read_matrix(r_mat, time, scale, is_constant); -} - -/* ************************************************************************** */ - -static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **err_str) -{ - AbcObjectReader *abc_reader = reinterpret_cast(reader); - IObject iobject = abc_reader->iobject(); - - if (!iobject.valid()) { - *err_str = "Invalid object: verify object path"; - return NULL; - } - - const ObjectHeader &header = iobject.getHeader(); - if (!abc_reader->accepts_object_type(header, ob, err_str)) { - /* err_str is set by acceptsObjectType() */ - return NULL; - } - - return abc_reader; -} - -static ISampleSelector sample_selector_for_time(float time) -{ - /* kFloorIndex is used to be compatible with non-interpolating - * properties; they use the floor. */ - return ISampleSelector(time, ISampleSelector::kFloorIndex); -} - -Mesh *ABC_read_mesh(CacheReader *reader, - Object *ob, - Mesh *existing_mesh, - const float time, - const char **err_str, - int read_flag) -{ - AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); - if (abc_reader == NULL) { - return NULL; - } - - ISampleSelector sample_sel = sample_selector_for_time(time); - return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str); -} - -bool ABC_mesh_topology_changed( - CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) -{ - AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); - if (abc_reader == NULL) { - return false; - } - - ISampleSelector sample_sel = sample_selector_for_time(time); - return abc_reader->topology_changed(existing_mesh, sample_sel); -} - -/* ************************************************************************** */ - -void CacheReader_free(CacheReader *reader) -{ - AbcObjectReader *abc_reader = reinterpret_cast(reader); - abc_reader->decref(); - - if (abc_reader->refcount() == 0) { - delete abc_reader; - } -} - -void CacheReader_incref(CacheReader *reader) -{ - AbcObjectReader *abc_reader = reinterpret_cast(reader); - abc_reader->incref(); -} - -CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, - CacheReader *reader, - Object *object, - const char *object_path) -{ - if (object_path[0] == '\0') { - return reader; - } - - ArchiveReader *archive = archive_from_handle(handle); - - if (!archive || !archive->valid()) { - return reader; - } - - IObject iobject; - find_iobject(archive->getTop(), iobject, object_path); - - if (reader) { - CacheReader_free(reader); - } - - ImportSettings settings; - AbcObjectReader *abc_reader = create_reader(iobject, settings); - if (abc_reader == NULL) { - /* This object is not supported */ - return NULL; - } - abc_reader->object(object); - abc_reader->incref(); - - return reinterpret_cast(abc_reader); -} diff --git a/source/blender/avi/AVI_avi.h b/source/blender/avi/AVI_avi.h deleted file mode 100644 index 4f3aa720da3..00000000000 --- a/source/blender/avi/AVI_avi.h +++ /dev/null @@ -1,299 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * \section avi_about About the AVI module - * - * This is external code. It provides avi file import/export and - * conversions. It has been adapted to make use of Blender memory - * management functions, and because of this it needs module - * blenlib. You need to provide this lib when linking with libavi.a . - * - * \subsection avi_issues Known issues with AVI - * - * - avi uses #MEM_mallocN, #MEM_freeN from blenlib. - * - Not all functions that are used externally are properly - * prototyped. - * - * This header has not been split, since it interleaves type defines - * and functions. You would need the types to be able to include the - * function headers anyway. And, after all, it is someone else's - * code. So we keep it like this. - */ - -#ifndef __AVI_AVI_H__ -#define __AVI_AVI_H__ - -#include "BLI_sys_types.h" -#include /* for FILE */ - -typedef struct _AviChunk { - int fcc; - int size; -} AviChunk; - -typedef struct _AviList { - int fcc; - int size; - int ids; -} AviList; - -typedef struct _AviMainHeader { - int fcc; - int size; - int MicroSecPerFrame; /* MicroSecPerFrame - timing between frames */ - int MaxBytesPerSec; /* MaxBytesPerSec - approx bps system must handle */ - int PaddingGranularity; - int Flags; - - /** had idx1 chunk */ -#define AVIF_HASINDEX 0x00000010 - /** must use idx1 chunk to determine order */ -#define AVIF_MUSTUSEINDEX 0x00000020 - /** AVI file is interleaved */ -#define AVIF_ISINTERLEAVED 0x00000100 -#define AVIF_TRUSTCKTYPE 0x00000800 - /** specially allocated used for capturing real time video */ -#define AVIF_WASCAPTUREFILE 0x00010000 - /** contains copyrighted data */ -#define AVIF_COPYRIGHTED 0x00020000 - - int TotalFrames; - int InitialFrames; /* InitialFrames - initial frame before interleaving */ - int Streams; - int SuggestedBufferSize; - int Width; - int Height; - int Reserved[4]; -} AviMainHeader; - -typedef struct _AviStreamHeader { - int fcc; - int size; - int Type; -#define AVIST_VIDEO FCC("vids") -#define AVIST_AUDIO FCC("auds") -#define AVIST_MIDI FCC("mids") -#define AVIST_TEXT FCC("txts") - - int Handler; - int Flags; -#define AVISF_DISABLED 0x00000001 -#define AVISF_VIDEO_PALCHANGES 0x00010000 - - short Priority; - short Language; - int InitialFrames; - int Scale; - int Rate; - int Start; - int Length; - int SuggestedBufferSize; - int Quality; - int SampleSize; - short left; - short top; - short right; - short bottom; -} AviStreamHeader; - -typedef struct _AviBitmapInfoHeader { - int fcc; - int size; - int Size; - int Width; - int Height; - short Planes; - short BitCount; - int Compression; - int SizeImage; - int XPelsPerMeter; - int YPelsPerMeter; - int ClrUsed; - int ClrImportant; -} AviBitmapInfoHeader; - -typedef struct _AviMJPEGUnknown { - int a; - int b; - int c; - int d; - int e; - int f; - int g; -} AviMJPEGUnknown; - -typedef struct _AviIndexEntry { - int ChunkId; - int Flags; -#define AVIIF_LIST 0x00000001 -#define AVIIF_KEYFRAME 0x00000010 -#define AVIIF_NO_TIME 0x00000100 -#define AVIIF_COMPRESSOR 0x0FFF0000 - int Offset; - int Size; -} AviIndexEntry; - -typedef struct _AviIndex { - int fcc; - int size; - AviIndexEntry *entrys; -} AviIndex; - -typedef enum { - /** The most basic of forms, 3 bytes per pixel, 1 per r, g, b. */ - AVI_FORMAT_RGB24, - /** The second most basic of forms, 4 bytes per pixel, 1 per r, g, b, alpha. */ - AVI_FORMAT_RGB32, - /** Same as above, but is in the weird AVI order (bottom to top, left to right). */ - AVI_FORMAT_AVI_RGB, - /** Motion-JPEG. */ - AVI_FORMAT_MJPEG, -} AviFormat; - -typedef struct _AviStreamRec { - AviStreamHeader sh; - void *sf; - int sf_size; - AviFormat format; -} AviStreamRec; - -typedef struct _AviMovie { - FILE *fp; - - int type; -#define AVI_MOVIE_READ 0 -#define AVI_MOVIE_WRITE 1 - - int64_t size; - - AviMainHeader *header; - AviStreamRec *streams; - AviIndexEntry *entries; - int index_entries; - - int64_t movi_offset; - int64_t read_offset; - int64_t *offset_table; - - /* Local data goes here */ - int interlace; - int odd_fields; -} AviMovie; - -typedef enum { - AVI_ERROR_NONE = 0, - AVI_ERROR_COMPRESSION, - AVI_ERROR_OPEN, - AVI_ERROR_READING, - AVI_ERROR_WRITING, - AVI_ERROR_FORMAT, - AVI_ERROR_ALLOC, - AVI_ERROR_FOUND, - AVI_ERROR_OPTION, -} AviError; - -/* belongs to the option-setting function. */ -typedef enum { - AVI_OPTION_WIDTH = 0, - AVI_OPTION_HEIGHT, - AVI_OPTION_QUALITY, - AVI_OPTION_FRAMERATE, -} AviOption; - -/* The offsets that will always stay the same in AVI files we - * write... used to seek around to the places where we need to write - * the sizes */ - -#define AVI_RIFF_SOFF 4L -#define AVI_HDRL_SOFF 16L - -/** - * This is a sort of MAKE_ID thing. Used in imbuf :( It is used - * through options in the AVI header (AviStreamHeader). */ -#define FCC(ch4) (ch4[0] | ch4[1] << 8 | ch4[2] << 16 | ch4[3] << 24) - -/** - * Test whether this is an avi-format. - */ -bool AVI_is_avi(const char *name); - -/** - * Open a compressed file, decompress it into memory. - */ -AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...); - -/** - * Finalize a compressed output stream. - */ -AviError AVI_close_compress(AviMovie *movie); - -/** - * Choose a compression option for \. Possible options are - * AVI_OPTION_TYPE_MAIN, AVI_OPTION_TYPE_STRH, AVI_OPTION_TYPE_STRF - */ -AviError AVI_set_compress_option( - AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data); -/* Hmmm... there should be some explanation about what these mean */ -/** - * Compression option, for use in avi_set_compress_option - */ -#define AVI_OPTION_TYPE_MAIN 0 -/** - * Compression option, for use in avi_set_compress_option - */ -#define AVI_OPTION_TYPE_STRH 1 -/** - * Compression option, for use in avi_set_compress_option - */ -#define AVI_OPTION_TYPE_STRF 2 - -/** - * Direct the streams \ to \. Redirect \ - * streams. - */ -int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num); - -/** - * Open a movie stream from file. - */ -AviError AVI_open_movie(const char *name, AviMovie *movie); - -/** - * Read a frame from a movie stream. - */ -void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream); -/** - * Close an open movie stream. - */ -AviError AVI_close(AviMovie *movie); - -/** - * Write frames to a movie stream. - */ -AviError AVI_write_frame(AviMovie *movie, int frame_num, ...); - -/** - * Unused but still external - */ -AviError AVI_print_error(AviError error); - -#endif /* __AVI_AVI_H__ */ diff --git a/source/blender/avi/CMakeLists.txt b/source/blender/avi/CMakeLists.txt deleted file mode 100644 index eafb299944d..00000000000 --- a/source/blender/avi/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The Original Code is Copyright (C) 2006, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -set(INC - . - ../blenlib - ../imbuf - ../../../intern/guardedalloc -) - -set(INC_SYS - ${JPEG_INCLUDE_DIR} -) - -set(SRC - intern/avi.c - intern/avi_codecs.c - intern/avi_endian.c - intern/avi_mjpeg.c - intern/avi_options.c - intern/avi_rgb.c - intern/avi_rgb32.c - - AVI_avi.h - intern/avi_endian.h - intern/avi_intern.h - intern/avi_mjpeg.h - intern/avi_rgb.h - intern/avi_rgb32.h -) - -set(LIB - ${JPEG_LIBRARIES} -) - -blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/avi/intern/avi.c b/source/blender/avi/intern/avi.c deleted file mode 100644 index 22eb0be0cc0..00000000000 --- a/source/blender/avi/intern/avi.c +++ /dev/null @@ -1,1056 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. - */ - -#include -#include -#include -#include -#include - -#ifdef WIN32 -# include "BLI_winstuff.h" -#endif - -#include "MEM_guardedalloc.h" - -#include "BLI_sys_types.h" -#include "BLI_utildefines.h" -#include "BLI_fileops.h" - -#include "AVI_avi.h" -#include "avi_intern.h" - -#include "avi_endian.h" - -static int AVI_DEBUG = 0; -static char DEBUG_FCC[4]; - -#define DEBUG_PRINT(x) \ - if (AVI_DEBUG) { \ - printf("AVI DEBUG: " x); \ - } \ - (void)0 - -/* local functions */ -char *fcc_to_char(unsigned int fcc); -char *tcc_to_char(unsigned int tcc); - -/* implementation */ - -unsigned int GET_FCC(FILE *fp) -{ - unsigned char tmp[4]; - - tmp[0] = getc(fp); - tmp[1] = getc(fp); - tmp[2] = getc(fp); - tmp[3] = getc(fp); - - return FCC(tmp); -} - -unsigned int GET_TCC(FILE *fp) -{ - char tmp[5]; - - tmp[0] = getc(fp); - tmp[1] = getc(fp); - tmp[2] = 0; - tmp[3] = 0; - - return FCC(tmp); -} - -char *fcc_to_char(unsigned int fcc) -{ - DEBUG_FCC[0] = (fcc)&127; - DEBUG_FCC[1] = (fcc >> 8) & 127; - DEBUG_FCC[2] = (fcc >> 16) & 127; - DEBUG_FCC[3] = (fcc >> 24) & 127; - - return DEBUG_FCC; -} - -char *tcc_to_char(unsigned int tcc) -{ - DEBUG_FCC[0] = (tcc)&127; - DEBUG_FCC[1] = (tcc >> 8) & 127; - DEBUG_FCC[2] = 0; - DEBUG_FCC[3] = 0; - - return DEBUG_FCC; -} - -int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num) -{ - int cur_stream; - - if (movie == NULL) { - return -AVI_ERROR_OPTION; - } - - for (cur_stream = 0; cur_stream < movie->header->Streams; cur_stream++) { - if (movie->streams[cur_stream].sh.Type == avist_type) { - if (stream_num == 0) { - return cur_stream; - } - else { - stream_num--; - } - } - } - - return -AVI_ERROR_FOUND; -} - -static int fcc_get_stream(int fcc) -{ - char fccs[4]; - - fccs[0] = fcc; - fccs[1] = fcc >> 8; - fccs[2] = fcc >> 16; - fccs[3] = fcc >> 24; - - return 10 * (fccs[0] - '0') + (fccs[1] - '0'); -} - -static bool fcc_is_data(int fcc) -{ - char fccs[4]; - - fccs[0] = fcc; - fccs[1] = fcc >> 8; - fccs[2] = fcc >> 16; - fccs[3] = fcc >> 24; - - if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w')) { - return 0; - } - if (fccs[3] != 'b' && fccs[3] != 'c') { - return 0; - } - - return 1; -} - -AviError AVI_print_error(AviError in_error) -{ - int error; - - if ((int)in_error < 0) { - error = -in_error; - } - else { - error = in_error; - } - - switch (error) { - case AVI_ERROR_NONE: - break; - case AVI_ERROR_COMPRESSION: - printf("AVI ERROR: compressed in an unsupported format\n"); - break; - case AVI_ERROR_OPEN: - printf("AVI ERROR: could not open file\n"); - break; - case AVI_ERROR_READING: - printf("AVI ERROR: could not read from file\n"); - break; - case AVI_ERROR_WRITING: - printf("AVI ERROR: could not write to file\n"); - break; - case AVI_ERROR_FORMAT: - printf("AVI ERROR: file is in an illegal or unrecognized format\n"); - break; - case AVI_ERROR_ALLOC: - printf("AVI ERROR: error encountered while allocating memory\n"); - break; - case AVI_ERROR_OPTION: - printf("AVI ERROR: program made illegal request\n"); - break; - case AVI_ERROR_FOUND: - printf("AVI ERROR: movie did not contain expected item\n"); - break; - default: - break; - } - - return in_error; -} - -bool AVI_is_avi(const char *name) -{ - int temp, fcca, j; - AviMovie movie = {NULL}; - AviMainHeader header; - AviBitmapInfoHeader bheader; - int movie_tracks = 0; - - DEBUG_PRINT("opening movie\n"); - - movie.type = AVI_MOVIE_READ; - movie.fp = BLI_fopen(name, "rb"); - movie.offset_table = NULL; - - if (movie.fp == NULL) { - return 0; - } - - if (GET_FCC(movie.fp) != FCC("RIFF") || !(movie.size = GET_FCC(movie.fp))) { - fclose(movie.fp); - return 0; - } - - movie.header = &header; - - if (GET_FCC(movie.fp) != FCC("AVI ") || GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || - GET_FCC(movie.fp) != FCC("hdrl") || (movie.header->fcc = GET_FCC(movie.fp)) != FCC("avih") || - !(movie.header->size = GET_FCC(movie.fp))) { - DEBUG_PRINT("bad initial header info\n"); - fclose(movie.fp); - return 0; - } - - movie.header->MicroSecPerFrame = GET_FCC(movie.fp); - movie.header->MaxBytesPerSec = GET_FCC(movie.fp); - movie.header->PaddingGranularity = GET_FCC(movie.fp); - movie.header->Flags = GET_FCC(movie.fp); - movie.header->TotalFrames = GET_FCC(movie.fp); - movie.header->InitialFrames = GET_FCC(movie.fp); - movie.header->Streams = GET_FCC(movie.fp); - movie.header->SuggestedBufferSize = GET_FCC(movie.fp); - movie.header->Width = GET_FCC(movie.fp); - movie.header->Height = GET_FCC(movie.fp); - movie.header->Reserved[0] = GET_FCC(movie.fp); - movie.header->Reserved[1] = GET_FCC(movie.fp); - movie.header->Reserved[2] = GET_FCC(movie.fp); - movie.header->Reserved[3] = GET_FCC(movie.fp); - - fseek(movie.fp, movie.header->size - 14 * 4, SEEK_CUR); - - /* Limit number of streams to some reasonable amount to prevent - * buffer overflow vulnerabilities. */ - if (movie.header->Streams < 1 || movie.header->Streams > 65536) { - DEBUG_PRINT("Number of streams should be in range 1-65536\n"); - fclose(movie.fp); - return 0; - } - - movie.streams = (AviStreamRec *)MEM_calloc_arrayN( - movie.header->Streams, sizeof(AviStreamRec), "moviestreams"); - - for (temp = 0; temp < movie.header->Streams; temp++) { - - if (GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || - GET_FCC(movie.fp) != FCC("strl") || - (movie.streams[temp].sh.fcc = GET_FCC(movie.fp)) != FCC("strh") || - !(movie.streams[temp].sh.size = GET_FCC(movie.fp))) { - DEBUG_PRINT("bad stream header information\n"); - - MEM_freeN(movie.streams); - fclose(movie.fp); - return 0; - } - - movie.streams[temp].sh.Type = GET_FCC(movie.fp); - movie.streams[temp].sh.Handler = GET_FCC(movie.fp); - - fcca = movie.streams[temp].sh.Handler; - - if (movie.streams[temp].sh.Type == FCC("vids")) { - if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || - fcca == FCC("RAW ") || fcca == 0) { - movie.streams[temp].format = AVI_FORMAT_AVI_RGB; - } - else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { - movie.streams[temp].format = AVI_FORMAT_MJPEG; - } - else { - MEM_freeN(movie.streams); - fclose(movie.fp); - return 0; - } - movie_tracks++; - } - - movie.streams[temp].sh.Flags = GET_FCC(movie.fp); - movie.streams[temp].sh.Priority = GET_TCC(movie.fp); - movie.streams[temp].sh.Language = GET_TCC(movie.fp); - movie.streams[temp].sh.InitialFrames = GET_FCC(movie.fp); - movie.streams[temp].sh.Scale = GET_FCC(movie.fp); - movie.streams[temp].sh.Rate = GET_FCC(movie.fp); - movie.streams[temp].sh.Start = GET_FCC(movie.fp); - movie.streams[temp].sh.Length = GET_FCC(movie.fp); - movie.streams[temp].sh.SuggestedBufferSize = GET_FCC(movie.fp); - movie.streams[temp].sh.Quality = GET_FCC(movie.fp); - movie.streams[temp].sh.SampleSize = GET_FCC(movie.fp); - movie.streams[temp].sh.left = GET_TCC(movie.fp); - movie.streams[temp].sh.top = GET_TCC(movie.fp); - movie.streams[temp].sh.right = GET_TCC(movie.fp); - movie.streams[temp].sh.bottom = GET_TCC(movie.fp); - - fseek(movie.fp, movie.streams[temp].sh.size - 14 * 4, SEEK_CUR); - - if (GET_FCC(movie.fp) != FCC("strf")) { - DEBUG_PRINT("no stream format information\n"); - MEM_freeN(movie.streams); - fclose(movie.fp); - return 0; - } - - movie.streams[temp].sf_size = GET_FCC(movie.fp); - if (movie.streams[temp].sh.Type == FCC("vids")) { - j = movie.streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); - if (j >= 0) { - AviBitmapInfoHeader *bi; - - movie.streams[temp].sf = &bheader; - bi = (AviBitmapInfoHeader *)movie.streams[temp].sf; - - bi->fcc = FCC("strf"); - bi->size = movie.streams[temp].sf_size; - bi->Size = GET_FCC(movie.fp); - bi->Width = GET_FCC(movie.fp); - bi->Height = GET_FCC(movie.fp); - bi->Planes = GET_TCC(movie.fp); - bi->BitCount = GET_TCC(movie.fp); - bi->Compression = GET_FCC(movie.fp); - bi->SizeImage = GET_FCC(movie.fp); - bi->XPelsPerMeter = GET_FCC(movie.fp); - bi->YPelsPerMeter = GET_FCC(movie.fp); - bi->ClrUsed = GET_FCC(movie.fp); - bi->ClrImportant = GET_FCC(movie.fp); - - fcca = bi->Compression; - - if (movie.streams[temp].format == AVI_FORMAT_AVI_RGB) { - if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || - fcca == FCC("RAW ") || fcca == 0) { - /* pass */ - } - else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { - movie.streams[temp].format = AVI_FORMAT_MJPEG; - } - else { - MEM_freeN(movie.streams); - fclose(movie.fp); - return 0; - } - } - } - if (j > 0) { - fseek(movie.fp, j, SEEK_CUR); - } - } - else { - fseek(movie.fp, movie.streams[temp].sf_size, SEEK_CUR); - } - - /* Walk to the next LIST */ - while (GET_FCC(movie.fp) != FCC("LIST")) { - temp = GET_FCC(movie.fp); - if (temp < 0 || ftell(movie.fp) > movie.size) { - DEBUG_PRINT("incorrect size in header or error in AVI\n"); - - MEM_freeN(movie.streams); - fclose(movie.fp); - return 0; - } - fseek(movie.fp, temp, SEEK_CUR); - } - - fseek(movie.fp, -4L, SEEK_CUR); - } - - MEM_freeN(movie.streams); - fclose(movie.fp); - - /* at least one video track is needed */ - return (movie_tracks != 0); -} - -AviError AVI_open_movie(const char *name, AviMovie *movie) -{ - int temp, fcca, size, j; - - DEBUG_PRINT("opening movie\n"); - - memset(movie, 0, sizeof(AviMovie)); - - movie->type = AVI_MOVIE_READ; - movie->fp = BLI_fopen(name, "rb"); - movie->offset_table = NULL; - - if (movie->fp == NULL) { - return AVI_ERROR_OPEN; - } - - if (GET_FCC(movie->fp) != FCC("RIFF") || !(movie->size = GET_FCC(movie->fp))) { - return AVI_ERROR_FORMAT; - } - - movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); - - if (GET_FCC(movie->fp) != FCC("AVI ") || GET_FCC(movie->fp) != FCC("LIST") || - !GET_FCC(movie->fp) || GET_FCC(movie->fp) != FCC("hdrl") || - (movie->header->fcc = GET_FCC(movie->fp)) != FCC("avih") || - !(movie->header->size = GET_FCC(movie->fp))) { - DEBUG_PRINT("bad initial header info\n"); - return AVI_ERROR_FORMAT; - } - - movie->header->MicroSecPerFrame = GET_FCC(movie->fp); - movie->header->MaxBytesPerSec = GET_FCC(movie->fp); - movie->header->PaddingGranularity = GET_FCC(movie->fp); - movie->header->Flags = GET_FCC(movie->fp); - movie->header->TotalFrames = GET_FCC(movie->fp); - movie->header->InitialFrames = GET_FCC(movie->fp); - movie->header->Streams = GET_FCC(movie->fp); - movie->header->SuggestedBufferSize = GET_FCC(movie->fp); - movie->header->Width = GET_FCC(movie->fp); - movie->header->Height = GET_FCC(movie->fp); - movie->header->Reserved[0] = GET_FCC(movie->fp); - movie->header->Reserved[1] = GET_FCC(movie->fp); - movie->header->Reserved[2] = GET_FCC(movie->fp); - movie->header->Reserved[3] = GET_FCC(movie->fp); - - fseek(movie->fp, movie->header->size - 14 * 4, SEEK_CUR); - - /* Limit number of streams to some reasonable amount to prevent - * buffer overflow vulnerabilities. */ - if (movie->header->Streams < 1 || movie->header->Streams > 65536) { - DEBUG_PRINT("Number of streams should be in range 1-65536\n"); - return AVI_ERROR_FORMAT; - } - - movie->streams = (AviStreamRec *)MEM_calloc_arrayN( - movie->header->Streams, sizeof(AviStreamRec), "moviestreams"); - - for (temp = 0; temp < movie->header->Streams; temp++) { - - if (GET_FCC(movie->fp) != FCC("LIST") || !GET_FCC(movie->fp) || - GET_FCC(movie->fp) != FCC("strl") || - (movie->streams[temp].sh.fcc = GET_FCC(movie->fp)) != FCC("strh") || - !(movie->streams[temp].sh.size = GET_FCC(movie->fp))) { - DEBUG_PRINT("bad stream header information\n"); - return AVI_ERROR_FORMAT; - } - - movie->streams[temp].sh.Type = GET_FCC(movie->fp); - movie->streams[temp].sh.Handler = GET_FCC(movie->fp); - - fcca = movie->streams[temp].sh.Handler; - - if (movie->streams[temp].sh.Type == FCC("vids")) { - if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || - fcca == FCC("RAW ") || fcca == 0) { - movie->streams[temp].format = AVI_FORMAT_AVI_RGB; - } - else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { - movie->streams[temp].format = AVI_FORMAT_MJPEG; - } - else { - return AVI_ERROR_COMPRESSION; - } - } - - movie->streams[temp].sh.Flags = GET_FCC(movie->fp); - movie->streams[temp].sh.Priority = GET_TCC(movie->fp); - movie->streams[temp].sh.Language = GET_TCC(movie->fp); - movie->streams[temp].sh.InitialFrames = GET_FCC(movie->fp); - movie->streams[temp].sh.Scale = GET_FCC(movie->fp); - movie->streams[temp].sh.Rate = GET_FCC(movie->fp); - movie->streams[temp].sh.Start = GET_FCC(movie->fp); - movie->streams[temp].sh.Length = GET_FCC(movie->fp); - movie->streams[temp].sh.SuggestedBufferSize = GET_FCC(movie->fp); - movie->streams[temp].sh.Quality = GET_FCC(movie->fp); - movie->streams[temp].sh.SampleSize = GET_FCC(movie->fp); - movie->streams[temp].sh.left = GET_TCC(movie->fp); - movie->streams[temp].sh.top = GET_TCC(movie->fp); - movie->streams[temp].sh.right = GET_TCC(movie->fp); - movie->streams[temp].sh.bottom = GET_TCC(movie->fp); - - fseek(movie->fp, movie->streams[temp].sh.size - 14 * 4, SEEK_CUR); - - if (GET_FCC(movie->fp) != FCC("strf")) { - DEBUG_PRINT("no stream format information\n"); - return AVI_ERROR_FORMAT; - } - - movie->streams[temp].sf_size = GET_FCC(movie->fp); - if (movie->streams[temp].sh.Type == FCC("vids")) { - j = movie->streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); - if (j >= 0) { - AviBitmapInfoHeader *bi; - - movie->streams[temp].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat"); - - bi = (AviBitmapInfoHeader *)movie->streams[temp].sf; - - bi->fcc = FCC("strf"); - bi->size = movie->streams[temp].sf_size; - bi->Size = GET_FCC(movie->fp); - bi->Width = GET_FCC(movie->fp); - bi->Height = GET_FCC(movie->fp); - bi->Planes = GET_TCC(movie->fp); - bi->BitCount = GET_TCC(movie->fp); - bi->Compression = GET_FCC(movie->fp); - bi->SizeImage = GET_FCC(movie->fp); - bi->XPelsPerMeter = GET_FCC(movie->fp); - bi->YPelsPerMeter = GET_FCC(movie->fp); - bi->ClrUsed = GET_FCC(movie->fp); - bi->ClrImportant = GET_FCC(movie->fp); - - fcca = bi->Compression; - - if (movie->streams[temp].format == AVI_FORMAT_AVI_RGB) { - if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || - fcca == FCC("RAW ") || fcca == 0) { - /* pass */ - } - else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { - movie->streams[temp].format = AVI_FORMAT_MJPEG; - } - else { - return AVI_ERROR_COMPRESSION; - } - } - } - if (j > 0) { - fseek(movie->fp, j, SEEK_CUR); - } - } - else { - fseek(movie->fp, movie->streams[temp].sf_size, SEEK_CUR); - } - - /* Walk to the next LIST */ - while (GET_FCC(movie->fp) != FCC("LIST")) { - temp = GET_FCC(movie->fp); - if (temp < 0 || ftell(movie->fp) > movie->size) { - DEBUG_PRINT("incorrect size in header or error in AVI\n"); - return AVI_ERROR_FORMAT; - } - fseek(movie->fp, temp, SEEK_CUR); - } - - fseek(movie->fp, -4L, SEEK_CUR); - } - - while (1) { - temp = GET_FCC(movie->fp); - size = GET_FCC(movie->fp); - - if (size == 0) { - break; - } - - if (temp == FCC("LIST")) { - if (GET_FCC(movie->fp) == FCC("movi")) { - break; - } - else { - fseek(movie->fp, size - 4, SEEK_CUR); - } - } - else { - fseek(movie->fp, size, SEEK_CUR); - } - if (ftell(movie->fp) > movie->size) { - DEBUG_PRINT("incorrect size in header or error in AVI\n"); - return AVI_ERROR_FORMAT; - } - } - - movie->movi_offset = ftell(movie->fp); - movie->read_offset = movie->movi_offset; - - /* Read in the index if the file has one, otherwise create one */ - if (movie->header->Flags & AVIF_HASINDEX) { - fseek(movie->fp, size - 4, SEEK_CUR); - - if (GET_FCC(movie->fp) != FCC("idx1")) { - DEBUG_PRINT("bad index informatio\n"); - return AVI_ERROR_FORMAT; - } - - movie->index_entries = GET_FCC(movie->fp) / sizeof(AviIndexEntry); - if (movie->index_entries == 0) { - DEBUG_PRINT("no index entries\n"); - return AVI_ERROR_FORMAT; - } - - movie->entries = (AviIndexEntry *)MEM_mallocN(movie->index_entries * sizeof(AviIndexEntry), - "movieentries"); - - for (temp = 0; temp < movie->index_entries; temp++) { - movie->entries[temp].ChunkId = GET_FCC(movie->fp); - movie->entries[temp].Flags = GET_FCC(movie->fp); - movie->entries[temp].Offset = GET_FCC(movie->fp); - movie->entries[temp].Size = GET_FCC(movie->fp); - - if (AVI_DEBUG) { - printf("Index entry %04d: ChunkId:%s Flags:%d Offset:%d Size:%d\n", - temp, - fcc_to_char(movie->entries[temp].ChunkId), - movie->entries[temp].Flags, - movie->entries[temp].Offset, - movie->entries[temp].Size); - } - } - - /* Some AVI's have offset entries in absolute coordinates - * instead of an offset from the movie beginning... this is... - * wacky, but we need to handle it. The wacky offset always - * starts at movi_offset it seems... so we'll check that. - * Note the offset needs an extra 4 bytes for some - * undetermined reason */ - - if (movie->entries[0].Offset == movie->movi_offset) { - movie->read_offset = 4; - } - } - - DEBUG_PRINT("movie successfully opened\n"); - return AVI_ERROR_NONE; -} - -void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream) -{ - int cur_frame = -1, i = 0, rewind = 1; - void *buffer; - - /* Retrieve the record number of the desired frame in the index - * If a chunk has Size 0 we need to rewind to previous frame */ - while (rewind && frame > -1) { - i = 0; - cur_frame = -1; - rewind = 0; - - while (cur_frame < frame && i < movie->index_entries) { - if (fcc_is_data(movie->entries[i].ChunkId) && - fcc_get_stream(movie->entries[i].ChunkId) == stream) { - if ((cur_frame == frame - 1) && (movie->entries[i].Size == 0)) { - rewind = 1; - frame = frame - 1; - } - else { - cur_frame++; - } - } - i++; - } - } - - if (cur_frame != frame) { - return NULL; - } - - fseek(movie->fp, movie->read_offset + movie->entries[i - 1].Offset, SEEK_SET); - - size_t size = GET_FCC(movie->fp); - buffer = MEM_mallocN(size, "readbuffer"); - - if (fread(buffer, 1, size, movie->fp) != size) { - MEM_freeN(buffer); - - return NULL; - } - - buffer = avi_format_convert(movie, stream, buffer, movie->streams[stream].format, format, &size); - - return buffer; -} - -AviError AVI_close(AviMovie *movie) -{ - int i; - - fclose(movie->fp); - - for (i = 0; i < movie->header->Streams; i++) { - if (movie->streams[i].sf != NULL) { - MEM_freeN(movie->streams[i].sf); - } - } - - MEM_freeN(movie->header); - MEM_freeN(movie->streams); - - if (movie->entries != NULL) { - MEM_freeN(movie->entries); - } - if (movie->offset_table != NULL) { - MEM_freeN(movie->offset_table); - } - - return AVI_ERROR_NONE; -} - -AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...) -{ - va_list ap; - AviList list; - AviChunk chunk; - int i; - int64_t header_pos1, header_pos2; - int64_t stream_pos1, stream_pos2; - int64_t junk_pos; - - movie->type = AVI_MOVIE_WRITE; - movie->fp = BLI_fopen(name, "wb"); - - movie->index_entries = 0; - - if (movie->fp == NULL) { - return AVI_ERROR_OPEN; - } - - movie->offset_table = (int64_t *)MEM_mallocN((1 + streams * 2) * sizeof(int64_t), "offsettable"); - - for (i = 0; i < 1 + streams * 2; i++) { - movie->offset_table[i] = -1L; - } - - movie->entries = NULL; - - movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); - - movie->header->fcc = FCC("avih"); - movie->header->size = 56; - movie->header->MicroSecPerFrame = 66667; - movie->header->MaxBytesPerSec = 0; - movie->header->PaddingGranularity = 0; - movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX; - movie->header->TotalFrames = 0; - movie->header->InitialFrames = 0; - movie->header->Streams = streams; - movie->header->SuggestedBufferSize = 0; - movie->header->Width = 0; - movie->header->Height = 0; - movie->header->Reserved[0] = 0; - movie->header->Reserved[1] = 0; - movie->header->Reserved[2] = 0; - movie->header->Reserved[3] = 0; - - /* Limit number of streams to some reasonable amount to prevent - * buffer overflow vulnerabilities. */ - if (movie->header->Streams < 0 || movie->header->Streams > 65536) { - DEBUG_PRINT("Number of streams should be in range 0-65536\n"); - return AVI_ERROR_FORMAT; - } - - movie->streams = (AviStreamRec *)MEM_mallocN(sizeof(AviStreamRec) * movie->header->Streams, - "moviestreams"); - - va_start(ap, streams); - - for (i = 0; i < movie->header->Streams; i++) { - movie->streams[i].format = va_arg(ap, AviFormat); - - movie->streams[i].sh.fcc = FCC("strh"); - movie->streams[i].sh.size = 56; - movie->streams[i].sh.Type = avi_get_format_type(movie->streams[i].format); - if (movie->streams[i].sh.Type == 0) { - va_end(ap); - return AVI_ERROR_FORMAT; - } - - movie->streams[i].sh.Handler = avi_get_format_fcc(movie->streams[i].format); - if (movie->streams[i].sh.Handler == 0) { - va_end(ap); - return AVI_ERROR_FORMAT; - } - - movie->streams[i].sh.Flags = 0; - movie->streams[i].sh.Priority = 0; - movie->streams[i].sh.Language = 0; - movie->streams[i].sh.InitialFrames = 0; - movie->streams[i].sh.Scale = 66667; - movie->streams[i].sh.Rate = 1000000; - movie->streams[i].sh.Start = 0; - movie->streams[i].sh.Length = 0; - movie->streams[i].sh.SuggestedBufferSize = 0; - movie->streams[i].sh.Quality = 10000; - movie->streams[i].sh.SampleSize = 0; - movie->streams[i].sh.left = 0; - movie->streams[i].sh.top = 0; - movie->streams[i].sh.right = 0; - movie->streams[i].sh.bottom = 0; - - if (movie->streams[i].sh.Type == FCC("vids")) { - movie->streams[i].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "moviestreamformatS"); - movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader); - - ((AviBitmapInfoHeader *)movie->streams[i].sf)->fcc = FCC("strf"); - ((AviBitmapInfoHeader *)movie->streams[i].sf)->size = movie->streams[i].sf_size - 8; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Planes = 1; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->BitCount = 24; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Compression = avi_get_format_compression( - movie->streams[i].format); - ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->XPelsPerMeter = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->YPelsPerMeter = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrUsed = 0; - ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrImportant = 0; - } - } - - list.fcc = FCC("RIFF"); - list.size = 0; - list.ids = FCC("AVI "); - - awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); - - list.fcc = FCC("LIST"); - list.size = 0; - list.ids = FCC("hdrl"); - - awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); - - header_pos1 = ftell(movie->fp); - - movie->offset_table[0] = ftell(movie->fp); - - awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); - - for (i = 0; i < movie->header->Streams; i++) { - list.fcc = FCC("LIST"); - list.size = 0; - list.ids = FCC("strl"); - - awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); - - stream_pos1 = ftell(movie->fp); - - movie->offset_table[1 + i * 2] = ftell(movie->fp); - awrite(movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); - - movie->offset_table[1 + i * 2 + 1] = ftell(movie->fp); - awrite(movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH); - - stream_pos2 = ftell(movie->fp); - - fseek(movie->fp, stream_pos1 - 8, SEEK_SET); - - PUT_FCCN((stream_pos2 - stream_pos1 + 4L), movie->fp); - - fseek(movie->fp, stream_pos2, SEEK_SET); - } - - junk_pos = ftell(movie->fp); - - if (junk_pos < 2024 - 8) { - chunk.fcc = FCC("JUNK"); - chunk.size = 2024 - 8 - (int)junk_pos; - - awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); - - for (i = 0; i < chunk.size; i++) { - putc(0, movie->fp); - } - } - - header_pos2 = ftell(movie->fp); - - list.fcc = FCC("LIST"); - list.size = 0; - list.ids = FCC("movi"); - - awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); - - movie->movi_offset = ftell(movie->fp) - 8L; - - fseek(movie->fp, AVI_HDRL_SOFF, SEEK_SET); - - PUT_FCCN((header_pos2 - header_pos1 + 4L), movie->fp); - - va_end(ap); - - return AVI_ERROR_NONE; -} - -AviError AVI_write_frame(AviMovie *movie, int frame_num, ...) -{ - AviList list; - AviChunk chunk; - va_list ap; - int stream; - int64_t rec_off; - AviFormat format; - void *buffer; - - if (frame_num < 0) { - return AVI_ERROR_OPTION; - } - - /* Allocate the new memory for the index entry */ - - if (frame_num >= movie->index_entries) { - const size_t entry_size = (movie->header->Streams + 1) * sizeof(AviIndexEntry); - movie->entries = (AviIndexEntry *)MEM_recallocN(movie->entries, (frame_num + 1) * entry_size); - movie->index_entries = frame_num + 1; - } - - /* Slap a new record entry onto the end of the file */ - - fseek(movie->fp, 0L, SEEK_END); - - list.fcc = FCC("LIST"); - list.size = 0; - list.ids = FCC("rec "); - - awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); - - rec_off = ftell(movie->fp) - 8L; - - /* Write a frame for every stream */ - - va_start(ap, frame_num); - - for (stream = 0; stream < movie->header->Streams; stream++) { - unsigned int tbuf = 0; - - format = va_arg(ap, AviFormat); - buffer = va_arg(ap, void *); - size_t size = va_arg(ap, int); - - /* Convert the buffer into the output format */ - buffer = avi_format_convert( - movie, stream, buffer, format, movie->streams[stream].format, &size); - - /* Write the header info for this data chunk */ - - fseek(movie->fp, 0L, SEEK_END); - - chunk.fcc = avi_get_data_id(format, stream); - chunk.size = size; - - if (size % 4) { - chunk.size += 4 - size % 4; - } - - awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); - - /* Write the index entry for this data chunk */ - - movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].ChunkId = chunk.fcc; - movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Flags = AVIIF_KEYFRAME; - movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Offset = - (int)(ftell(movie->fp) - 12L - movie->movi_offset); - movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Size = chunk.size; - - /* Write the chunk */ - awrite(movie, buffer, 1, size, movie->fp, AVI_RAW); - MEM_freeN(buffer); - - if (size % 4) { - awrite(movie, &tbuf, 1, 4 - size % 4, movie->fp, AVI_RAW); - } - - /* Update the stream headers length field */ - movie->streams[stream].sh.Length++; - fseek(movie->fp, movie->offset_table[1 + stream * 2], SEEK_SET); - awrite(movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); - } - va_end(ap); - - /* Record the entry for the new record */ - - fseek(movie->fp, 0L, SEEK_END); - - movie->entries[frame_num * (movie->header->Streams + 1)].ChunkId = FCC("rec "); - movie->entries[frame_num * (movie->header->Streams + 1)].Flags = AVIIF_LIST; - movie->entries[frame_num * (movie->header->Streams + 1)].Offset = (int)(rec_off - 8L - - movie->movi_offset); - movie->entries[frame_num * (movie->header->Streams + 1)].Size = (int)(ftell(movie->fp) - - (rec_off + 4L)); - - /* Update the record size */ - fseek(movie->fp, rec_off, SEEK_SET); - PUT_FCCN(movie->entries[frame_num * (movie->header->Streams + 1)].Size, movie->fp); - - /* Update the main header information in the file */ - movie->header->TotalFrames++; - fseek(movie->fp, movie->offset_table[0], SEEK_SET); - awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); - - return AVI_ERROR_NONE; -} - -AviError AVI_close_compress(AviMovie *movie) -{ - int temp, movi_size, i; - - if (movie->fp == NULL) { - /* none of the allocations below were done if the file failed to open */ - return AVI_ERROR_FOUND; - } - - fseek(movie->fp, 0L, SEEK_END); - movi_size = (int)ftell(movie->fp); - - PUT_FCC("idx1", movie->fp); - PUT_FCCN((movie->index_entries * (movie->header->Streams + 1) * 16), movie->fp); - - for (temp = 0; temp < movie->index_entries * (movie->header->Streams + 1); temp++) { - awrite(movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE); - } - - temp = (int)ftell(movie->fp); - - fseek(movie->fp, AVI_RIFF_SOFF, SEEK_SET); - - PUT_FCCN((temp - 8L), movie->fp); - - fseek(movie->fp, movie->movi_offset, SEEK_SET); - - PUT_FCCN((movi_size - (movie->movi_offset + 4L)), movie->fp); - - fclose(movie->fp); - - for (i = 0; i < movie->header->Streams; i++) { - if (movie->streams && (movie->streams[i].sf != NULL)) { - MEM_freeN(movie->streams[i].sf); - } - } - - MEM_freeN(movie->header); - - if (movie->entries != NULL) { - MEM_freeN(movie->entries); - } - if (movie->streams != NULL) { - MEM_freeN(movie->streams); - } - if (movie->offset_table != NULL) { - MEM_freeN(movie->offset_table); - } - return AVI_ERROR_NONE; -} diff --git a/source/blender/avi/intern/avi_codecs.c b/source/blender/avi/intern/avi_codecs.c deleted file mode 100644 index 15f498ac653..00000000000 --- a/source/blender/avi/intern/avi_codecs.c +++ /dev/null @@ -1,138 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Identify and convert different avi-files. - */ - -#include "AVI_avi.h" -#include "avi_intern.h" - -#include "avi_rgb.h" -#include "avi_mjpeg.h" -#include "avi_rgb32.h" - -void *avi_format_convert( - AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size) -{ - if (from == to) { - return buffer; - } - - if (from != AVI_FORMAT_RGB24 && to != AVI_FORMAT_RGB24) { - return avi_format_convert( - movie, - stream, - avi_format_convert(movie, stream, buffer, from, AVI_FORMAT_RGB24, size), - AVI_FORMAT_RGB24, - to, - size); - } - - switch (to) { - case AVI_FORMAT_RGB24: - switch (from) { - case AVI_FORMAT_AVI_RGB: - buffer = avi_converter_from_avi_rgb(movie, stream, buffer, size); - break; - case AVI_FORMAT_MJPEG: - buffer = avi_converter_from_mjpeg(movie, stream, buffer, size); - break; - case AVI_FORMAT_RGB32: - buffer = avi_converter_from_rgb32(movie, stream, buffer, size); - break; - default: - break; - } - break; - case AVI_FORMAT_AVI_RGB: - buffer = avi_converter_to_avi_rgb(movie, stream, buffer, size); - break; - case AVI_FORMAT_MJPEG: - buffer = avi_converter_to_mjpeg(movie, stream, buffer, size); - break; - case AVI_FORMAT_RGB32: - buffer = avi_converter_to_rgb32(movie, stream, buffer, size); - break; - default: - break; - } - - return buffer; -} - -int avi_get_data_id(AviFormat format, int stream) -{ - char fcc[5]; - - if (avi_get_format_type(format) == FCC("vids")) { - sprintf(fcc, "%2.2ddc", stream); - } - else if (avi_get_format_type(format) == FCC("auds")) { - sprintf(fcc, "%2.2ddc", stream); - } - else { - return 0; - } - - return FCC(fcc); -} - -int avi_get_format_type(AviFormat format) -{ - switch (format) { - case AVI_FORMAT_RGB24: - case AVI_FORMAT_RGB32: - case AVI_FORMAT_AVI_RGB: - case AVI_FORMAT_MJPEG: - return FCC("vids"); - default: - return 0; - } -} - -int avi_get_format_fcc(AviFormat format) -{ - switch (format) { - case AVI_FORMAT_RGB24: - case AVI_FORMAT_RGB32: - case AVI_FORMAT_AVI_RGB: - return FCC("DIB "); - case AVI_FORMAT_MJPEG: - return FCC("MJPG"); - default: - return 0; - } -} - -int avi_get_format_compression(AviFormat format) -{ - switch (format) { - case AVI_FORMAT_RGB24: - case AVI_FORMAT_RGB32: - case AVI_FORMAT_AVI_RGB: - return 0; - case AVI_FORMAT_MJPEG: - return FCC("MJPG"); - default: - return 0; - } -} diff --git a/source/blender/avi/intern/avi_endian.c b/source/blender/avi/intern/avi_endian.c deleted file mode 100644 index 56474e9e329..00000000000 --- a/source/blender/avi/intern/avi_endian.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Streams bytes to output depending on the - * endianness of the system. - */ - -#include -#include -#include - -#include "AVI_avi.h" -#include "avi_endian.h" -#include "avi_intern.h" - -#ifdef __BIG_ENDIAN__ -# include "MEM_guardedalloc.h" -#endif - -#ifdef __BIG_ENDIAN__ - -/* copied from BLI_endian_switch_inline.h */ -static void invert(int *val) -{ - int tval = *val; - *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); -} - -static void sinvert(short int *val) -{ - short tval = *val; - *val = (tval >> 8) | (tval << 8); -} - -static void Ichunk(AviChunk *chunk) -{ - invert(&chunk->fcc); - invert(&chunk->size); -} -#endif - -#ifdef __BIG_ENDIAN__ -static void Ilist(AviList *list) -{ - invert(&list->fcc); - invert(&list->size); - invert(&list->ids); -} - -static void Imainh(AviMainHeader *mainh) -{ - invert(&mainh->fcc); - invert(&mainh->size); - invert(&mainh->MicroSecPerFrame); - invert(&mainh->MaxBytesPerSec); - invert(&mainh->PaddingGranularity); - invert(&mainh->Flags); - invert(&mainh->TotalFrames); - invert(&mainh->InitialFrames); - invert(&mainh->Streams); - invert(&mainh->SuggestedBufferSize); - invert(&mainh->Width); - invert(&mainh->Height); - invert(&mainh->Reserved[0]); - invert(&mainh->Reserved[1]); - invert(&mainh->Reserved[2]); - invert(&mainh->Reserved[3]); -} - -static void Istreamh(AviStreamHeader *streamh) -{ - invert(&streamh->fcc); - invert(&streamh->size); - invert(&streamh->Type); - invert(&streamh->Handler); - invert(&streamh->Flags); - sinvert(&streamh->Priority); - sinvert(&streamh->Language); - invert(&streamh->InitialFrames); - invert(&streamh->Scale); - invert(&streamh->Rate); - invert(&streamh->Start); - invert(&streamh->Length); - invert(&streamh->SuggestedBufferSize); - invert(&streamh->Quality); - invert(&streamh->SampleSize); - sinvert(&streamh->left); - sinvert(&streamh->right); - sinvert(&streamh->top); - sinvert(&streamh->bottom); -} - -static void Ibitmaph(AviBitmapInfoHeader *bitmaph) -{ - invert(&bitmaph->fcc); - invert(&bitmaph->size); - invert(&bitmaph->Size); - invert(&bitmaph->Width); - invert(&bitmaph->Height); - sinvert(&bitmaph->Planes); - sinvert(&bitmaph->BitCount); - invert(&bitmaph->Compression); - invert(&bitmaph->SizeImage); - invert(&bitmaph->XPelsPerMeter); - invert(&bitmaph->YPelsPerMeter); - invert(&bitmaph->ClrUsed); - invert(&bitmaph->ClrImportant); -} - -static void Imjpegu(AviMJPEGUnknown *mjpgu) -{ - invert(&mjpgu->a); - invert(&mjpgu->b); - invert(&mjpgu->c); - invert(&mjpgu->d); - invert(&mjpgu->e); - invert(&mjpgu->f); - invert(&mjpgu->g); -} - -static void Iindexe(AviIndexEntry *indexe) -{ - invert(&indexe->ChunkId); - invert(&indexe->Flags); - invert(&indexe->Offset); - invert(&indexe->Size); -} -#endif /* __BIG_ENDIAN__ */ - -void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type) -{ -#ifdef __BIG_ENDIAN__ - void *data; - - data = MEM_mallocN(size, "avi endian"); - - memcpy(data, datain, size); - - switch (type) { - case AVI_RAW: - fwrite(data, block, size, fp); - break; - case AVI_CHUNK: - Ichunk((AviChunk *)data); - fwrite(data, block, size, fp); - break; - case AVI_LIST: - Ilist((AviList *)data); - fwrite(data, block, size, fp); - break; - case AVI_MAINH: - Imainh((AviMainHeader *)data); - fwrite(data, block, size, fp); - break; - case AVI_STREAMH: - Istreamh((AviStreamHeader *)data); - fwrite(data, block, size, fp); - break; - case AVI_BITMAPH: - Ibitmaph((AviBitmapInfoHeader *)data); - if (size == sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown)) { - Imjpegu((AviMJPEGUnknown *)((char *)data + sizeof(AviBitmapInfoHeader))); - } - fwrite(data, block, size, fp); - break; - case AVI_MJPEGU: - Imjpegu((AviMJPEGUnknown *)data); - fwrite(data, block, size, fp); - break; - case AVI_INDEXE: - Iindexe((AviIndexEntry *)data); - fwrite(data, block, size, fp); - break; - default: - break; - } - - MEM_freeN(data); -#else /* __BIG_ENDIAN__ */ - (void)movie; /* unused */ - (void)type; /* unused */ - fwrite(datain, block, size, fp); -#endif /* __BIG_ENDIAN__ */ -} diff --git a/source/blender/avi/intern/avi_endian.h b/source/blender/avi/intern/avi_endian.h deleted file mode 100644 index d1253f488e7..00000000000 --- a/source/blender/avi/intern/avi_endian.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. - */ - -#ifndef __AVI_ENDIAN_H__ -#define __AVI_ENDIAN_H__ - -#define AVI_RAW 0 -#define AVI_CHUNK 1 -#define AVI_LIST 2 -#define AVI_MAINH 3 -#define AVI_STREAMH 4 -#define AVI_BITMAPH 5 -#define AVI_INDEXE 6 -#define AVI_MJPEGU 7 - -void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type); - -#endif /* __AVI_ENDIAN_H__ */ diff --git a/source/blender/avi/intern/avi_intern.h b/source/blender/avi/intern/avi_intern.h deleted file mode 100644 index 6ce91ce7f70..00000000000 --- a/source/blender/avi/intern/avi_intern.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - */ - -#ifndef __AVI_INTERN_H__ -#define __AVI_INTERN_H__ - -#include /* for FILE */ - -unsigned int GET_FCC(FILE *fp); -unsigned int GET_TCC(FILE *fp); - -#define PUT_FCC(ch4, fp) \ - { \ - putc(ch4[0], fp); \ - putc(ch4[1], fp); \ - putc(ch4[2], fp); \ - putc(ch4[3], fp); \ - } \ - (void)0 - -#define PUT_FCCN(num, fp) \ - { \ - putc((num >> 0) & 0377, fp); \ - putc((num >> 8) & 0377, fp); \ - putc((num >> 16) & 0377, fp); \ - putc((num >> 24) & 0377, fp); \ - } \ - (void)0 - -#define PUT_TCC(ch2, fp) \ - { \ - putc(ch2[0], fp); \ - putc(ch2[1], fp); \ - } \ - (void)0 - -void *avi_format_convert( - AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size); - -int avi_get_data_id(AviFormat format, int stream); -int avi_get_format_type(AviFormat format); -int avi_get_format_fcc(AviFormat format); -int avi_get_format_compression(AviFormat format); - -#endif diff --git a/source/blender/avi/intern/avi_mjpeg.c b/source/blender/avi/intern/avi_mjpeg.c deleted file mode 100644 index d4c7378964e..00000000000 --- a/source/blender/avi/intern/avi_mjpeg.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Converts between avi and mpeg/jpeg. - */ - -#include -#include - -#include "AVI_avi.h" - -#include "MEM_guardedalloc.h" - -#include "IMB_imbuf.h" - -#include "jpeglib.h" -#include "jerror.h" - -#include "avi_mjpeg.h" - -static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize); -static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize); - -static size_t numbytes; - -static void add_huff_table(j_decompress_ptr dinfo, - JHUFF_TBL **htblptr, - const UINT8 *bits, - const UINT8 *val) -{ - if (*htblptr == NULL) { - *htblptr = jpeg_alloc_huff_table((j_common_ptr)dinfo); - } - - memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits)); - memcpy((*htblptr)->huffval, val, sizeof((*htblptr)->huffval)); - - /* Initialize sent_table false so table will be written to JPEG file. */ - (*htblptr)->sent_table = false; -} - -/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ -/* IMPORTANT: these are only valid for 8-bit data precision! */ - -static void std_huff_tables(j_decompress_ptr dinfo) -{ - static const UINT8 bits_dc_luminance[17] = { - /* 0-base */ - 0, - 0, - 1, - 5, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - }; - static const UINT8 val_dc_luminance[] = { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - }; - - static const UINT8 bits_dc_chrominance[17] = { - /* 0-base */ - 0, - 0, - 3, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 0, - 0, - 0, - 0, - 0, - }; - static const UINT8 val_dc_chrominance[] = { - 0, - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - }; - - static const UINT8 bits_ac_luminance[17] = { - /* 0-base */ - 0, - 0, - 2, - 1, - 3, - 3, - 2, - 4, - 3, - 5, - 5, - 4, - 4, - 0, - 0, - 1, - 0x7d, - }; - static const UINT8 val_ac_luminance[] = { - 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, - 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, - 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, - 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, - 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, - 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, - 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, - 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, - 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, - 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, - }; - static const UINT8 bits_ac_chrominance[17] = { - /* 0-base */ - 0, - 0, - 2, - 1, - 2, - 4, - 4, - 3, - 4, - 7, - 5, - 4, - 4, - 0, - 1, - 2, - 0x77, - }; - static const UINT8 val_ac_chrominance[] = { - 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, - 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, - 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, - 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, - 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, - 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, - 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, - 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, - 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, - 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, - }; - - add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[0], bits_dc_luminance, val_dc_luminance); - add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[0], bits_ac_luminance, val_ac_luminance); - add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[1], bits_dc_chrominance, val_dc_chrominance); - add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[1], bits_ac_chrominance, val_ac_chrominance); -} - -static int Decode_JPEG(unsigned char *inBuffer, - unsigned char *outBuffer, - unsigned int width, - unsigned int height, - size_t bufsize) -{ - struct jpeg_decompress_struct dinfo; - struct jpeg_error_mgr jerr; - - (void)width; /* unused */ - - numbytes = 0; - - dinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&dinfo); - jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize); - jpeg_read_header(&dinfo, true); - if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { - std_huff_tables(&dinfo); - } - dinfo.out_color_space = JCS_RGB; - dinfo.dct_method = JDCT_IFAST; - - jpeg_start_decompress(&dinfo); - - size_t rowstride = dinfo.output_width * dinfo.output_components; - for (size_t y = 0; y < dinfo.output_height; y++) { - jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); - outBuffer += rowstride; - } - jpeg_finish_decompress(&dinfo); - - if (dinfo.output_height >= height) { - return 0; - } - - inBuffer += numbytes; - jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize - numbytes); - - numbytes = 0; - jpeg_read_header(&dinfo, true); - if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { - std_huff_tables(&dinfo); - } - - jpeg_start_decompress(&dinfo); - rowstride = dinfo.output_width * dinfo.output_components; - for (size_t y = 0; y < dinfo.output_height; y++) { - jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); - outBuffer += rowstride; - } - jpeg_finish_decompress(&dinfo); - jpeg_destroy_decompress(&dinfo); - - return 1; -} - -static void Compress_JPEG(int quality, - unsigned char *outbuffer, - const unsigned char *inBuffer, - int width, - int height, - size_t bufsize) -{ - struct jpeg_compress_struct cinfo; - struct jpeg_error_mgr jerr; - unsigned char marker[60]; - - cinfo.err = jpeg_std_error(&jerr); - jpeg_create_compress(&cinfo); - jpegmemdestmgr_build(&cinfo, outbuffer, bufsize); - - cinfo.image_width = width; - cinfo.image_height = height; - cinfo.input_components = 3; - cinfo.in_color_space = JCS_RGB; - - jpeg_set_defaults(&cinfo); - jpeg_set_colorspace(&cinfo, JCS_YCbCr); - - jpeg_set_quality(&cinfo, quality, true); - - cinfo.dc_huff_tbl_ptrs[0]->sent_table = true; - cinfo.dc_huff_tbl_ptrs[1]->sent_table = true; - cinfo.ac_huff_tbl_ptrs[0]->sent_table = true; - cinfo.ac_huff_tbl_ptrs[1]->sent_table = true; - - cinfo.comp_info[0].component_id = 0; - cinfo.comp_info[0].v_samp_factor = 1; - cinfo.comp_info[1].component_id = 1; - cinfo.comp_info[2].component_id = 2; - - cinfo.write_JFIF_header = false; - - jpeg_start_compress(&cinfo, false); - - int i = 0; - marker[i++] = 'A'; - marker[i++] = 'V'; - marker[i++] = 'I'; - marker[i++] = '1'; - marker[i++] = 0; - while (i < 60) { - marker[i++] = 32; - } - - jpeg_write_marker(&cinfo, JPEG_APP0, marker, 60); - - i = 0; - while (i < 60) { - marker[i++] = 0; - } - - jpeg_write_marker(&cinfo, JPEG_COM, marker, 60); - - size_t rowstride = cinfo.image_width * cinfo.input_components; - for (size_t y = 0; y < cinfo.image_height; y++) { - jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&inBuffer, 1); - inBuffer += rowstride; - } - jpeg_finish_compress(&cinfo); - jpeg_destroy_compress(&cinfo); -} - -static void interlace(unsigned char *to, unsigned char *from, int width, int height) -{ - size_t i, rowstride = width * 3; - - for (i = 0; i < height; i++) { - if (i & 1) { - memcpy(&to[i * rowstride], &from[(i / 2 + height / 2) * rowstride], rowstride); - } - else { - memcpy(&to[i * rowstride], &from[(i / 2) * rowstride], rowstride); - } - } -} - -static void deinterlace(int odd, unsigned char *to, unsigned char *from, int width, int height) -{ - size_t i, rowstride = width * 3; - - for (i = 0; i < height; i++) { - if ((i & 1) == odd) { - memcpy(&to[(i / 2 + height / 2) * rowstride], &from[i * rowstride], rowstride); - } - else { - memcpy(&to[(i / 2) * rowstride], &from[i * rowstride], rowstride); - } - } -} - -void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - int deint; - unsigned char *buf; - - (void)stream; /* unused */ - - buf = imb_alloc_pixels(movie->header->Height, - movie->header->Width, - 3, - sizeof(unsigned char), - "avi.avi_converter_from_mjpeg 1"); - if (!buf) { - return NULL; - } - - deint = Decode_JPEG(buffer, buf, movie->header->Width, movie->header->Height, *size); - - MEM_freeN(buffer); - - if (deint) { - buffer = imb_alloc_pixels(movie->header->Height, - movie->header->Width, - 3, - sizeof(unsigned char), - "avi.avi_converter_from_mjpeg 2"); - if (buffer) { - interlace(buffer, buf, movie->header->Width, movie->header->Height); - } - MEM_freeN(buf); - - buf = buffer; - } - - return buf; -} - -void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - unsigned char *buf; - size_t bufsize = *size; - - numbytes = 0; - *size = 0; - - buf = imb_alloc_pixels(movie->header->Height, - movie->header->Width, - 3, - sizeof(unsigned char), - "avi.avi_converter_to_mjpeg 1"); - if (!buf) { - return NULL; - } - - if (!movie->interlace) { - Compress_JPEG(movie->streams[stream].sh.Quality / 100, - buf, - buffer, - movie->header->Width, - movie->header->Height, - bufsize); - *size += numbytes; - } - else { - deinterlace(movie->odd_fields, buf, buffer, movie->header->Width, movie->header->Height); - MEM_freeN(buffer); - - buffer = buf; - buf = imb_alloc_pixels(movie->header->Height, - movie->header->Width, - 3, - sizeof(unsigned char), - "avi.avi_converter_to_mjpeg 1"); - - if (buf) { - Compress_JPEG(movie->streams[stream].sh.Quality / 100, - buf, - buffer, - movie->header->Width, - movie->header->Height / 2, - bufsize / 2); - *size += numbytes; - numbytes = 0; - Compress_JPEG(movie->streams[stream].sh.Quality / 100, - buf + *size, - buffer + - (size_t)(movie->header->Height / 2) * (size_t)movie->header->Width * 3, - movie->header->Width, - movie->header->Height / 2, - bufsize / 2); - *size += numbytes; - } - } - - MEM_freeN(buffer); - return buf; -} - -/* Compression from memory */ - -static void jpegmemdestmgr_init_destination(j_compress_ptr cinfo) -{ - (void)cinfo; /* unused */ -} - -static boolean jpegmemdestmgr_empty_output_buffer(j_compress_ptr cinfo) -{ - (void)cinfo; /* unused */ - return true; -} - -static void jpegmemdestmgr_term_destination(j_compress_ptr cinfo) -{ - numbytes -= cinfo->dest->free_in_buffer; - - MEM_freeN(cinfo->dest); -} - -static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize) -{ - cinfo->dest = MEM_mallocN(sizeof(*(cinfo->dest)), "avi.jpegmemdestmgr_build"); - - cinfo->dest->init_destination = jpegmemdestmgr_init_destination; - cinfo->dest->empty_output_buffer = jpegmemdestmgr_empty_output_buffer; - cinfo->dest->term_destination = jpegmemdestmgr_term_destination; - - cinfo->dest->next_output_byte = buffer; - cinfo->dest->free_in_buffer = bufsize; - - numbytes = bufsize; -} - -/* Decompression from memory */ - -static void jpegmemsrcmgr_init_source(j_decompress_ptr dinfo) -{ - (void)dinfo; -} - -static boolean jpegmemsrcmgr_fill_input_buffer(j_decompress_ptr dinfo) -{ - unsigned char *buf = (unsigned char *)dinfo->src->next_input_byte - 2; - - /* if we get called, must have run out of data */ - WARNMS(dinfo, JWRN_JPEG_EOF); - - buf[0] = (JOCTET)0xFF; - buf[1] = (JOCTET)JPEG_EOI; - - dinfo->src->next_input_byte = buf; - dinfo->src->bytes_in_buffer = 2; - - return true; -} - -static void jpegmemsrcmgr_skip_input_data(j_decompress_ptr dinfo, long skipcnt) -{ - if (dinfo->src->bytes_in_buffer < skipcnt) { - skipcnt = dinfo->src->bytes_in_buffer; - } - - dinfo->src->next_input_byte += skipcnt; - dinfo->src->bytes_in_buffer -= skipcnt; -} - -static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo) -{ - numbytes -= dinfo->src->bytes_in_buffer; - - MEM_freeN(dinfo->src); -} - -static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize) -{ - dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build"); - - dinfo->src->init_source = jpegmemsrcmgr_init_source; - dinfo->src->fill_input_buffer = jpegmemsrcmgr_fill_input_buffer; - dinfo->src->skip_input_data = jpegmemsrcmgr_skip_input_data; - dinfo->src->resync_to_restart = jpeg_resync_to_restart; - dinfo->src->term_source = jpegmemsrcmgr_term_source; - - dinfo->src->bytes_in_buffer = bufsize; - dinfo->src->next_input_byte = buffer; - - numbytes = bufsize; -} diff --git a/source/blender/avi/intern/avi_mjpeg.h b/source/blender/avi/intern/avi_mjpeg.h deleted file mode 100644 index 30e46bf1d0c..00000000000 --- a/source/blender/avi/intern/avi_mjpeg.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - */ - -#ifndef __AVI_MJPEG_H__ -#define __AVI_MJPEG_H__ - -void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); -void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); - -#endif /* __AVI_MJPEG_H__ */ diff --git a/source/blender/avi/intern/avi_options.c b/source/blender/avi/intern/avi_options.c deleted file mode 100644 index 65db8c19397..00000000000 --- a/source/blender/avi/intern/avi_options.c +++ /dev/null @@ -1,148 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Sets some compression related options - * (width, height quality, framerate). - */ - -#include "AVI_avi.h" -#include "avi_intern.h" -#include "avi_endian.h" - -#ifdef WIN32 -# include "BLI_winstuff.h" -#endif - -/* avi_set_compress_options gets its own file... now don't WE feel important? */ - -AviError AVI_set_compress_option( - AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data) -{ - int i; - int useconds; - - (void)stream; /* unused */ - - if (movie->header->TotalFrames != 0) { - /* Can't change params after we have already started writing frames. */ - return AVI_ERROR_OPTION; - } - - switch (option_type) { - case AVI_OPTION_TYPE_MAIN: - switch (option) { - case AVI_OPTION_WIDTH: - movie->header->Width = *((int *)opt_data); - movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; - - for (i = 0; i < movie->header->Streams; i++) { - if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = *((int *)opt_data); - movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; - movie->streams[i].sh.right = *((int *)opt_data); - ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = - movie->header->SuggestedBufferSize; - fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); - awrite(movie, - movie->streams[i].sf, - 1, - movie->streams[i].sf_size, - movie->fp, - AVI_BITMAPH); - } - } - - break; - - case AVI_OPTION_HEIGHT: - movie->header->Height = *((int *)opt_data); - movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; - - for (i = 0; i < movie->header->Streams; i++) { - if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { - ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = *((int *)opt_data); - movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; - movie->streams[i].sh.bottom = *((int *)opt_data); - ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = - movie->header->SuggestedBufferSize; - fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); - awrite(movie, - movie->streams[i].sf, - 1, - movie->streams[i].sf_size, - movie->fp, - AVI_BITMAPH); - } - } - - break; - - case AVI_OPTION_QUALITY: - for (i = 0; i < movie->header->Streams; i++) { - if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { - movie->streams[i].sh.Quality = (*((int *)opt_data)) * 100; - fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); - awrite(movie, - movie->streams[i].sf, - 1, - movie->streams[i].sf_size, - movie->fp, - AVI_BITMAPH); - } - } - break; - - case AVI_OPTION_FRAMERATE: - useconds = (int)(1000000 / (*((double *)opt_data))); - if (useconds) { - movie->header->MicroSecPerFrame = useconds; - } - - for (i = 0; i < movie->header->Streams; i++) { - if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { - movie->streams[i].sh.Scale = movie->header->MicroSecPerFrame; - fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); - awrite(movie, - movie->streams[i].sf, - 1, - movie->streams[i].sf_size, - movie->fp, - AVI_BITMAPH); - } - } - break; - } - - fseek(movie->fp, movie->offset_table[0], SEEK_SET); - awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); - - break; - case AVI_OPTION_TYPE_STRH: - break; - case AVI_OPTION_TYPE_STRF: - break; - default: - return AVI_ERROR_OPTION; - } - - return AVI_ERROR_NONE; -} diff --git a/source/blender/avi/intern/avi_rgb.c b/source/blender/avi/intern/avi_rgb.c deleted file mode 100644 index d449556e79b..00000000000 --- a/source/blender/avi/intern/avi_rgb.c +++ /dev/null @@ -1,154 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Converts rgb-type avi-s. - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "AVI_avi.h" -#include "avi_rgb.h" - -#include "IMB_imbuf.h" - -/* implementation */ - -void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - unsigned char *buf; - AviBitmapInfoHeader *bi; - short bits = 32; - - (void)size; /* unused */ - - bi = (AviBitmapInfoHeader *)movie->streams[stream].sf; - if (bi) { - bits = bi->BitCount; - } - - if (bits == 16) { - unsigned short *pxl; - unsigned char *to; -#ifdef __BIG_ENDIAN__ - unsigned char *pxla; -#endif - - buf = imb_alloc_pixels( - movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); - - if (buf) { - size_t y = movie->header->Height; - to = buf; - - while (y--) { - pxl = (unsigned short *)(buffer + y * movie->header->Width * 2); - -#ifdef __BIG_ENDIAN__ - pxla = (unsigned char *)pxl; -#endif - - size_t x = movie->header->Width; - while (x--) { -#ifdef __BIG_ENDIAN__ - int i = pxla[0]; - pxla[0] = pxla[1]; - pxla[1] = i; - - pxla += 2; -#endif - - *(to++) = ((*pxl >> 10) & 0x1f) * 8; - *(to++) = ((*pxl >> 5) & 0x1f) * 8; - *(to++) = (*pxl & 0x1f) * 8; - pxl++; - } - } - } - - MEM_freeN(buffer); - - return buf; - } - else { - buf = imb_alloc_pixels( - movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); - - if (buf) { - size_t rowstride = movie->header->Width * 3; - if ((bits != 16) && (movie->header->Width % 2)) { - rowstride++; - } - - for (size_t y = 0; y < movie->header->Height; y++) { - memcpy(&buf[y * movie->header->Width * 3], - &buffer[((movie->header->Height - 1) - y) * rowstride], - movie->header->Width * 3); - } - - for (size_t y = 0; y < (size_t)movie->header->Height * (size_t)movie->header->Width * 3; - y += 3) { - int i = buf[y]; - buf[y] = buf[y + 2]; - buf[y + 2] = i; - } - } - - MEM_freeN(buffer); - - return buf; - } -} - -void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - unsigned char *buf; - - (void)stream; /* unused */ - - size_t rowstride = movie->header->Width * 3; - /* AVI files has uncompressed lines 4-byte aligned */ - rowstride = (rowstride + 3) & ~3; - - *size = movie->header->Height * rowstride; - buf = MEM_mallocN(*size, "toavirgbbuf"); - - for (size_t y = 0; y < movie->header->Height; y++) { - memcpy(&buf[y * rowstride], - &buffer[((movie->header->Height - 1) - y) * movie->header->Width * 3], - movie->header->Width * 3); - } - - for (size_t y = 0; y < movie->header->Height; y++) { - for (size_t x = 0; x < movie->header->Width * 3; x += 3) { - int i = buf[y * rowstride + x]; - buf[y * rowstride + x] = buf[y * rowstride + x + 2]; - buf[y * rowstride + x + 2] = i; - } - } - - MEM_freeN(buffer); - - return buf; -} diff --git a/source/blender/avi/intern/avi_rgb.h b/source/blender/avi/intern/avi_rgb.h deleted file mode 100644 index 7c8ce590d27..00000000000 --- a/source/blender/avi/intern/avi_rgb.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - */ - -#ifndef __AVI_RGB_H__ -#define __AVI_RGB_H__ - -void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); -void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); - -#endif /* __AVI_RGB_H__ */ diff --git a/source/blender/avi/intern/avi_rgb32.c b/source/blender/avi/intern/avi_rgb32.c deleted file mode 100644 index 3efa4814c70..00000000000 --- a/source/blender/avi/intern/avi_rgb32.c +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - * - * This is external code. Converts between rgb32 and avi. - */ - -#include -#include - -#include "MEM_guardedalloc.h" - -#include "IMB_imbuf.h" - -#include "AVI_avi.h" -#include "avi_rgb32.h" - -void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - unsigned char *buf; - - (void)stream; /* unused */ - - *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 3; - buf = imb_alloc_pixels( - movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromrgb32buf"); - if (!buf) { - return NULL; - } - - size_t rowstridea = movie->header->Width * 3; - size_t rowstrideb = movie->header->Width * 4; - - for (size_t y = 0; y < movie->header->Height; y++) { - for (size_t x = 0; x < movie->header->Width; x++) { - buf[y * rowstridea + x * 3 + 0] = buffer[y * rowstrideb + x * 4 + 3]; - buf[y * rowstridea + x * 3 + 1] = buffer[y * rowstrideb + x * 4 + 2]; - buf[y * rowstridea + x * 3 + 2] = buffer[y * rowstrideb + x * 4 + 1]; - } - } - - MEM_freeN(buffer); - - return buf; -} - -void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) -{ - unsigned char *buf; - unsigned char *to, *from; - - (void)stream; /* unused */ - - *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 4; - buf = imb_alloc_pixels( - movie->header->Height, movie->header->Width, 4, sizeof(unsigned char), "torgb32buf"); - if (!buf) { - return NULL; - } - - memset(buf, 255, *size); - - to = buf; - from = buffer; - size_t i = (size_t)movie->header->Height * (size_t)movie->header->Width; - - while (i--) { - memcpy(to, from, 3); - to += 4; - from += 3; - } - - MEM_freeN(buffer); - - return buf; -} diff --git a/source/blender/avi/intern/avi_rgb32.h b/source/blender/avi/intern/avi_rgb32.h deleted file mode 100644 index eb4b9ca4e21..00000000000 --- a/source/blender/avi/intern/avi_rgb32.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup avi - */ - -#ifndef __AVI_RGB32_H__ -#define __AVI_RGB32_H__ - -void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); -void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); - -#endif /* __AVI_RGB32_H__ */ diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 87ac82aaa55..58f05f65bef 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -487,7 +487,7 @@ endif() if(WITH_CODEC_AVI) list(APPEND INC - ../avi + ../io/avi ) add_definitions(-DWITH_AVI) endif() @@ -608,7 +608,7 @@ endif() if(WITH_ALEMBIC) list(APPEND INC - ../alembic + ../io/alembic ) add_definitions(-DWITH_ALEMBIC) endif() diff --git a/source/blender/blenloader/CMakeLists.txt b/source/blender/blenloader/CMakeLists.txt index 1520c7b82b8..450d3fc2371 100644 --- a/source/blender/blenloader/CMakeLists.txt +++ b/source/blender/blenloader/CMakeLists.txt @@ -87,7 +87,7 @@ endif() if(WITH_ALEMBIC) list(APPEND INC - ../alembic + ../io/alembic ) add_definitions(-DWITH_ALEMBIC) endif() diff --git a/source/blender/collada/AnimationClipExporter.cpp b/source/blender/collada/AnimationClipExporter.cpp deleted file mode 100644 index 5868c24e6cd..00000000000 --- a/source/blender/collada/AnimationClipExporter.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "GeometryExporter.h" -#include "AnimationClipExporter.h" -#include "MaterialExporter.h" - -void AnimationClipExporter::exportAnimationClips(Scene *sce) -{ - openLibrary(); - std::map clips; - - std::vector>::iterator anim_meta_entry; - for (anim_meta_entry = anim_meta.begin(); anim_meta_entry != anim_meta.end(); - ++anim_meta_entry) { - std::vector entry = *anim_meta_entry; - std::string action_id = entry[0]; - std::string action_name = entry[1]; - - std::map::iterator it = clips.find( - action_name); - if (it == clips.end()) { - COLLADASW::ColladaAnimationClip *clip = new COLLADASW::ColladaAnimationClip(action_name); - clips[action_name] = clip; - } - COLLADASW::ColladaAnimationClip *clip = clips[action_name]; - clip->setInstancedAnimation(action_id); - } - - std::map::iterator clips_it; - for (clips_it = clips.begin(); clips_it != clips.end(); clips_it++) { - COLLADASW::ColladaAnimationClip *clip = (COLLADASW::ColladaAnimationClip *)clips_it->second; - addAnimationClip(*clip); - } - - closeLibrary(); -} diff --git a/source/blender/collada/AnimationClipExporter.h b/source/blender/collada/AnimationClipExporter.h deleted file mode 100644 index 25c69fe6b93..00000000000 --- a/source/blender/collada/AnimationClipExporter.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ANIMATIONCLIPEXPORTER_H__ -#define __ANIMATIONCLIPEXPORTER_H__ - -#include -#include -#include - -#include "COLLADASWLibraryAnimationClips.h" - -class AnimationClipExporter : COLLADASW::LibraryAnimationClips { - private: - Depsgraph *depsgraph; - Scene *scene; - COLLADASW::StreamWriter *sw; - BCExportSettings &export_settings; - std::vector> anim_meta; - - public: - AnimationClipExporter(Depsgraph *depsgraph, - COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings, - std::vector> anim_meta) - : COLLADASW::LibraryAnimationClips(sw), - depsgraph(depsgraph), - scene(nullptr), - sw(sw), - export_settings(export_settings), - anim_meta(anim_meta) - { - } - - void exportAnimationClips(Scene *sce); -}; - -#endif /* __ANIMATIONCLIPEXPORTER_H__ */ diff --git a/source/blender/collada/AnimationExporter.cpp b/source/blender/collada/AnimationExporter.cpp deleted file mode 100644 index cd4319e3101..00000000000 --- a/source/blender/collada/AnimationExporter.cpp +++ /dev/null @@ -1,877 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "GeometryExporter.h" -#include "AnimationExporter.h" -#include "AnimationClipExporter.h" -#include "BCAnimationSampler.h" -#include "MaterialExporter.h" -#include "collada_utils.h" - -std::string EMPTY_STRING; - -std::string AnimationExporter::get_axis_name(std::string channel_type, int id) -{ - - static std::map> BC_COLLADA_AXIS_FROM_TYPE = { - {"color", {"R", "G", "B"}}, - {"specular_color", {"R", "G", "B"}}, - {"diffuse_color", {"R", "G", "B"}}, - {"alpha", {"R", "G", "B"}}, - {"scale", {"X", "Y", "Z"}}, - {"location", {"X", "Y", "Z"}}, - {"rotation_euler", {"X", "Y", "Z"}}}; - - std::map>::const_iterator it; - - it = BC_COLLADA_AXIS_FROM_TYPE.find(channel_type); - if (it == BC_COLLADA_AXIS_FROM_TYPE.end()) { - return ""; - } - - const std::vector &subchannel = it->second; - if (id >= subchannel.size()) { - return ""; - } - return subchannel[id]; -} - -bool AnimationExporter::open_animation_container(bool has_container, Object *ob) -{ - if (!has_container) { - char anim_id[200]; - sprintf(anim_id, "action_container-%s", translate_id(id_name(ob)).c_str()); - openAnimation(anim_id, encode_xml(id_name(ob))); - } - return true; -} - -void AnimationExporter::openAnimationWithClip(std::string action_id, std::string action_name) -{ - std::vector anim_meta_entry; - anim_meta_entry.push_back(translate_id(action_id)); - anim_meta_entry.push_back(action_name); - anim_meta.push_back(anim_meta_entry); - - openAnimation(translate_id(action_id), action_name); -} - -void AnimationExporter::close_animation_container(bool has_container) -{ - if (has_container) { - closeAnimation(); - } -} - -bool AnimationExporter::exportAnimations() -{ - Scene *sce = export_settings.get_scene(); - - LinkNode *export_set = this->export_settings.get_export_set(); - bool has_anim_data = bc_has_animations(sce, export_set); - int animation_count = 0; - if (has_anim_data) { - - BCObjectSet animated_subset; - BCAnimationSampler::get_animated_from_export_set(animated_subset, *export_set); - animation_count = animated_subset.size(); - BCAnimationSampler animation_sampler(export_settings, animated_subset); - - try { - animation_sampler.sample_scene(export_settings, /*keyframe_at_end = */ true); - - openLibrary(); - - BCObjectSet::iterator it; - for (it = animated_subset.begin(); it != animated_subset.end(); ++it) { - Object *ob = *it; - exportAnimation(ob, animation_sampler); - } - } - catch (std::invalid_argument &iae) { - fprintf(stderr, "Animation export interrupted"); - fprintf(stderr, "Exception was: %s", iae.what()); - } - - closeLibrary(); - -#if 0 - /* TODO: If all actions shall be exported, we need to call the - * AnimationClipExporter which will figure out which actions - * need to be exported for which objects - */ - if (this->export_settings->include_all_actions) { - AnimationClipExporter ace(eval_ctx, sw, export_settings, anim_meta); - ace.exportAnimationClips(sce); - } -#endif - } - return animation_count; -} - -/* called for each exported object */ -void AnimationExporter::exportAnimation(Object *ob, BCAnimationSampler &sampler) -{ - bool container_is_open = false; - - /* Transform animations (trans, rot, scale). */ - container_is_open = open_animation_container(container_is_open, ob); - - /* Now take care of the Object Animations - * Note: For Armatures the skeletal animation has already been exported (see above) - * However Armatures also can have Object animation. - */ - bool export_as_matrix = this->export_settings.get_animation_transformation_type() == - BC_TRANSFORMATION_TYPE_MATRIX; - - if (export_as_matrix) { - /* export all transform_curves as one single matrix animation */ - export_matrix_animation(ob, sampler); - } - - export_curve_animation_set(ob, sampler, export_as_matrix); - - if (ob->type == OB_ARMATURE && export_as_matrix) { - -#ifdef WITH_MORPH_ANIMATION - /* TODO: This needs to be handled by extra profiles, postponed for now */ - export_morph_animation(ob); -#endif - - /* Export skeletal animation (if any) */ - bArmature *arm = (bArmature *)ob->data; - for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next) { - export_bone_animations_recursive(ob, root_bone, sampler); - } - } - - close_animation_container(container_is_open); -} - -/* - * Export all animation FCurves of an Object. - * - * Note: This uses the keyframes as sample points, - * and exports "baked keyframes" while keeping the tangent information - * of the FCurves intact. This works for simple cases, but breaks - * especially when negative scales are involved in the animation. - * And when parent inverse matrices are involved (when exporting - * object hierarchies) - */ -void AnimationExporter::export_curve_animation_set(Object *ob, - BCAnimationSampler &sampler, - bool export_as_matrix) -{ - BCAnimationCurveMap *curves = sampler.get_curves(ob); - bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); - - BCAnimationCurveMap::iterator it; - for (it = curves->begin(); it != curves->end(); ++it) { - BCAnimationCurve &curve = *it->second; - std::string channel_type = curve.get_channel_type(); - if (channel_type == "rotation_quaternion") { - /* Can not export Quaternion animation in Collada as far as i know) - * Maybe automatically convert to euler rotation? - * Discard for now. */ - continue; - } - - if (export_as_matrix && curve.is_transform_curve()) { - /* All Transform curves will be exported within a single matrix animation, - * see export_matrix_animation() - * No need to export the curves here again. - */ - continue; - } - - if (!keep_flat_curves && !curve.is_animated()) { - continue; - } - - BCAnimationCurve *mcurve = get_modified_export_curve(ob, curve, *curves); - if (mcurve) { - export_curve_animation(ob, *mcurve); - delete mcurve; - } - else { - export_curve_animation(ob, curve); - } - } -} - -void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &sampler) -{ - bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); - - std::vector frames; - sampler.get_object_frames(frames, ob); - if (frames.size() > 0) { - BCMatrixSampleMap samples; - bool is_animated = sampler.get_object_samples(samples, ob); - if (keep_flat_curves || is_animated) { - bAction *action = bc_getSceneObjectAction(ob); - std::string name = encode_xml(id_name(ob)); - std::string action_name = (action == NULL) ? name + "-action" : id_name(action); - std::string channel_type = "transform"; - std::string axis = ""; - std::string id = bc_get_action_id(action_name, name, channel_type, axis); - - std::string target = translate_id(name) + '/' + channel_type; - - BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); - export_collada_matrix_animation( - id, name, target, frames, samples, global_rotation_type, ob->parentinv); - } - } -} - -BC_global_rotation_type AnimationExporter::get_global_rotation_type(Object *ob) -{ - bool is_export_root = this->export_settings.is_export_root(ob); - if (!is_export_root) { - return BC_NO_ROTATION; - } - - bool apply_global_rotation = this->export_settings.get_apply_global_orientation(); - - return (apply_global_rotation) ? BC_DATA_ROTATION : BC_OBJECT_ROTATION; -} - -/* Write bone animations in transform matrix sources. */ -void AnimationExporter::export_bone_animations_recursive(Object *ob, - Bone *bone, - BCAnimationSampler &sampler) -{ - bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); - - std::vector frames; - sampler.get_bone_frames(frames, ob, bone); - - if (frames.size()) { - BCMatrixSampleMap samples; - bool is_animated = sampler.get_bone_samples(samples, ob, bone); - if (keep_flat_curves || is_animated) { - export_bone_animation(ob, bone, frames, samples); - } - } - - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - export_bone_animations_recursive(ob, child, sampler); - } -} - -/** - * In some special cases the exported Curve needs to be replaced - * by a modified curve (for collada purposes) - * This method checks if a conversion is necessary and if applicable - * returns a pointer to the modified BCAnimationCurve. - * IMPORTANT: the modified curve must be deleted by the caller when no longer needed - * if no conversion is needed this method returns a NULL; - */ -BCAnimationCurve *AnimationExporter::get_modified_export_curve(Object *ob, - BCAnimationCurve &curve, - BCAnimationCurveMap &curves) -{ - std::string channel_type = curve.get_channel_type(); - BCAnimationCurve *mcurve = NULL; - if (channel_type == "lens") { - - /* Create an xfov curve */ - - BCCurveKey key(BC_ANIMATION_TYPE_CAMERA, "xfov", 0); - mcurve = new BCAnimationCurve(key, ob); - - /* now tricky part: transform the fcurve */ - BCValueMap lens_values; - curve.get_value_map(lens_values); - - BCAnimationCurve *sensor_curve = NULL; - BCCurveKey sensor_key(BC_ANIMATION_TYPE_CAMERA, "sensor_width", 0); - BCAnimationCurveMap::iterator cit = curves.find(sensor_key); - if (cit != curves.end()) { - sensor_curve = cit->second; - } - - BCValueMap::const_iterator vit; - for (vit = lens_values.begin(); vit != lens_values.end(); ++vit) { - int frame = vit->first; - float lens_value = vit->second; - - float sensor_value; - if (sensor_curve) { - sensor_value = sensor_curve->get_value(frame); - } - else { - sensor_value = ((Camera *)ob->data)->sensor_x; - } - float value = RAD2DEGF(focallength_to_fov(lens_value, sensor_value)); - mcurve->add_value(value, frame); - } - /* to reset the handles */ - mcurve->clean_handles(); - } - return mcurve; -} - -void AnimationExporter::export_curve_animation(Object *ob, BCAnimationCurve &curve) -{ - std::string channel_target = curve.get_channel_target(); - - /* - * Some curves can not be exported as is and need some conversion - * For more information see implementation of get_modified_export_curve() - * note: if mcurve is not NULL then it must be deleted at end of this method; - */ - - int channel_index = curve.get_channel_index(); - /* RGB or XYZ or "" */ - std::string channel_type = curve.get_channel_type(); - std::string axis = get_axis_name(channel_type, channel_index); - - std::string action_name; - bAction *action = bc_getSceneObjectAction(ob); - action_name = (action) ? id_name(action) : "constraint_anim"; - - const std::string curve_name = encode_xml(curve.get_animation_name(ob)); - std::string id = bc_get_action_id(action_name, curve_name, channel_target, axis, "."); - - std::string collada_target = translate_id(curve_name); - - if (curve.is_of_animation_type(BC_ANIMATION_TYPE_MATERIAL)) { - int material_index = curve.get_subindex(); - Material *ma = BKE_object_material_get(ob, material_index + 1); - if (ma) { - collada_target = translate_id(id_name(ma)) + "-effect/common/" + - get_collada_sid(curve, axis); - } - } - else { - collada_target += "/" + get_collada_sid(curve, axis); - } - - BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); - export_collada_curve_animation( - id, curve_name, collada_target, axis, curve, global_rotation_type); -} - -void AnimationExporter::export_bone_animation(Object *ob, - Bone *bone, - BCFrames &frames, - BCMatrixSampleMap &samples) -{ - bAction *action = bc_getSceneObjectAction(ob); - std::string bone_name(bone->name); - std::string name = encode_xml(id_name(ob)); - std::string id = bc_get_action_id(id_name(action), name, bone_name, "pose_matrix"); - std::string target = translate_id(id_name(ob) + "_" + bone_name) + "/transform"; - - BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); - export_collada_matrix_animation( - id, name, target, frames, samples, global_rotation_type, ob->parentinv); -} - -bool AnimationExporter::is_bone_deform_group(Bone *bone) -{ - bool is_def; - /* Check if current bone is deform */ - if ((bone->flag & BONE_NO_DEFORM) == 0) { - return true; - } - /* Check child bones */ - else { - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - /* loop through all the children until deform bone is found, and then return */ - is_def = is_bone_deform_group(child); - if (is_def) { - return true; - } - } - } - /* no deform bone found in children also */ - return false; -} - -void AnimationExporter::export_collada_curve_animation( - std::string id, - std::string name, - std::string collada_target, - std::string axis, - BCAnimationCurve &curve, - BC_global_rotation_type global_rotation_type) -{ - BCFrames frames; - BCValues values; - curve.get_frames(frames); - curve.get_values(values); - std::string channel_target = curve.get_channel_target(); - - fprintf( - stdout, "Export animation curve %s (%d control points)\n", id.c_str(), int(frames.size())); - openAnimation(id, name); - BC_animation_source_type source_type = (curve.is_rotation_curve()) ? BC_SOURCE_TYPE_ANGLE : - BC_SOURCE_TYPE_VALUE; - - std::string input_id = collada_source_from_values( - BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, axis); - std::string output_id = collada_source_from_values( - source_type, COLLADASW::InputSemantic::OUTPUT, values, id, axis); - - bool has_tangents = false; - std::string interpolation_id; - if (this->export_settings.get_keep_smooth_curves()) { - interpolation_id = collada_interpolation_source(curve, id, axis, &has_tangents); - } - else { - interpolation_id = collada_linear_interpolation_source(frames.size(), id); - } - - std::string intangent_id; - std::string outtangent_id; - if (has_tangents) { - intangent_id = collada_tangent_from_curve( - COLLADASW::InputSemantic::IN_TANGENT, curve, id, axis); - outtangent_id = collada_tangent_from_curve( - COLLADASW::InputSemantic::OUT_TANGENT, curve, id, axis); - } - - std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX; - - COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); - - sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id)); - sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id)); - sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, - COLLADABU::URI(EMPTY_STRING, interpolation_id)); - - if (has_tangents) { - sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT, - COLLADABU::URI(EMPTY_STRING, intangent_id)); - sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT, - COLLADABU::URI(EMPTY_STRING, outtangent_id)); - } - - addSampler(sampler); - addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), collada_target); - - closeAnimation(); -} - -void AnimationExporter::export_collada_matrix_animation( - std::string id, - std::string name, - std::string target, - BCFrames &frames, - BCMatrixSampleMap &samples, - BC_global_rotation_type global_rotation_type, - Matrix &parentinv) -{ - fprintf( - stdout, "Export animation matrix %s (%d control points)\n", id.c_str(), int(frames.size())); - - openAnimationWithClip(id, name); - - std::string input_id = collada_source_from_values( - BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, ""); - std::string output_id = collada_source_from_values(samples, id, global_rotation_type, parentinv); - std::string interpolation_id = collada_linear_interpolation_source(frames.size(), id); - - std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX; - COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); - - sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id)); - sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id)); - sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, - COLLADABU::URI(EMPTY_STRING, interpolation_id)); - - /* Matrix animation has no tangents */ - - addSampler(sampler); - addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), target); - - closeAnimation(); -} - -std::string AnimationExporter::get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) -{ - switch (semantic) { - case COLLADASW::InputSemantic::INPUT: - return INPUT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::OUTPUT: - return OUTPUT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::INTERPOLATION: - return INTERPOLATION_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::IN_TANGENT: - return INTANGENT_SOURCE_ID_SUFFIX; - case COLLADASW::InputSemantic::OUT_TANGENT: - return OUTTANGENT_SOURCE_ID_SUFFIX; - default: - break; - } - return ""; -} - -void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList ¶m, - COLLADASW::InputSemantic::Semantics semantic, - bool is_rot, - const std::string axis, - bool transform) -{ - switch (semantic) { - case COLLADASW::InputSemantic::INPUT: - param.push_back("TIME"); - break; - case COLLADASW::InputSemantic::OUTPUT: - if (is_rot) { - param.push_back("ANGLE"); - } - else { - if (axis != "") { - param.push_back(axis); - } - else if (transform) { - param.push_back("TRANSFORM"); - } - else { - /* assumes if axis isn't specified all axises are added */ - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - } - } - break; - case COLLADASW::InputSemantic::IN_TANGENT: - case COLLADASW::InputSemantic::OUT_TANGENT: - param.push_back("X"); - param.push_back("Y"); - break; - default: - break; - } -} - -std::string AnimationExporter::collada_tangent_from_curve( - COLLADASW::InputSemantic::Semantics semantic, - BCAnimationCurve &curve, - const std::string &anim_id, - std::string axis_name) -{ - Scene *scene = this->export_settings.get_scene(); - - std::string channel = curve.get_channel_target(); - - const std::string source_id = anim_id + get_semantic_suffix(semantic); - - bool is_angle = (bc_startswith(channel, "rotation") || channel == "spot_size"); - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(curve.sample_count()); - source.setAccessorStride(2); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, is_angle, axis_name, false); - - source.prepareToAppendValues(); - - const FCurve *fcu = curve.get_fcurve(); - int tangent = (semantic == COLLADASW::InputSemantic::IN_TANGENT) ? 0 : 2; - - for (int i = 0; i < fcu->totvert; i++) { - BezTriple &bezt = fcu->bezt[i]; - - float sampled_time = bezt.vec[tangent][0]; - float sampled_val = bezt.vec[tangent][1]; - - if (is_angle) { - sampled_val = RAD2DEGF(sampled_val); - } - - source.appendValues(FRA2TIME(sampled_time)); - source.appendValues(sampled_val); - } - source.finish(); - return source_id; -} - -std::string AnimationExporter::collada_source_from_values( - BC_animation_source_type source_type, - COLLADASW::InputSemantic::Semantics semantic, - std::vector &values, - const std::string &anim_id, - const std::string axis_name) -{ - BlenderContext &blender_context = this->export_settings.get_blender_context(); - Scene *scene = blender_context.get_scene(); - /* T can be float, int or double */ - - int stride = 1; - int entry_count = values.size() / stride; - std::string source_id = anim_id + get_semantic_suffix(semantic); - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(entry_count); - source.setAccessorStride(stride); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, source_type == BC_SOURCE_TYPE_ANGLE, axis_name, false); - - source.prepareToAppendValues(); - - for (int i = 0; i < entry_count; i++) { - float val = values[i]; - switch (source_type) { - case BC_SOURCE_TYPE_TIMEFRAME: - val = FRA2TIME(val); - break; - case BC_SOURCE_TYPE_ANGLE: - val = RAD2DEGF(val); - break; - default: - break; - } - source.appendValues(val); - } - - source.finish(); - - return source_id; -} - -/* - * Create a collada matrix source for a set of samples - */ -std::string AnimationExporter::collada_source_from_values( - BCMatrixSampleMap &samples, - const std::string &anim_id, - BC_global_rotation_type global_rotation_type, - Matrix &parentinv) -{ - COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; - std::string source_id = anim_id + get_semantic_suffix(semantic); - - COLLADASW::Float4x4Source source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(samples.size()); - source.setAccessorStride(16); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - add_source_parameters(param, semantic, false, "", true); - - source.prepareToAppendValues(); - - BCMatrixSampleMap::iterator it; - /* could be made configurable */ - int precision = (this->export_settings.get_limit_precision()) ? 6 : -1; - for (it = samples.begin(); it != samples.end(); it++) { - BCMatrix sample = BCMatrix(*it->second); - BCMatrix global_transform = this->export_settings.get_global_transform(); - DMatrix daemat; - if (this->export_settings.get_apply_global_orientation()) { - sample.apply_transform(global_transform); - } - else { - sample.add_transform(global_transform); - } - sample.get_matrix(daemat, true, precision); - source.appendValues(daemat); - } - - source.finish(); - return source_id; -} - -std::string AnimationExporter::collada_interpolation_source(const BCAnimationCurve &curve, - const std::string &anim_id, - const std::string axis, - bool *has_tangents) -{ - std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); - - COLLADASW::NameSource source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(curve.sample_count()); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("INTERPOLATION"); - - source.prepareToAppendValues(); - - *has_tangents = false; - - std::vector frames; - curve.get_frames(frames); - - for (unsigned int i = 0; i < curve.sample_count(); i++) { - float frame = frames[i]; - int ipo = curve.get_interpolation_type(frame); - if (ipo == BEZT_IPO_BEZ) { - source.appendValues(BEZIER_NAME); - *has_tangents = true; - } - else if (ipo == BEZT_IPO_CONST) { - source.appendValues(STEP_NAME); - } - else { - /* BEZT_IPO_LIN */ - source.appendValues(LINEAR_NAME); - } - } - /* unsupported? -- HERMITE, CARDINAL, BSPLINE, NURBS */ - - source.finish(); - - return source_id; -} - -std::string AnimationExporter::collada_linear_interpolation_source(int tot, - const std::string &anim_id) -{ - std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); - - COLLADASW::NameSource source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(tot); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("INTERPOLATION"); - - source.prepareToAppendValues(); - - for (int i = 0; i < tot; i++) { - source.appendValues(LINEAR_NAME); - } - - source.finish(); - - return source_id; -} - -const std::string AnimationExporter::get_collada_name(std::string channel_type) const -{ - /* - * Translation table to map FCurve animation types to Collada animation. - * Todo: Maybe we can keep the names from the fcurves here instead of - * mapping. However this is what i found in the old code. So keep - * this map for now. - */ - static std::map BC_CHANNEL_BLENDER_TO_COLLADA = { - {"rotation", "rotation"}, - {"rotation_euler", "rotation"}, - {"rotation_quaternion", "rotation"}, - {"scale", "scale"}, - {"location", "location"}, - - /* Materials */ - {"specular_color", "specular"}, - {"diffuse_color", "diffuse"}, - {"ior", "index_of_refraction"}, - {"specular_hardness", "specular_hardness"}, - {"alpha", "alpha"}, - - /* Lights */ - {"color", "color"}, - {"fall_off_angle", "falloff_angle"}, - {"spot_size", "falloff_angle"}, - {"fall_off_exponent", "falloff_exponent"}, - {"spot_blend", "falloff_exponent"}, - /* Special blender profile (todo: make this more elegant). */ - {"blender/blender_dist", "blender/blender_dist"}, - /* Special blender profile (todo: make this more elegant). */ - {"distance", "blender/blender_dist"}, - - /* Cameras */ - {"lens", "xfov"}, - {"xfov", "xfov"}, - {"xmag", "xmag"}, - {"zfar", "zfar"}, - {"znear", "znear"}, - {"ortho_scale", "xmag"}, - {"clip_end", "zfar"}, - {"clip_start", "znear"}}; - - std::map::iterator name_it = BC_CHANNEL_BLENDER_TO_COLLADA.find( - channel_type); - if (name_it == BC_CHANNEL_BLENDER_TO_COLLADA.end()) { - return ""; - } - std::string tm_name = name_it->second; - return tm_name; -} - -/* - * Assign sid of the animated parameter or transform for rotation, - * axis name is always appended and the value of append_axis is ignored - */ -std::string AnimationExporter::get_collada_sid(const BCAnimationCurve &curve, - const std::string axis_name) -{ - std::string channel_target = curve.get_channel_target(); - std::string channel_type = curve.get_channel_type(); - std::string tm_name = get_collada_name(channel_type); - - bool is_angle = curve.is_rotation_curve(); - - if (tm_name.size()) { - if (is_angle) { - return tm_name + std::string(axis_name) + ".ANGLE"; - } - else if (axis_name != "") { - return tm_name + "." + std::string(axis_name); - } - else { - return tm_name; - } - } - - return tm_name; -} - -#ifdef WITH_MORPH_ANIMATION -/* TODO: This function needs to be implemented similar to the material animation export - * So we have to update BCSample for this to work. */ -void AnimationExporter::export_morph_animation(Object *ob, BCAnimationSampler &sampler) -{ - FCurve *fcu; - Key *key = BKE_key_from_object(ob); - if (!key) { - return; - } - - if (key->adt && key->adt->action) { - fcu = (FCurve *)key->adt->action->curves.first; - - while (fcu) { - BC_animation_transform_type tm_type = get_transform_type(fcu->rna_path); - - create_keyframed_animation(ob, fcu, tm_type, true, sampler); - - fcu = fcu->next; - } - } -} -#endif diff --git a/source/blender/collada/AnimationExporter.h b/source/blender/collada/AnimationExporter.h deleted file mode 100644 index 64751ec5327..00000000000 --- a/source/blender/collada/AnimationExporter.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ANIMATIONEXPORTER_H__ -#define __ANIMATIONEXPORTER_H__ - -#include -#include -#include - -#include "BCAnimationCurve.h" - -extern "C" { -#include "DNA_scene_types.h" -#include "DNA_object_types.h" -#include "DNA_anim_types.h" -#include "DNA_action_types.h" -#include "DNA_curve_types.h" -#include "DNA_light_types.h" -#include "DNA_camera_types.h" -#include "DNA_armature_types.h" -#include "DNA_material_types.h" -#include "DNA_constraint_types.h" -#include "DNA_scene_types.h" - -#include "BLI_math.h" -#include "BLI_string.h" -#include "BLI_listbase.h" -#include "BLI_utildefines.h" - -#include "BKE_fcurve.h" -#include "BKE_animsys.h" -#include "BKE_scene.h" -#include "BKE_action.h" // pose functions -#include "BKE_armature.h" -#include "BKE_object.h" -#include "BKE_constraint.h" -#include "BIK_api.h" -#include "ED_object.h" -} - -#include "MEM_guardedalloc.h" - -#include "RNA_access.h" - -#include "COLLADASWSource.h" -#include "COLLADASWInstanceGeometry.h" -#include "COLLADASWInputList.h" -#include "COLLADASWPrimitves.h" -#include "COLLADASWVertices.h" -#include "COLLADASWLibraryAnimations.h" -#include "COLLADASWParamTemplate.h" -#include "COLLADASWParamBase.h" -#include "COLLADASWSampler.h" -#include "COLLADASWConstants.h" -#include "COLLADASWBaseInputElement.h" - -#include "EffectExporter.h" -#include "BCAnimationSampler.h" -#include "collada_internal.h" - -#include "IK_solver.h" - -#include -#include -#include // std::find - -typedef enum BC_animation_source_type { - BC_SOURCE_TYPE_VALUE, - BC_SOURCE_TYPE_ANGLE, - BC_SOURCE_TYPE_TIMEFRAME, -} BC_animation_source_type; - -typedef enum BC_global_rotation_type { - BC_NO_ROTATION, - BC_OBJECT_ROTATION, - BC_DATA_ROTATION -} BC_global_rotation_type; - -class AnimationExporter : COLLADASW::LibraryAnimations { - private: - COLLADASW::StreamWriter *sw; - BCExportSettings &export_settings; - - BC_global_rotation_type get_global_rotation_type(Object *ob); - - public: - AnimationExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) - : COLLADASW::LibraryAnimations(sw), sw(sw), export_settings(export_settings) - { - } - - bool exportAnimations(); - - // called for each exported object - void operator()(Object *ob); - - protected: - void export_object_constraint_animation(Object *ob); - - void export_morph_animation(Object *ob); - - void write_bone_animation_matrix(Object *ob_arm, Bone *bone); - - void write_bone_animation(Object *ob_arm, Bone *bone); - - void sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type); - - void sample_and_write_bone_animation_matrix(Object *ob_arm, Bone *bone); - - void sample_animation(float *v, - std::vector &frames, - int type, - Bone *bone, - Object *ob_arm, - bPoseChannel *pChan); - - void sample_animation(std::vector &mats, - std::vector &frames, - Bone *bone, - Object *ob_arm, - bPoseChannel *pChan); - - // dae_bone_animation -> add_bone_animation - // (blend this into dae_bone_animation) - void dae_bone_animation(std::vector &fra, - float *v, - int tm_type, - int axis, - std::string ob_name, - std::string bone_name); - - void dae_baked_animation(std::vector &fra, Object *ob_arm, Bone *bone); - - void dae_baked_object_animation(std::vector &fra, Object *ob); - - float convert_time(float frame); - - float convert_angle(float angle); - - std::vector> anim_meta; - - /* Main entry point into Animation export (called for each exported object) */ - void exportAnimation(Object *ob, BCAnimationSampler &sampler); - - /* export animation as separate trans/rot/scale curves */ - void export_curve_animation_set(Object *ob, BCAnimationSampler &sampler, bool export_tm_curves); - - /* export one single curve */ - void export_curve_animation(Object *ob, BCAnimationCurve &curve); - - /* export animation as matrix data */ - void export_matrix_animation(Object *ob, BCAnimationSampler &sampler); - - /* step through the bone hierarchy */ - void export_bone_animations_recursive(Object *ob_arm, Bone *bone, BCAnimationSampler &sampler); - - /* Export for one bone */ - void export_bone_animation(Object *ob, Bone *bone, BCFrames &frames, BCMatrixSampleMap &outmats); - - /* call to the low level collada exporter */ - void export_collada_curve_animation(std::string id, - std::string name, - std::string target, - std::string axis, - BCAnimationCurve &curve, - BC_global_rotation_type global_rotation_type); - - /* call to the low level collada exporter */ - void export_collada_matrix_animation(std::string id, - std::string name, - std::string target, - BCFrames &frames, - BCMatrixSampleMap &outmats, - BC_global_rotation_type global_rotation_type, - Matrix &parentinv); - - BCAnimationCurve *get_modified_export_curve(Object *ob, - BCAnimationCurve &curve, - BCAnimationCurveMap &curves); - - /* Helper functions */ - void openAnimationWithClip(std::string id, std::string name); - bool open_animation_container(bool has_container, Object *ob); - void close_animation_container(bool has_container); - - /* Input and Output sources (single valued) */ - std::string collada_source_from_values(BC_animation_source_type tm_channel, - COLLADASW::InputSemantic::Semantics semantic, - std::vector &values, - const std::string &anim_id, - const std::string axis_name); - - /* Output sources (matrix data) */ - std::string collada_source_from_values(BCMatrixSampleMap &samples, - const std::string &anim_id, - BC_global_rotation_type global_rotation_type, - Matrix &parentinv); - - /* Interpolation sources */ - std::string collada_linear_interpolation_source(int tot, const std::string &anim_id); - - /* source ID = animation_name + semantic_suffix */ - - std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic); - - void add_source_parameters(COLLADASW::SourceBase::ParameterNameList ¶m, - COLLADASW::InputSemantic::Semantics semantic, - bool is_rot, - const std::string axis, - bool transform); - - int get_point_in_curve(BCBezTriple &bezt, - COLLADASW::InputSemantic::Semantics semantic, - bool is_angle, - float *values); - int get_point_in_curve(const BCAnimationCurve &curve, - float sample_frame, - COLLADASW::InputSemantic::Semantics semantic, - bool is_angle, - float *values); - - std::string collada_tangent_from_curve(COLLADASW::InputSemantic::Semantics semantic, - BCAnimationCurve &curve, - const std::string &anim_id, - const std::string axis_name); - - std::string collada_interpolation_source(const BCAnimationCurve &curve, - const std::string &anim_id, - std::string axis_name, - bool *has_tangents); - - std::string get_axis_name(std::string channel, int id); - const std::string get_collada_name(std::string channel_target) const; - std::string get_collada_sid(const BCAnimationCurve &curve, const std::string axis_name); - - /* ===================================== */ - /* Currently unused or not (yet?) needed */ - /* ===================================== */ - - bool is_bone_deform_group(Bone *bone); - -#if 0 - BC_animation_transform_type _get_transform_type(const std::string path); - void get_eul_source_for_quat(std::vector &cache, Object *ob); -#endif - -#ifdef WITH_MORPH_ANIMATION - void export_morph_animation(Object *ob, BCAnimationSampler &sampler); -#endif -}; - -#endif /* __ANIMATIONEXPORTER_H__ */ diff --git a/source/blender/collada/AnimationImporter.cpp b/source/blender/collada/AnimationImporter.cpp deleted file mode 100644 index 715cd9e1a12..00000000000 --- a/source/blender/collada/AnimationImporter.cpp +++ /dev/null @@ -1,2232 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "DNA_armature_types.h" - -#include "ED_keyframing.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_string.h" -#include "BLI_string_utils.h" - -#include "BLT_translation.h" - -#include "BKE_action.h" -#include "BKE_armature.h" -#include "BKE_fcurve.h" -#include "BKE_object.h" - -#include "MEM_guardedalloc.h" - -#include "collada_utils.h" -#include "AnimationImporter.h" -#include "ArmatureImporter.h" -#include "MaterialExporter.h" - -#include - -/* first try node name, if not available (since is optional), fall back to original id */ -template static const char *bc_get_joint_name(T *node) -{ - const std::string &id = node->getName(); - return id.size() ? id.c_str() : node->getOriginalId().c_str(); -} - -FCurve *AnimationImporter::create_fcurve(int array_index, const char *rna_path) -{ - FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); - fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); - fcu->array_index = array_index; - return fcu; -} - -void AnimationImporter::add_bezt(FCurve *fcu, - float frame, - float value, - eBezTriple_Interpolation ipo) -{ - // float fps = (float)FPS; - BezTriple bez; - memset(&bez, 0, sizeof(BezTriple)); - bez.vec[1][0] = frame; - bez.vec[1][1] = value; - bez.ipo = ipo; /* use default interpolation mode here... */ - bez.f1 = bez.f2 = bez.f3 = SELECT; - bez.h1 = bez.h2 = HD_AUTO; - insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); -} - -/* create one or several fcurves depending on the number of parameters being animated */ -void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) -{ - COLLADAFW::FloatOrDoubleArray &input = curve->getInputValues(); - COLLADAFW::FloatOrDoubleArray &output = curve->getOutputValues(); - - float fps = (float)FPS; - size_t dim = curve->getOutDimension(); - unsigned int i; - - std::vector &fcurves = curve_map[curve->getUniqueId()]; - - switch (dim) { - case 1: /* X, Y, Z or angle */ - case 3: /* XYZ */ - case 4: - case 16: /* matrix */ - { - for (i = 0; i < dim; i++) { - FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); - - fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); - fcu->array_index = 0; - fcu->auto_smoothing = U.auto_smoothing_new; - - for (unsigned int j = 0; j < curve->getKeyCount(); j++) { - BezTriple bez; - memset(&bez, 0, sizeof(BezTriple)); - - /* input, output */ - bez.vec[1][0] = bc_get_float_value(input, j) * fps; - bez.vec[1][1] = bc_get_float_value(output, j * dim + i); - bez.h1 = bez.h2 = HD_AUTO; - - if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER || - curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_STEP) { - COLLADAFW::FloatOrDoubleArray &intan = curve->getInTangentValues(); - COLLADAFW::FloatOrDoubleArray &outtan = curve->getOutTangentValues(); - - /* intangent */ - unsigned int index = 2 * (j * dim + i); - bez.vec[0][0] = bc_get_float_value(intan, index) * fps; - bez.vec[0][1] = bc_get_float_value(intan, index + 1); - - /* outtangent */ - bez.vec[2][0] = bc_get_float_value(outtan, index) * fps; - bez.vec[2][1] = bc_get_float_value(outtan, index + 1); - if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER) { - bez.ipo = BEZT_IPO_BEZ; - bez.h1 = bez.h2 = HD_AUTO_ANIM; - } - else { - bez.ipo = BEZT_IPO_CONST; - } - } - else { - bez.ipo = BEZT_IPO_LIN; - } -#if 0 - bez.ipo = U.ipo_new; /* use default interpolation mode here... */ -#endif - bez.f1 = bez.f2 = bez.f3 = SELECT; - - insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - } - - calchandles_fcurve(fcu); - - fcurves.push_back(fcu); - unused_curves.push_back(fcu); - } - } break; - default: - fprintf(stderr, - "Output dimension of %d is not yet supported (animation id = %s)\n", - (int)dim, - curve->getOriginalId().c_str()); - } -} - -void AnimationImporter::fcurve_deg_to_rad(FCurve *cu) -{ - for (unsigned int i = 0; i < cu->totvert; i++) { - /* TODO convert handles too */ - cu->bezt[i].vec[1][1] *= DEG2RADF(1.0f); - cu->bezt[i].vec[0][1] *= DEG2RADF(1.0f); - cu->bezt[i].vec[2][1] *= DEG2RADF(1.0f); - } -} - -void AnimationImporter::fcurve_scale(FCurve *cu, int scale) -{ - for (unsigned int i = 0; i < cu->totvert; i++) { - /* TODO convert handles too */ - cu->bezt[i].vec[1][1] *= scale; - cu->bezt[i].vec[0][1] *= scale; - cu->bezt[i].vec[2][1] *= scale; - } -} - -void AnimationImporter::fcurve_is_used(FCurve *fcu) -{ - unused_curves.erase(std::remove(unused_curves.begin(), unused_curves.end(), fcu), - unused_curves.end()); -} - -void AnimationImporter::add_fcurves_to_object(Main *bmain, - Object *ob, - std::vector &curves, - char *rna_path, - int array_index, - Animation *animated) -{ - bAction *act; - - if (!ob->adt || !ob->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&ob->id); - } - else { - act = ob->adt->action; - } - - std::vector::iterator it; - int i; - -#if 0 - char *p = strstr(rna_path, "rotation_euler"); - bool is_rotation = p && *(p + strlen("rotation_euler")) == '\0'; - - /* convert degrees to radians for rotation */ - if (is_rotation) { - fcurve_deg_to_rad(fcu); - } -#endif - - for (it = curves.begin(), i = 0; it != curves.end(); it++, i++) { - FCurve *fcu = *it; - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); - - if (array_index == -1) { - fcu->array_index = i; - } - else { - fcu->array_index = array_index; - } - - if (ob->type == OB_ARMATURE) { - bActionGroup *grp = NULL; - const char *bone_name = bc_get_joint_name(animated->node); - - if (bone_name) { - /* try to find group */ - grp = BKE_action_group_find_name(act, bone_name); - - /* no matching groups, so add one */ - if (grp == NULL) { - /* Add a new group, and make it active */ - grp = (bActionGroup *)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); - - grp->flag = AGRP_SELECTED; - BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); - - BLI_addtail(&act->groups, grp); - BLI_uniquename(&act->groups, - grp, - CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), - '.', - offsetof(bActionGroup, name), - 64); - } - - /* add F-Curve to group */ - action_groups_add_channel(act, grp, fcu); - fcurve_is_used(fcu); - } -#if 0 - if (is_rotation) { - fcurves_actionGroup_map[grp].push_back(fcu); - } -#endif - } - else { - BLI_addtail(&act->curves, fcu); - fcurve_is_used(fcu); - } - } -} - -AnimationImporter::~AnimationImporter() -{ - /* free unused FCurves */ - for (std::vector::iterator it = unused_curves.begin(); it != unused_curves.end(); - it++) { - free_fcurve(*it); - } - - if (unused_curves.size()) { - fprintf(stderr, "removed %d unused curves\n", (int)unused_curves.size()); - } -} - -bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim) -{ - if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) { - COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve *)anim; - - /* XXX Don't know if it's necessary - * Should we check outPhysicalDimension? */ - if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) { - fprintf(stderr, "Inputs physical dimension is not time.\n"); - return true; - } - - /* a curve can have mixed interpolation type, - * in this case curve->getInterpolationTypes returns a list of interpolation types per key */ - COLLADAFW::AnimationCurve::InterpolationType interp = curve->getInterpolationType(); - - if (interp != COLLADAFW::AnimationCurve::INTERPOLATION_MIXED) { - switch (interp) { - case COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR: - case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER: - case COLLADAFW::AnimationCurve::INTERPOLATION_STEP: - animation_to_fcurves(curve); - break; - default: - /* TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types */ - fprintf(stderr, - "CARDINAL, HERMITE and BSPLINE anim interpolation types not supported yet.\n"); - break; - } - } - else { - /* not supported yet */ - fprintf(stderr, "MIXED anim interpolation type is not supported yet.\n"); - } - } - else { - fprintf(stderr, "FORMULA animation type is not supported yet.\n"); - } - - return true; -} - -/* called on post-process stage after writeVisualScenes */ -bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *animlist) -{ - const COLLADAFW::UniqueId &animlist_id = animlist->getUniqueId(); - animlist_map[animlist_id] = animlist; - -#if 0 - - /* should not happen */ - if (uid_animated_map.find(animlist_id) == uid_animated_map.end()) { - return true; - } - - /* for bones rna_path is like: pose.bones["bone-name"].rotation */ - -#endif - - return true; -} - -/* \todo refactor read_node_transform to not automatically apply anything, - * but rather return the transform matrix, so caller can do with it what is - * necessary. Same for \ref get_node_mat */ -void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob) -{ - float mat[4][4]; - TransformReader::get_node_mat(mat, node, &uid_animated_map, ob); - if (ob) { - copy_m4_m4(ob->obmat, mat); - BKE_object_apply_mat4(ob, ob->obmat, 0, 0); - } -} - -#if 0 -virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act) -{ - bActionGroup *grp; - int i; - - for (grp = (bActionGroup *)act->groups.first; grp; grp = grp->next) { - - FCurve *eulcu[3] = {NULL, NULL, NULL}; - - if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) { - continue; - } - - std::vector &rot_fcurves = fcurves_actionGroup_map[grp]; - - if (rot_fcurves.size() > 3) { - continue; - } - - for (i = 0; i < rot_fcurves.size(); i++) { - eulcu[rot_fcurves[i]->array_index] = rot_fcurves[i]; - } - - char joint_path[100]; - char rna_path[100]; - - BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name); - BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path); - - FCurve *quatcu[4] = { - create_fcurve(0, rna_path), - create_fcurve(1, rna_path), - create_fcurve(2, rna_path), - create_fcurve(3, rna_path), - }; - - bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, grp->name); - - float m4[4][4], irest[3][3]; - invert_m4_m4(m4, chan->bone->arm_mat); - copy_m3_m4(irest, m4); - - for (i = 0; i < 3; i++) { - - FCurve *cu = eulcu[i]; - - if (!cu) { - continue; - } - - for (int j = 0; j < cu->totvert; j++) { - float frame = cu->bezt[j].vec[1][0]; - - float eul[3] = { - eulcu[0] ? evaluate_fcurve(eulcu[0], frame) : 0.0f, - eulcu[1] ? evaluate_fcurve(eulcu[1], frame) : 0.0f, - eulcu[2] ? evaluate_fcurve(eulcu[2], frame) : 0.0f, - }; - - /* make eul relative to bone rest pose */ - float rot[3][3], rel[3][3], quat[4]; - -# if 0 - eul_to_mat3(rot, eul); - mul_m3_m3m3(rel, irest, rot); - mat3_to_quat(quat, rel); -# endif - - eul_to_quat(quat, eul); - - for (int k = 0; k < 4; k++) { - create_bezt(quatcu[k], frame, quat[k], U.ipo_new); - } - } - } - - /* now replace old Euler curves */ - - for (i = 0; i < 3; i++) { - if (!eulcu[i]) { - continue; - } - - action_groups_remove_channel(act, eulcu[i]); - free_fcurve(eulcu[i]); - } - - chan->rotmode = ROT_MODE_QUAT; - - for (i = 0; i < 4; i++) { - action_groups_add_channel(act, grp, quatcu[i]); - } - } - - bPoseChannel *pchan; - for (pchan = (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan = pchan->next) { - pchan->rotmode = ROT_MODE_QUAT; - } -} -#endif - -/* sets the rna_path and array index to curve */ -void AnimationImporter::modify_fcurve(std::vector *curves, - const char *rna_path, - int array_index, - int scale) -{ - std::vector::iterator it; - int i; - for (it = curves->begin(), i = 0; it != curves->end(); it++, i++) { - FCurve *fcu = *it; - fcu->rna_path = BLI_strdup(rna_path); - - if (array_index == -1) { - fcu->array_index = i; - } - else { - fcu->array_index = array_index; - } - - if (scale != 1) { - fcurve_scale(fcu, scale); - } - - fcurve_is_used(fcu); - } -} - -void AnimationImporter::unused_fcurve(std::vector *curves) -{ - /* when an error happens and we can't actually use curve remove it from unused_curves */ - std::vector::iterator it; - for (it = curves->begin(); it != curves->end(); it++) { - FCurve *fcu = *it; - fcurve_is_used(fcu); - } -} - -void AnimationImporter::find_frames(std::vector *frames, std::vector *curves) -{ - std::vector::iterator iter; - for (iter = curves->begin(); iter != curves->end(); iter++) { - FCurve *fcu = *iter; - - for (unsigned int k = 0; k < fcu->totvert; k++) { - /* get frame value from bezTriple */ - float fra = fcu->bezt[k].vec[1][0]; - /* if frame already not added add frame to frames */ - if (std::find(frames->begin(), frames->end(), fra) == frames->end()) { - frames->push_back(fra); - } - } - } -} - -static int get_animation_axis_index(const COLLADABU::Math::Vector3 &axis) -{ - int index; - if (COLLADABU::Math::Vector3::UNIT_X == axis) { - index = 0; - } - else if (COLLADABU::Math::Vector3::UNIT_Y == axis) { - index = 1; - } - else if (COLLADABU::Math::Vector3::UNIT_Z == axis) { - index = 2; - } - else { - index = -1; - } - return index; -} - -/* creates the rna_paths and array indices of fcurves from animations using transformation and - * bound animation class of each animation. */ -void AnimationImporter::Assign_transform_animations( - COLLADAFW::Transformation *transform, - const COLLADAFW::AnimationList::AnimationBinding *binding, - std::vector *curves, - bool is_joint, - char *joint_path) -{ - COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); - bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; - bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; - - /* to check if the no of curves are valid */ - bool xyz = ((tm_type == COLLADAFW::Transformation::TRANSLATE || - tm_type == COLLADAFW::Transformation::SCALE) && - binding->animationClass == COLLADAFW::AnimationList::POSITION_XYZ); - - if (!((!xyz && curves->size() == 1) || (xyz && curves->size() == 3) || is_matrix)) { - fprintf(stderr, "expected %d curves, got %d\n", xyz ? 3 : 1, (int)curves->size()); - return; - } - - char rna_path[100]; - - switch (tm_type) { - case COLLADAFW::Transformation::TRANSLATE: - case COLLADAFW::Transformation::SCALE: { - bool loc = tm_type == COLLADAFW::Transformation::TRANSLATE; - if (is_joint) { - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, loc ? "location" : "scale"); - } - else { - BLI_strncpy(rna_path, loc ? "location" : "scale", sizeof(rna_path)); - } - - switch (binding->animationClass) { - case COLLADAFW::AnimationList::POSITION_X: - modify_fcurve(curves, rna_path, 0); - break; - case COLLADAFW::AnimationList::POSITION_Y: - modify_fcurve(curves, rna_path, 1); - break; - case COLLADAFW::AnimationList::POSITION_Z: - modify_fcurve(curves, rna_path, 2); - break; - case COLLADAFW::AnimationList::POSITION_XYZ: - modify_fcurve(curves, rna_path, -1); - break; - default: - unused_fcurve(curves); - fprintf(stderr, - "AnimationClass %d is not supported for %s.\n", - binding->animationClass, - loc ? "TRANSLATE" : "SCALE"); - } - break; - } - - case COLLADAFW::Transformation::ROTATE: { - if (is_joint) { - BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_euler", joint_path); - } - else { - BLI_strncpy(rna_path, "rotation_euler", sizeof(rna_path)); - } - std::vector::iterator iter; - for (iter = curves->begin(); iter != curves->end(); iter++) { - FCurve *fcu = *iter; - - /* if transform is rotation the fcurves values must be turned in to radian. */ - if (is_rotation) { - fcurve_deg_to_rad(fcu); - } - } - COLLADAFW::Rotate *rot = (COLLADAFW::Rotate *)transform; - COLLADABU::Math::Vector3 &axis = rot->getRotationAxis(); - - switch (binding->animationClass) { - case COLLADAFW::AnimationList::ANGLE: { - int axis_index = get_animation_axis_index(axis); - if (axis_index >= 0) { - modify_fcurve(curves, rna_path, axis_index); - } - else { - unused_fcurve(curves); - } - } break; - case COLLADAFW::AnimationList::AXISANGLE: - /* TODO convert axis-angle to quat? or XYZ? */ - default: - unused_fcurve(curves); - fprintf(stderr, - "AnimationClass %d is not supported for ROTATE transformation.\n", - binding->animationClass); - } - break; - } - - case COLLADAFW::Transformation::MATRIX: -#if 0 - { - COLLADAFW::Matrix *mat = (COLLADAFW::Matrix *)transform; - COLLADABU::Math::Matrix4 mat4 = mat->getMatrix(); - switch (binding->animationClass) { - case COLLADAFW::AnimationList::TRANSFORM: - } - } -#endif - unused_fcurve(curves); - break; - case COLLADAFW::Transformation::SKEW: - case COLLADAFW::Transformation::LOOKAT: - unused_fcurve(curves); - fprintf(stderr, "Animation of SKEW and LOOKAT transformations is not supported yet.\n"); - break; - } -} - -/* creates the rna_paths and array indices of fcurves from animations using color and bound - * animation class of each animation. */ -void AnimationImporter::Assign_color_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const char *anim_type) -{ - char rna_path[100]; - BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); - - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - if (animlist == NULL) { - fprintf(stderr, - "Collada: No animlist found for ID: %s of type %s\n", - listid.toAscii().c_str(), - anim_type); - return; - } - - const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); - /* all the curves belonging to the current binding */ - std::vector animcurves; - for (unsigned int j = 0; j < bindings.getCount(); j++) { - animcurves = curve_map[bindings[j].animation]; - - switch (bindings[j].animationClass) { - case COLLADAFW::AnimationList::COLOR_R: - modify_fcurve(&animcurves, rna_path, 0); - break; - case COLLADAFW::AnimationList::COLOR_G: - modify_fcurve(&animcurves, rna_path, 1); - break; - case COLLADAFW::AnimationList::COLOR_B: - modify_fcurve(&animcurves, rna_path, 2); - break; - case COLLADAFW::AnimationList::COLOR_RGB: - case COLLADAFW::AnimationList::COLOR_RGBA: /* to do-> set intensity */ - modify_fcurve(&animcurves, rna_path, -1); - break; - - default: - unused_fcurve(&animcurves); - fprintf(stderr, - "AnimationClass %d is not supported for %s.\n", - bindings[j].animationClass, - "COLOR"); - } - - std::vector::iterator iter; - /* Add the curves of the current animation to the object */ - for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { - FCurve *fcu = *iter; - BLI_addtail(AnimCurves, fcu); - fcurve_is_used(fcu); - } - } -} - -void AnimationImporter::Assign_float_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const char *anim_type) -{ - char rna_path[100]; - if (animlist_map.find(listid) == animlist_map.end()) { - return; - } - else { - /* anim_type has animations */ - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); - /* all the curves belonging to the current binding */ - std::vector animcurves; - for (unsigned int j = 0; j < bindings.getCount(); j++) { - animcurves = curve_map[bindings[j].animation]; - - BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); - modify_fcurve(&animcurves, rna_path, 0); - std::vector::iterator iter; - /* Add the curves of the current animation to the object */ - for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { - FCurve *fcu = *iter; - /* All anim_types whose values are to be converted from Degree to Radians can be ORed here - */ - if (STREQ("spot_size", anim_type)) { - /* NOTE: Do NOT convert if imported file was made by blender <= 2.69.10 - * Reason: old blender versions stored spot_size in radians (was a bug) - */ - if (this->import_from_version == "" || - BLI_strcasecmp_natural(this->import_from_version.c_str(), "2.69.10") != -1) { - fcurve_deg_to_rad(fcu); - } - } - /** XXX What About animtype "rotation" ? */ - - BLI_addtail(AnimCurves, fcu); - fcurve_is_used(fcu); - } - } - } -} - -float AnimationImporter::convert_to_focal_length(float in_xfov, - int fov_type, - float aspect, - float sensorx) -{ - /* NOTE: Needs more testing (As we currently have no official test data for this) */ - float xfov = (fov_type == CAMERA_YFOV) ? - (2.0f * atanf(aspect * tanf(DEG2RADF(in_xfov) * 0.5f))) : - DEG2RADF(in_xfov); - return fov_to_focallength(xfov, sensorx); -} - -/* - * Lens animations must be stored in COLLADA by using FOV, - * while blender internally uses focal length. - * The imported animation curves must be converted appropriately. - */ -void AnimationImporter::Assign_lens_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const double aspect, - Camera *cam, - const char *anim_type, - int fov_type) -{ - char rna_path[100]; - if (animlist_map.find(listid) == animlist_map.end()) { - return; - } - else { - /* anim_type has animations */ - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); - /* all the curves belonging to the current binding */ - std::vector animcurves; - for (unsigned int j = 0; j < bindings.getCount(); j++) { - animcurves = curve_map[bindings[j].animation]; - - BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); - - modify_fcurve(&animcurves, rna_path, 0); - std::vector::iterator iter; - /* Add the curves of the current animation to the object */ - for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { - FCurve *fcu = *iter; - - for (unsigned int i = 0; i < fcu->totvert; i++) { - fcu->bezt[i].vec[0][1] = convert_to_focal_length( - fcu->bezt[i].vec[0][1], fov_type, aspect, cam->sensor_x); - fcu->bezt[i].vec[1][1] = convert_to_focal_length( - fcu->bezt[i].vec[1][1], fov_type, aspect, cam->sensor_x); - fcu->bezt[i].vec[2][1] = convert_to_focal_length( - fcu->bezt[i].vec[2][1], fov_type, aspect, cam->sensor_x); - } - - BLI_addtail(AnimCurves, fcu); - fcurve_is_used(fcu); - } - } - } -} - -void AnimationImporter::apply_matrix_curves(Object *ob, - std::vector &animcurves, - COLLADAFW::Node *root, - COLLADAFW::Node *node, - COLLADAFW::Transformation *tm) -{ - bool is_joint = node->getType() == COLLADAFW::Node::JOINT; - const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; - char joint_path[200]; - if (is_joint) { - armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); - } - - std::vector frames; - find_frames(&frames, &animcurves); - - float irest_dae[4][4]; - float rest[4][4], irest[4][4]; - - if (is_joint) { - get_joint_rest_mat(irest_dae, root, node); - invert_m4(irest_dae); - - Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); - if (!bone) { - fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); - return; - } - - unit_m4(rest); - copy_m4_m4(rest, bone->arm_mat); - invert_m4_m4(irest, rest); - } - /* new curves to assign matrix transform animation */ - FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale */ - unsigned int totcu = 10; - const char *tm_str = NULL; - char rna_path[200]; - for (int i = 0; i < totcu; i++) { - - int axis = i; - - if (i < 4) { - tm_str = "rotation_quaternion"; - axis = i; - } - else if (i < 7) { - tm_str = "location"; - axis = i - 4; - } - else { - tm_str = "scale"; - axis = i - 7; - } - - if (is_joint) { - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); - } - else { - BLI_strncpy(rna_path, tm_str, sizeof(rna_path)); - } - newcu[i] = create_fcurve(axis, rna_path); - newcu[i]->totvert = frames.size(); - } - - if (frames.size() == 0) { - return; - } - - std::sort(frames.begin(), frames.end()); - - std::vector::iterator it; - -#if 0 - float qref[4]; - unit_qt(qref); -#endif - - /* sample values at each frame */ - for (it = frames.begin(); it != frames.end(); it++) { - float fra = *it; - - float mat[4][4]; - float matfra[4][4]; - - unit_m4(matfra); - - /* calc object-space mat */ - evaluate_transform_at_frame(matfra, node, fra); - - /* for joints, we need a special matrix */ - if (is_joint) { - /* special matrix: iR * M * iR_dae * R - * where R, iR are bone rest and inverse rest mats in world space (Blender bones), - * iR_dae is joint inverse rest matrix (DAE) - * and M is an evaluated joint world-space matrix (DAE) */ - float temp[4][4], par[4][4]; - - /* calc M */ - calc_joint_parent_mat_rest(par, NULL, root, node); - mul_m4_m4m4(temp, par, matfra); - -#if 0 - evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); -#endif - - /* calc special matrix */ - mul_m4_series(mat, irest, temp, irest_dae, rest); - } - else { - copy_m4_m4(mat, matfra); - } - - float rot[4], loc[3], scale[3]; - mat4_decompose(loc, rot, scale, mat); - - /* add keys */ - for (int i = 0; i < totcu; i++) { - if (i < 4) { - add_bezt(newcu[i], fra, rot[i]); - } - else if (i < 7) { - add_bezt(newcu[i], fra, loc[i - 4]); - } - else { - add_bezt(newcu[i], fra, scale[i - 7]); - } - } - } - Main *bmain = CTX_data_main(mContext); - ED_id_action_ensure(bmain, (ID *)&ob->id); - - ListBase *curves = &ob->adt->action->curves; - - /* add curves */ - for (int i = 0; i < totcu; i++) { - if (is_joint) { - add_bone_fcurve(ob, node, newcu[i]); - } - else { - BLI_addtail(curves, newcu[i]); - } -#if 0 - fcurve_is_used(newcu[i]); /* never added to unused */ -#endif - } - - if (is_joint) { - bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); - chan->rotmode = ROT_MODE_QUAT; - } - else { - ob->rotmode = ROT_MODE_QUAT; - } - - return; -} - -/* - * This function returns the aspect ration from the Collada camera. - * - * Note:COLLADA allows to specify either XFov, or YFov alone. - * In that case the aspect ratio can be determined from - * the viewport aspect ratio (which is 1:1 ?) - * XXX: check this: its probably wrong! - * If both values are specified, then the aspect ration is simply xfov/yfov - * and if aspect ratio is efined, then .. well then its that one. - */ -static const double get_aspect_ratio(const COLLADAFW::Camera *camera) -{ - double aspect = camera->getAspectRatio().getValue(); - - if (aspect == 0) { - const double yfov = camera->getYFov().getValue(); - - if (yfov == 0) { - aspect = 1; /* assume yfov and xfov are equal */ - } - else { - const double xfov = camera->getXFov().getValue(); - if (xfov == 0) { - aspect = 1; - } - else { - aspect = xfov / yfov; - } - } - } - return aspect; -} - -static ListBase &get_animation_curves(Main *bmain, Material *ma) -{ - bAction *act; - if (!ma->adt || !ma->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&ma->id); - } - else { - act = ma->adt->action; - } - - return act->curves; -} - -void AnimationImporter::translate_Animations( - COLLADAFW::Node *node, - std::map &root_map, - std::multimap &object_map, - std::map FW_object_map, - std::map uid_material_map) -{ - bool is_joint = node->getType() == COLLADAFW::Node::JOINT; - COLLADAFW::UniqueId uid = node->getUniqueId(); - COLLADAFW::Node *root = root_map.find(uid) == root_map.end() ? node : root_map[uid]; - - Object *ob; - if (is_joint) { - ob = armature_importer->get_armature_for_joint(root); - } - else { - ob = object_map.find(uid) == object_map.end() ? NULL : object_map.find(uid)->second; - } - - if (!ob) { - fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); - return; - } - - AnimationImporter::AnimMix *animType = get_animation_type(node, FW_object_map); - bAction *act; - Main *bmain = CTX_data_main(mContext); - - if ((animType->transform) != 0) { - /* const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; */ /* UNUSED */ - char joint_path[200]; - - if (is_joint) { - armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); - } - - if (!ob->adt || !ob->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&ob->id); - } - else { - act = ob->adt->action; - } - - /* Get the list of animation curves of the object */ - ListBase *AnimCurves = &(act->curves); - - const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); - - /* for each transformation in node */ - for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { - COLLADAFW::Transformation *transform = nodeTransforms[i]; - COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); - - bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; - bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; - - const COLLADAFW::UniqueId &listid = transform->getAnimationList(); - - /* check if transformation has animations */ - if (animlist_map.find(listid) == animlist_map.end()) { - continue; - } - else { - /* transformation has animations */ - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings &bindings = - animlist->getAnimationBindings(); - /* all the curves belonging to the current binding */ - std::vector animcurves; - for (unsigned int j = 0; j < bindings.getCount(); j++) { - animcurves = curve_map[bindings[j].animation]; - if (is_matrix) { - apply_matrix_curves(ob, animcurves, root, node, transform); - } - else { - /* calculate rnapaths and array index of fcurves according to transformation and - * animation class */ - Assign_transform_animations( - transform, &bindings[j], &animcurves, is_joint, joint_path); - - std::vector::iterator iter; - /* Add the curves of the current animation to the object */ - for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { - FCurve *fcu = *iter; - - BLI_addtail(AnimCurves, fcu); - fcurve_is_used(fcu); - } - } - } - } - if (is_rotation && !(is_joint || is_matrix)) { - ob->rotmode = ROT_MODE_EUL; - } - } - } - - if ((animType->light) != 0) { - Light *lamp = (Light *)ob->data; - if (!lamp->adt || !lamp->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&lamp->id); - } - else { - act = lamp->adt->action; - } - - ListBase *AnimCurves = &(act->curves); - const COLLADAFW::InstanceLightPointerArray &nodeLights = node->getInstanceLights(); - - for (unsigned int i = 0; i < nodeLights.getCount(); i++) { - const COLLADAFW::Light *light = (COLLADAFW::Light *) - FW_object_map[nodeLights[i]->getInstanciatedObjectId()]; - - if ((animType->light & LIGHT_COLOR) != 0) { - const COLLADAFW::Color *col = &(light->getColor()); - const COLLADAFW::UniqueId &listid = col->getAnimationList(); - - Assign_color_animations(listid, AnimCurves, "color"); - } - if ((animType->light & LIGHT_FOA) != 0) { - const COLLADAFW::AnimatableFloat *foa = &(light->getFallOffAngle()); - const COLLADAFW::UniqueId &listid = foa->getAnimationList(); - - Assign_float_animations(listid, AnimCurves, "spot_size"); - } - if ((animType->light & LIGHT_FOE) != 0) { - const COLLADAFW::AnimatableFloat *foe = &(light->getFallOffExponent()); - const COLLADAFW::UniqueId &listid = foe->getAnimationList(); - - Assign_float_animations(listid, AnimCurves, "spot_blend"); - } - } - } - - if (animType->camera != 0) { - - Camera *cam = (Camera *)ob->data; - if (!cam->adt || !cam->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&cam->id); - } - else { - act = cam->adt->action; - } - - ListBase *AnimCurves = &(act->curves); - const COLLADAFW::InstanceCameraPointerArray &nodeCameras = node->getInstanceCameras(); - - for (unsigned int i = 0; i < nodeCameras.getCount(); i++) { - const COLLADAFW::Camera *camera = (COLLADAFW::Camera *) - FW_object_map[nodeCameras[i]->getInstanciatedObjectId()]; - - if ((animType->camera & CAMERA_XFOV) != 0) { - const COLLADAFW::AnimatableFloat *xfov = &(camera->getXFov()); - const COLLADAFW::UniqueId &listid = xfov->getAnimationList(); - double aspect = get_aspect_ratio(camera); - Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_XFOV); - } - - else if ((animType->camera & CAMERA_YFOV) != 0) { - const COLLADAFW::AnimatableFloat *yfov = &(camera->getYFov()); - const COLLADAFW::UniqueId &listid = yfov->getAnimationList(); - double aspect = get_aspect_ratio(camera); - Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_YFOV); - } - - else if ((animType->camera & CAMERA_XMAG) != 0) { - const COLLADAFW::AnimatableFloat *xmag = &(camera->getXMag()); - const COLLADAFW::UniqueId &listid = xmag->getAnimationList(); - Assign_float_animations(listid, AnimCurves, "ortho_scale"); - } - - else if ((animType->camera & CAMERA_YMAG) != 0) { - const COLLADAFW::AnimatableFloat *ymag = &(camera->getYMag()); - const COLLADAFW::UniqueId &listid = ymag->getAnimationList(); - Assign_float_animations(listid, AnimCurves, "ortho_scale"); - } - - if ((animType->camera & CAMERA_ZFAR) != 0) { - const COLLADAFW::AnimatableFloat *zfar = &(camera->getFarClippingPlane()); - const COLLADAFW::UniqueId &listid = zfar->getAnimationList(); - Assign_float_animations(listid, AnimCurves, "clip_end"); - } - - if ((animType->camera & CAMERA_ZNEAR) != 0) { - const COLLADAFW::AnimatableFloat *znear = &(camera->getNearClippingPlane()); - const COLLADAFW::UniqueId &listid = znear->getAnimationList(); - Assign_float_animations(listid, AnimCurves, "clip_start"); - } - } - } - if (animType->material != 0) { - - Material *ma = BKE_object_material_get(ob, 1); - if (!ma->adt || !ma->adt->action) { - act = ED_id_action_ensure(bmain, (ID *)&ma->id); - } - else { - act = ma->adt->action; - } - - const COLLADAFW::InstanceGeometryPointerArray &nodeGeoms = node->getInstanceGeometries(); - for (unsigned int i = 0; i < nodeGeoms.getCount(); i++) { - const COLLADAFW::MaterialBindingArray &matBinds = nodeGeoms[i]->getMaterialBindings(); - for (unsigned int j = 0; j < matBinds.getCount(); j++) { - const COLLADAFW::UniqueId &matuid = matBinds[j].getReferencedMaterial(); - const COLLADAFW::Effect *ef = (COLLADAFW::Effect *)(FW_object_map[matuid]); - if (ef != NULL) { /* can be NULL [#28909] */ - Material *ma = uid_material_map[matuid]; - if (!ma) { - fprintf(stderr, - "Collada: Node %s refers to undefined material\n", - node->getName().c_str()); - continue; - } - ListBase &AnimCurves = get_animation_curves(bmain, ma); - const COLLADAFW::CommonEffectPointerArray &commonEffects = ef->getCommonEffects(); - COLLADAFW::EffectCommon *efc = commonEffects[0]; - if ((animType->material & MATERIAL_SHININESS) != 0) { - const COLLADAFW::FloatOrParam *shin = &(efc->getShininess()); - const COLLADAFW::UniqueId &listid = shin->getAnimationList(); - Assign_float_animations(listid, &AnimCurves, "specular_hardness"); - } - - if ((animType->material & MATERIAL_IOR) != 0) { - const COLLADAFW::FloatOrParam *ior = &(efc->getIndexOfRefraction()); - const COLLADAFW::UniqueId &listid = ior->getAnimationList(); - Assign_float_animations(listid, &AnimCurves, "raytrace_transparency.ior"); - } - - if ((animType->material & MATERIAL_SPEC_COLOR) != 0) { - const COLLADAFW::ColorOrTexture *cot = &(efc->getSpecular()); - const COLLADAFW::UniqueId &listid = cot->getColor().getAnimationList(); - Assign_color_animations(listid, &AnimCurves, "specular_color"); - } - - if ((animType->material & MATERIAL_DIFF_COLOR) != 0) { - const COLLADAFW::ColorOrTexture *cot = &(efc->getDiffuse()); - const COLLADAFW::UniqueId &listid = cot->getColor().getAnimationList(); - Assign_color_animations(listid, &AnimCurves, "diffuse_color"); - } - } - } - } - } - - delete animType; -} - -void AnimationImporter::add_bone_animation_sampled(Object *ob, - std::vector &animcurves, - COLLADAFW::Node *root, - COLLADAFW::Node *node, - COLLADAFW::Transformation *tm) -{ - const char *bone_name = bc_get_joint_name(node); - char joint_path[200]; - armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); - - std::vector frames; - find_frames(&frames, &animcurves); - - /* convert degrees to radians */ - if (tm->getTransformationType() == COLLADAFW::Transformation::ROTATE) { - - std::vector::iterator iter; - for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { - FCurve *fcu = *iter; - - fcurve_deg_to_rad(fcu); - } - } - - float irest_dae[4][4]; - float rest[4][4], irest[4][4]; - - get_joint_rest_mat(irest_dae, root, node); - invert_m4(irest_dae); - - Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); - if (!bone) { - fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); - return; - } - - unit_m4(rest); - copy_m4_m4(rest, bone->arm_mat); - invert_m4_m4(irest, rest); - - /* new curves to assign matrix transform animation */ - FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale. */ - unsigned int totcu = 10; - const char *tm_str = NULL; - char rna_path[200]; - for (int i = 0; i < totcu; i++) { - - int axis = i; - - if (i < 4) { - tm_str = "rotation_quaternion"; - axis = i; - } - else if (i < 7) { - tm_str = "location"; - axis = i - 4; - } - else { - tm_str = "scale"; - axis = i - 7; - } - - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); - - newcu[i] = create_fcurve(axis, rna_path); - newcu[i]->totvert = frames.size(); - } - - if (frames.size() == 0) { - return; - } - - std::sort(frames.begin(), frames.end()); - - BCQuat qref; - - std::vector::iterator it; - - /* sample values at each frame */ - for (it = frames.begin(); it != frames.end(); it++) { - float fra = *it; - - Matrix mat; - Matrix matfra; - - unit_m4(matfra); - - /* calc object-space mat */ - evaluate_transform_at_frame(matfra, node, fra); - - /* for joints, we need a special matrix - * special matrix: iR * M * iR_dae * R - * where R, iR are bone rest and inverse rest mats in world space (Blender bones), - * iR_dae is joint inverse rest matrix (DAE) - * and M is an evaluated joint world-space matrix (DAE). */ - Matrix temp, par; - - /* calc M */ - calc_joint_parent_mat_rest(par, NULL, root, node); - mul_m4_m4m4(temp, par, matfra); - - /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ - - /* calc special matrix */ - mul_m4_series(mat, irest, temp, irest_dae, rest); - - Vector loc, scale; - - qref.rotate_to(mat); - - copy_v3_v3(loc, mat[3]); - mat4_to_size(scale, mat); - - /* add keys */ - for (int i = 0; i < totcu; i++) { - if (i < 4) { - add_bezt(newcu[i], fra, qref.quat()[i]); - } - else if (i < 7) { - add_bezt(newcu[i], fra, loc[i - 4]); - } - else { - add_bezt(newcu[i], fra, scale[i - 7]); - } - } - } - Main *bmain = CTX_data_main(mContext); - ED_id_action_ensure(bmain, (ID *)&ob->id); - - /* add curves */ - for (int i = 0; i < totcu; i++) { - add_bone_fcurve(ob, node, newcu[i]); -#if 0 - fcurve_is_used(newcu[i]); /* never added to unused */ -#endif - } - - bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); - chan->rotmode = ROT_MODE_QUAT; -} - -/* Check if object is animated by checking if animlist_map - * holds the animlist_id of node transforms */ -AnimationImporter::AnimMix *AnimationImporter::get_animation_type( - const COLLADAFW::Node *node, - std::map FW_object_map) -{ - AnimMix *types = new AnimMix(); - - const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); - - /* for each transformation in node */ - for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { - COLLADAFW::Transformation *transform = nodeTransforms[i]; - const COLLADAFW::UniqueId &listid = transform->getAnimationList(); - - /* check if transformation has animations */ - if (animlist_map.find(listid) == animlist_map.end()) { - continue; - } - else { - types->transform = types->transform | BC_NODE_TRANSFORM; - break; - } - } - const COLLADAFW::InstanceLightPointerArray &nodeLights = node->getInstanceLights(); - - for (unsigned int i = 0; i < nodeLights.getCount(); i++) { - const COLLADAFW::Light *light = (COLLADAFW::Light *) - FW_object_map[nodeLights[i]->getInstanciatedObjectId()]; - types->light = setAnimType(&(light->getColor()), (types->light), LIGHT_COLOR); - types->light = setAnimType(&(light->getFallOffAngle()), (types->light), LIGHT_FOA); - types->light = setAnimType(&(light->getFallOffExponent()), (types->light), LIGHT_FOE); - - if (types->light != 0) { - break; - } - } - - const COLLADAFW::InstanceCameraPointerArray &nodeCameras = node->getInstanceCameras(); - for (unsigned int i = 0; i < nodeCameras.getCount(); i++) { - const COLLADAFW::Camera *camera = (COLLADAFW::Camera *) - FW_object_map[nodeCameras[i]->getInstanciatedObjectId()]; - if (camera == NULL) { - /* Can happen if the node refers to an unknown camera. */ - continue; - } - - const bool is_perspective_type = camera->getCameraType() == COLLADAFW::Camera::PERSPECTIVE; - - int addition; - const COLLADAFW::Animatable *mag; - const COLLADAFW::UniqueId listid = camera->getYMag().getAnimationList(); - if (animlist_map.find(listid) != animlist_map.end()) { - mag = &(camera->getYMag()); - addition = (is_perspective_type) ? CAMERA_YFOV : CAMERA_YMAG; - } - else { - mag = &(camera->getXMag()); - addition = (is_perspective_type) ? CAMERA_XFOV : CAMERA_XMAG; - } - types->camera = setAnimType(mag, (types->camera), addition); - - types->camera = setAnimType(&(camera->getFarClippingPlane()), (types->camera), CAMERA_ZFAR); - types->camera = setAnimType(&(camera->getNearClippingPlane()), (types->camera), CAMERA_ZNEAR); - - if (types->camera != 0) { - break; - } - } - - const COLLADAFW::InstanceGeometryPointerArray &nodeGeoms = node->getInstanceGeometries(); - for (unsigned int i = 0; i < nodeGeoms.getCount(); i++) { - const COLLADAFW::MaterialBindingArray &matBinds = nodeGeoms[i]->getMaterialBindings(); - for (unsigned int j = 0; j < matBinds.getCount(); j++) { - const COLLADAFW::UniqueId &matuid = matBinds[j].getReferencedMaterial(); - const COLLADAFW::Effect *ef = (COLLADAFW::Effect *)(FW_object_map[matuid]); - if (ef != NULL) { /* can be NULL [#28909] */ - const COLLADAFW::CommonEffectPointerArray &commonEffects = ef->getCommonEffects(); - if (!commonEffects.empty()) { - COLLADAFW::EffectCommon *efc = commonEffects[0]; - types->material = setAnimType( - &(efc->getShininess()), (types->material), MATERIAL_SHININESS); - types->material = setAnimType( - &(efc->getSpecular().getColor()), (types->material), MATERIAL_SPEC_COLOR); - types->material = setAnimType( - &(efc->getDiffuse().getColor()), (types->material), MATERIAL_DIFF_COLOR); -#if 0 - types->material = setAnimType(&(efc->get()), (types->material), MATERIAL_TRANSPARENCY); -#endif - types->material = setAnimType( - &(efc->getIndexOfRefraction()), (types->material), MATERIAL_IOR); - } - } - } - } - return types; -} - -int AnimationImporter::setAnimType(const COLLADAFW::Animatable *prop, int types, int addition) -{ - int anim_type; - const COLLADAFW::UniqueId &listid = prop->getAnimationList(); - if (animlist_map.find(listid) != animlist_map.end()) { - anim_type = types | addition; - } - else { - anim_type = types; - } - - return anim_type; -} - -/* Is not used anymore. */ -void AnimationImporter::find_frames_old(std::vector *frames, - COLLADAFW::Node *node, - COLLADAFW::Transformation::TransformationType tm_type) -{ - bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; - bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; - /* for each , , etc. there is a separate Transformation */ - const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); - - unsigned int i; - /* find frames at which to sample plus convert all rotation keys to radians */ - for (i = 0; i < nodeTransforms.getCount(); i++) { - COLLADAFW::Transformation *transform = nodeTransforms[i]; - COLLADAFW::Transformation::TransformationType nodeTmType = transform->getTransformationType(); - - if (nodeTmType == tm_type) { - /* get animation bindings for the current transformation */ - const COLLADAFW::UniqueId &listid = transform->getAnimationList(); - /* if transform is animated its animlist must exist. */ - if (animlist_map.find(listid) != animlist_map.end()) { - - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings &bindings = - animlist->getAnimationBindings(); - - if (bindings.getCount()) { - /* for each AnimationBinding get the fcurves which animate the transform */ - for (unsigned int j = 0; j < bindings.getCount(); j++) { - std::vector &curves = curve_map[bindings[j].animation]; - bool xyz = ((nodeTmType == COLLADAFW::Transformation::TRANSLATE || - nodeTmType == COLLADAFW::Transformation::SCALE) && - bindings[j].animationClass == COLLADAFW::AnimationList::POSITION_XYZ); - - if ((!xyz && curves.size() == 1) || (xyz && curves.size() == 3) || is_matrix) { - std::vector::iterator iter; - - for (iter = curves.begin(); iter != curves.end(); iter++) { - FCurve *fcu = *iter; - - /* if transform is rotation the fcurves values must be turned in to radian. */ - if (is_rotation) { - fcurve_deg_to_rad(fcu); - } - - for (unsigned int k = 0; k < fcu->totvert; k++) { - /* get frame value from bezTriple */ - float fra = fcu->bezt[k].vec[1][0]; - /* if frame already not added add frame to frames */ - if (std::find(frames->begin(), frames->end(), fra) == frames->end()) { - frames->push_back(fra); - } - } - } - } - else { - fprintf(stderr, "expected %d curves, got %d\n", xyz ? 3 : 1, (int)curves.size()); - } - } - } - } - } - } -} - -/* prerequisites: - * animlist_map - map animlist id -> animlist - * curve_map - map anim id -> curve(s) */ -Object *AnimationImporter::translate_animation_OLD( - COLLADAFW::Node *node, - std::map &object_map, - std::map &root_map, - COLLADAFW::Transformation::TransformationType tm_type, - Object *par_job) -{ - - bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; - bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; - bool is_joint = node->getType() == COLLADAFW::Node::JOINT; - - COLLADAFW::Node *root = root_map.find(node->getUniqueId()) == root_map.end() ? - node : - root_map[node->getUniqueId()]; - Object *ob = is_joint ? armature_importer->get_armature_for_joint(node) : - object_map[node->getUniqueId()]; - const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; - if (!ob) { - fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); - return NULL; - } - - /* frames at which to sample */ - std::vector frames; - - find_frames_old(&frames, node, tm_type); - - unsigned int i; - - float irest_dae[4][4]; - float rest[4][4], irest[4][4]; - - if (is_joint) { - get_joint_rest_mat(irest_dae, root, node); - invert_m4(irest_dae); - - Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); - if (!bone) { - fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); - return NULL; - } - - unit_m4(rest); - copy_m4_m4(rest, bone->arm_mat); - invert_m4_m4(irest, rest); - } - - Object *job = NULL; - -#ifdef ARMATURE_TEST - FCurve *job_curves[10]; - job = get_joint_object(root, node, par_job); -#endif - - if (frames.size() == 0) { - return job; - } - - std::sort(frames.begin(), frames.end()); - - const char *tm_str = NULL; - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - tm_str = "rotation_quaternion"; - break; - case COLLADAFW::Transformation::SCALE: - tm_str = "scale"; - break; - case COLLADAFW::Transformation::TRANSLATE: - tm_str = "location"; - break; - case COLLADAFW::Transformation::MATRIX: - break; - default: - return job; - } - - char rna_path[200]; - char joint_path[200]; - - if (is_joint) { - armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); - } - - /* new curves */ - FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale */ - unsigned int totcu = is_matrix ? 10 : (is_rotation ? 4 : 3); - - for (i = 0; i < totcu; i++) { - - int axis = i; - - if (is_matrix) { - if (i < 4) { - tm_str = "rotation_quaternion"; - axis = i; - } - else if (i < 7) { - tm_str = "location"; - axis = i - 4; - } - else { - tm_str = "scale"; - axis = i - 7; - } - } - - if (is_joint) { - BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); - } - else { - BLI_strncpy(rna_path, tm_str, sizeof(rna_path)); - } - newcu[i] = create_fcurve(axis, rna_path); - -#ifdef ARMATURE_TEST - if (is_joint) { - job_curves[i] = create_fcurve(axis, tm_str); - } -#endif - } - - std::vector::iterator it; - - /* sample values at each frame */ - for (it = frames.begin(); it != frames.end(); it++) { - float fra = *it; - - float mat[4][4]; - float matfra[4][4]; - - unit_m4(matfra); - - /* calc object-space mat */ - evaluate_transform_at_frame(matfra, node, fra); - - /* for joints, we need a special matrix */ - if (is_joint) { - /* special matrix: iR * M * iR_dae * R - * where R, iR are bone rest and inverse rest mats in world space (Blender bones), - * iR_dae is joint inverse rest matrix (DAE) - * and M is an evaluated joint world-space matrix (DAE). */ - float temp[4][4], par[4][4]; - - /* calc M */ - calc_joint_parent_mat_rest(par, NULL, root, node); - mul_m4_m4m4(temp, par, matfra); - - /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ - - /* calc special matrix */ - mul_m4_series(mat, irest, temp, irest_dae, rest); - } - else { - copy_m4_m4(mat, matfra); - } - - float val[4] = {}; - float rot[4], loc[3], scale[3]; - - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - mat4_to_quat(val, mat); - break; - case COLLADAFW::Transformation::SCALE: - mat4_to_size(val, mat); - break; - case COLLADAFW::Transformation::TRANSLATE: - copy_v3_v3(val, mat[3]); - break; - case COLLADAFW::Transformation::MATRIX: - mat4_to_quat(rot, mat); - copy_v3_v3(loc, mat[3]); - mat4_to_size(scale, mat); - break; - default: - break; - } - - /* add keys */ - for (i = 0; i < totcu; i++) { - if (is_matrix) { - if (i < 4) { - add_bezt(newcu[i], fra, rot[i]); - } - else if (i < 7) { - add_bezt(newcu[i], fra, loc[i - 4]); - } - else { - add_bezt(newcu[i], fra, scale[i - 7]); - } - } - else { - add_bezt(newcu[i], fra, val[i]); - } - } - -#ifdef ARMATURE_TEST - if (is_joint) { - switch (tm_type) { - case COLLADAFW::Transformation::ROTATE: - mat4_to_quat(val, matfra); - break; - case COLLADAFW::Transformation::SCALE: - mat4_to_size(val, matfra); - break; - case COLLADAFW::Transformation::TRANSLATE: - copy_v3_v3(val, matfra[3]); - break; - case MATRIX: - mat4_to_quat(rot, matfra); - copy_v3_v3(loc, matfra[3]); - mat4_to_size(scale, matfra); - break; - default: - break; - } - - for (i = 0; i < totcu; i++) { - if (is_matrix) { - if (i < 4) { - add_bezt(job_curves[i], fra, rot[i]); - } - else if (i < 7) { - add_bezt(job_curves[i], fra, loc[i - 4]); - } - else { - add_bezt(job_curves[i], fra, scale[i - 7]); - } - } - else { - add_bezt(job_curves[i], fra, val[i]); - } - } - } -#endif - } - Main *bmain = CTX_data_main(mContext); - ED_id_action_ensure(bmain, (ID *)&ob->id); - - ListBase *curves = &ob->adt->action->curves; - - /* add curves */ - for (i = 0; i < totcu; i++) { - if (is_joint) { - add_bone_fcurve(ob, node, newcu[i]); - } - else { - BLI_addtail(curves, newcu[i]); - } - -#ifdef ARMATURE_TEST - if (is_joint) { - BLI_addtail(&job->adt->action->curves, job_curves[i]); - } -#endif - } - - if (is_rotation || is_matrix) { - if (is_joint) { - bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); - chan->rotmode = (is_matrix) ? ROT_MODE_QUAT : ROT_MODE_EUL; - } - else { - ob->rotmode = (is_matrix) ? ROT_MODE_QUAT : ROT_MODE_EUL; - } - } - - return job; -} - -/* internal, better make it private - * warning: evaluates only rotation and only assigns matrix transforms now - * prerequisites: animlist_map, curve_map */ -void AnimationImporter::evaluate_transform_at_frame(float mat[4][4], - COLLADAFW::Node *node, - float fra) -{ - const COLLADAFW::TransformationPointerArray &tms = node->getTransformations(); - - unit_m4(mat); - - for (unsigned int i = 0; i < tms.getCount(); i++) { - COLLADAFW::Transformation *tm = tms[i]; - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - float m[4][4]; - - unit_m4(m); - - std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId(); - if (!evaluate_animation(tm, m, fra, nodename.c_str())) { - switch (type) { - case COLLADAFW::Transformation::ROTATE: - dae_rotate_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::TRANSLATE: - dae_translate_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::SCALE: - dae_scale_to_mat4(tm, m); - break; - case COLLADAFW::Transformation::MATRIX: - dae_matrix_to_mat4(tm, m); - break; - default: - fprintf(stderr, "unsupported transformation type %d\n", type); - } - } - - float temp[4][4]; - copy_m4_m4(temp, mat); - - mul_m4_m4m4(mat, temp, m); - } -} - -static void report_class_type_unsupported(const char *path, - const COLLADAFW::AnimationList::AnimationClass animclass, - const COLLADAFW::Transformation::TransformationType type) -{ - if (animclass == COLLADAFW::AnimationList::UNKNOWN_CLASS) { - fprintf(stderr, "%s: UNKNOWN animation class\n", path); - } - else { - fprintf(stderr, - "%s: animation class %d is not supported yet for transformation type %d\n", - path, - animclass, - type); - } -} - -/* return true to indicate that mat contains a sane value */ -bool AnimationImporter::evaluate_animation(COLLADAFW::Transformation *tm, - float mat[4][4], - float fra, - const char *node_id) -{ - const COLLADAFW::UniqueId &listid = tm->getAnimationList(); - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - - if (type != COLLADAFW::Transformation::ROTATE && type != COLLADAFW::Transformation::SCALE && - type != COLLADAFW::Transformation::TRANSLATE && type != COLLADAFW::Transformation::MATRIX) { - fprintf(stderr, "animation of transformation %d is not supported yet\n", type); - return false; - } - - if (animlist_map.find(listid) == animlist_map.end()) { - return false; - } - - const COLLADAFW::AnimationList *animlist = animlist_map[listid]; - const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); - - if (bindings.getCount()) { - float vec[3]; - - bool is_scale = (type == COLLADAFW::Transformation::SCALE); - bool is_translate = (type == COLLADAFW::Transformation::TRANSLATE); - - if (is_scale) { - dae_scale_to_v3(tm, vec); - } - else if (is_translate) { - dae_translate_to_v3(tm, vec); - } - - for (unsigned int index = 0; index < bindings.getCount(); index++) { - const COLLADAFW::AnimationList::AnimationBinding &binding = bindings[index]; - std::vector &curves = curve_map[binding.animation]; - COLLADAFW::AnimationList::AnimationClass animclass = binding.animationClass; - char path[100]; - - switch (type) { - case COLLADAFW::Transformation::ROTATE: - BLI_snprintf(path, sizeof(path), "%s.rotate (binding %u)", node_id, index); - break; - case COLLADAFW::Transformation::SCALE: - BLI_snprintf(path, sizeof(path), "%s.scale (binding %u)", node_id, index); - break; - case COLLADAFW::Transformation::TRANSLATE: - BLI_snprintf(path, sizeof(path), "%s.translate (binding %u)", node_id, index); - break; - case COLLADAFW::Transformation::MATRIX: - BLI_snprintf(path, sizeof(path), "%s.matrix (binding %u)", node_id, index); - break; - default: - break; - } - - if (type == COLLADAFW::Transformation::ROTATE) { - if (curves.size() != 1) { - fprintf(stderr, "expected 1 curve, got %d\n", (int)curves.size()); - return false; - } - - /* TODO support other animclasses */ - if (animclass != COLLADAFW::AnimationList::ANGLE) { - report_class_type_unsupported(path, animclass, type); - return false; - } - - COLLADABU::Math::Vector3 &axis = ((COLLADAFW::Rotate *)tm)->getRotationAxis(); - - float ax[3] = {(float)axis[0], (float)axis[1], (float)axis[2]}; - float angle = evaluate_fcurve(curves[0], fra); - axis_angle_to_mat4(mat, ax, angle); - - return true; - } - else if (is_scale || is_translate) { - bool is_xyz = animclass == COLLADAFW::AnimationList::POSITION_XYZ; - - if ((!is_xyz && curves.size() != 1) || (is_xyz && curves.size() != 3)) { - if (is_xyz) { - fprintf(stderr, "%s: expected 3 curves, got %d\n", path, (int)curves.size()); - } - else { - fprintf(stderr, "%s: expected 1 curve, got %d\n", path, (int)curves.size()); - } - return false; - } - - switch (animclass) { - case COLLADAFW::AnimationList::POSITION_X: - vec[0] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_Y: - vec[1] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_Z: - vec[2] = evaluate_fcurve(curves[0], fra); - break; - case COLLADAFW::AnimationList::POSITION_XYZ: - vec[0] = evaluate_fcurve(curves[0], fra); - vec[1] = evaluate_fcurve(curves[1], fra); - vec[2] = evaluate_fcurve(curves[2], fra); - break; - default: - report_class_type_unsupported(path, animclass, type); - break; - } - } - else if (type == COLLADAFW::Transformation::MATRIX) { - /* for now, of matrix animation, - * support only the case when all values are packed into one animation */ - if (curves.size() != 16) { - fprintf(stderr, "%s: expected 16 curves, got %d\n", path, (int)curves.size()); - return false; - } - - COLLADABU::Math::Matrix4 matrix; - int mi = 0, mj = 0; - - for (std::vector::iterator it = curves.begin(); it != curves.end(); it++) { - matrix.setElement(mi, mj, evaluate_fcurve(*it, fra)); - mj++; - if (mj == 4) { - mi++; - mj = 0; - } - } - unit_converter->dae_matrix_to_mat4_(mat, matrix); - return true; - } - } - - if (is_scale) { - size_to_mat4(mat, vec); - } - else { - copy_v3_v3(mat[3], vec); - } - - return is_scale || is_translate; - } - - return false; -} - -/* gives a world-space mat of joint at rest position */ -void AnimationImporter::get_joint_rest_mat(float mat[4][4], - COLLADAFW::Node *root, - COLLADAFW::Node *node) -{ - /* if bind mat is not available, - * use "current" node transform, i.e. all those tms listed inside */ - if (!armature_importer->get_joint_bind_mat(mat, node)) { - float par[4][4], m[4][4]; - - calc_joint_parent_mat_rest(par, NULL, root, node); - get_node_mat(m, node, NULL, NULL); - mul_m4_m4m4(mat, par, m); - } -} - -/* gives a world-space mat, end's mat not included */ -bool AnimationImporter::calc_joint_parent_mat_rest(float mat[4][4], - float par[4][4], - COLLADAFW::Node *node, - COLLADAFW::Node *end) -{ - float m[4][4]; - - if (node == end) { - par ? copy_m4_m4(mat, par) : unit_m4(mat); - return true; - } - - /* use bind matrix if available or calc "current" world mat */ - if (!armature_importer->get_joint_bind_mat(m, node)) { - if (par) { - float temp[4][4]; - get_node_mat(temp, node, NULL, NULL); - mul_m4_m4m4(m, par, temp); - } - else { - get_node_mat(m, node, NULL, NULL); - } - } - - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (calc_joint_parent_mat_rest(mat, m, children[i], end)) { - return true; - } - } - - return false; -} - -#ifdef ARMATURE_TEST -Object *AnimationImporter::get_joint_object(COLLADAFW::Node *root, - COLLADAFW::Node *node, - Object *par_job) -{ - if (joint_objects.find(node->getUniqueId()) == joint_objects.end()) { - Object *job = bc_add_object(scene, OB_EMPTY, (char *)get_joint_name(node)); - - job->lay = BKE_scene_base_find(scene, job)->lay = 2; - - mul_v3_fl(job->scale, 0.5f); - DEG_id_tag_update(&job->id, ID_RECALC_TRANSFORM); - - ED_id_action_ensure((ID *)&job->id); - - job->rotmode = ROT_MODE_QUAT; - - float mat[4][4]; - get_joint_rest_mat(mat, root, node); - - if (par_job) { - float temp[4][4], ipar[4][4]; - invert_m4_m4(ipar, par_job->obmat); - copy_m4_m4(temp, mat); - mul_m4_m4m4(mat, ipar, temp); - } - - bc_decompose(mat, job->loc, NULL, job->quat, job->scale); - - if (par_job) { - job->parent = par_job; - - DEG_id_tag_update(&par_job->id, ID_RECALC_TRANSFORM); - job->parsubstr[0] = 0; - } - - BKE_object_where_is_calc(scene, job); - - /* after parenting and layer change */ - DEG_relations_tag_update(CTX_data_main(C)); - - joint_objects[node->getUniqueId()] = job; - } - - return joint_objects[node->getUniqueId()]; -} -#endif - -#if 0 -/* recursively evaluates joint tree until end is found, - * mat then is world-space matrix of end mat must be identity on enter, node must be root. */ -bool AnimationImporter::evaluate_joint_world_transform_at_frame( - float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra) -{ - float m[4][4]; - if (par) { - float temp[4][4]; - evaluate_transform_at_frame(temp, node, node == end ? fra : 0.0f); - mul_m4_m4m4(m, par, temp); - } - else { - evaluate_transform_at_frame(m, node, node == end ? fra : 0.0f); - } - - if (node == end) { - copy_m4_m4(mat, m); - return true; - } - else { - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - for (int i = 0; i < children.getCount(); i++) { - if (evaluate_joint_world_transform_at_frame(mat, m, children[i], end, fra)) { - return true; - } - } - } - - return false; -} -#endif - -void AnimationImporter::add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu) -{ - const char *bone_name = bc_get_joint_name(node); - bAction *act = ob->adt->action; - - /* try to find group */ - bActionGroup *grp = BKE_action_group_find_name(act, bone_name); - - /* no matching groups, so add one */ - if (grp == NULL) { - /* Add a new group, and make it active */ - grp = (bActionGroup *)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); - - grp->flag = AGRP_SELECTED; - BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); - - BLI_addtail(&act->groups, grp); - BLI_uniquename(&act->groups, - grp, - CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), - '.', - offsetof(bActionGroup, name), - 64); - } - - /* add F-Curve to group */ - action_groups_add_channel(act, grp, fcu); -} - -void AnimationImporter::set_import_from_version(std::string import_from_version) -{ - this->import_from_version = import_from_version; -} diff --git a/source/blender/collada/AnimationImporter.h b/source/blender/collada/AnimationImporter.h deleted file mode 100644 index 0043dad7116..00000000000 --- a/source/blender/collada/AnimationImporter.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __ANIMATIONIMPORTER_H__ -#define __ANIMATIONIMPORTER_H__ - -#include -#include - -#include "COLLADAFWAnimation.h" -#include "COLLADAFWAnimationCurve.h" -#include "COLLADAFWAnimationList.h" -#include "COLLADAFWNode.h" -#include "COLLADAFWUniqueId.h" -#include "COLLADAFWLight.h" -#include "COLLADAFWCamera.h" -#include "COLLADAFWMaterial.h" -#include "COLLADAFWEffect.h" -#include "COLLADAFWInstanceGeometry.h" - -extern "C" { -#include "BKE_context.h" -#include "DNA_anim_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_light_types.h" -#include "DNA_camera_types.h" -} - -//#include "ArmatureImporter.h" -#include "TransformReader.h" - -#include "collada_internal.h" - -class ArmatureImporter; - -class AnimationImporterBase { - public: - // virtual void change_eul_to_quat(Object *ob, bAction *act) = 0; -}; - -class AnimationImporter : private TransformReader, public AnimationImporterBase { - private: - bContext *mContext; - ArmatureImporter *armature_importer; - Scene *scene; - - std::map> curve_map; - std::map uid_animated_map; - // std::map > fcurves_actionGroup_map; - std::map animlist_map; - std::vector unused_curves; - std::map joint_objects; - - FCurve *create_fcurve(int array_index, const char *rna_path); - - void add_bezt(FCurve *fcu, - float frame, - float value, - eBezTriple_Interpolation ipo = BEZT_IPO_LIN); - - // create one or several fcurves depending on the number of parameters being animated - void animation_to_fcurves(COLLADAFW::AnimationCurve *curve); - - void fcurve_deg_to_rad(FCurve *cu); - void fcurve_scale(FCurve *cu, int scale); - - void fcurve_is_used(FCurve *fcu); - - void add_fcurves_to_object(Main *bmain, - Object *ob, - std::vector &curves, - char *rna_path, - int array_index, - Animation *animated); - - int typeFlag; - - std::string import_from_version; - - enum lightAnim { - // INANIMATE = 0, - LIGHT_COLOR = 2, - LIGHT_FOA = 4, - LIGHT_FOE = 8, - }; - - enum cameraAnim { - // INANIMATE = 0, - CAMERA_XFOV = 2, - CAMERA_XMAG = 4, - CAMERA_YFOV = 8, - CAMERA_YMAG = 16, - CAMERA_ZFAR = 32, - CAMERA_ZNEAR = 64, - }; - - enum matAnim { - MATERIAL_SHININESS = 2, - MATERIAL_SPEC_COLOR = 4, - MATERIAL_DIFF_COLOR = 1 << 3, - MATERIAL_TRANSPARENCY = 1 << 4, - MATERIAL_IOR = 1 << 5, - }; - - enum AnimationType { - BC_INANIMATE = 0, - BC_NODE_TRANSFORM = 1, - }; - - struct AnimMix { - int transform; - int light; - int camera; - int material; - int texture; - }; - - public: - AnimationImporter(bContext *C, UnitConverter *conv, ArmatureImporter *arm, Scene *scene) - : TransformReader(conv), mContext(C), armature_importer(arm), scene(scene) - { - } - - ~AnimationImporter(); - - void set_import_from_version(std::string import_from_version); - bool write_animation(const COLLADAFW::Animation *anim); - - // called on post-process stage after writeVisualScenes - bool write_animation_list(const COLLADAFW::AnimationList *animlist); - - void read_node_transform(COLLADAFW::Node *node, Object *ob); -#if 0 - virtual void change_eul_to_quat(Object *ob, bAction *act); -#endif - - void translate_Animations(COLLADAFW::Node *Node, - std::map &root_map, - std::multimap &object_map, - std::map FW_object_map, - std::map uid_material_map); - - AnimMix *get_animation_type( - const COLLADAFW::Node *node, - std::map FW_object_map); - - void apply_matrix_curves(Object *ob, - std::vector &animcurves, - COLLADAFW::Node *root, - COLLADAFW::Node *node, - COLLADAFW::Transformation *tm); - - void add_bone_animation_sampled(Object *ob, - std::vector &animcurves, - COLLADAFW::Node *root, - COLLADAFW::Node *node, - COLLADAFW::Transformation *tm); - - void Assign_transform_animations(COLLADAFW::Transformation *transform, - const COLLADAFW::AnimationList::AnimationBinding *binding, - std::vector *curves, - bool is_joint, - char *joint_path); - - void Assign_color_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const char *anim_type); - void Assign_float_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const char *anim_type); - void Assign_lens_animations(const COLLADAFW::UniqueId &listid, - ListBase *AnimCurves, - const double aspect, - Camera *cam, - const char *anim_type, - int fov_type); - - int setAnimType(const COLLADAFW::Animatable *prop, int type, int addition); - - void modify_fcurve(std::vector *curves, - const char *rna_path, - int array_index, - int scale = 1); - void unused_fcurve(std::vector *curves); - // prerequisites: - // animlist_map - map animlist id -> animlist - // curve_map - map anim id -> curve(s) - Object *translate_animation_OLD(COLLADAFW::Node *node, - std::map &object_map, - std::map &root_map, - COLLADAFW::Transformation::TransformationType tm_type, - Object *par_job = NULL); - - void find_frames(std::vector *frames, std::vector *curves); - void find_frames_old(std::vector *frames, - COLLADAFW::Node *node, - COLLADAFW::Transformation::TransformationType tm_type); - // internal, better make it private - // warning: evaluates only rotation - // prerequisites: animlist_map, curve_map - void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra); - - // return true to indicate that mat contains a sane value - bool evaluate_animation(COLLADAFW::Transformation *tm, - float mat[4][4], - float fra, - const char *node_id); - - // gives a world-space mat of joint at rest position - void get_joint_rest_mat(float mat[4][4], COLLADAFW::Node *root, COLLADAFW::Node *node); - - // gives a world-space mat, end's mat not included - bool calc_joint_parent_mat_rest(float mat[4][4], - float par[4][4], - COLLADAFW::Node *node, - COLLADAFW::Node *end); - - float convert_to_focal_length(float in_xfov, int fov_type, float aspect, float sensorx); - -#ifdef ARMATURE_TEST - Object *get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job); -#endif - -#if 0 - // recursively evaluates joint tree until end is found, mat then is world-space matrix of end - // mat must be identity on enter, node must be root - bool evaluate_joint_world_transform_at_frame( - float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra); -#endif - - void add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu); - - void extra_data_importer(std::string elementName); -}; - -#endif diff --git a/source/blender/collada/ArmatureExporter.cpp b/source/blender/collada/ArmatureExporter.cpp deleted file mode 100644 index 84979cc4ca4..00000000000 --- a/source/blender/collada/ArmatureExporter.cpp +++ /dev/null @@ -1,328 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "COLLADASWBaseInputElement.h" -#include "COLLADASWInstanceController.h" -#include "COLLADASWPrimitves.h" -#include "COLLADASWSource.h" - -#include "DNA_action_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" - -#include "BKE_action.h" -#include "BKE_armature.h" - -extern "C" { -#include "BKE_global.h" -#include "BKE_mesh.h" -} - -#include "ED_armature.h" - -#include "BLI_listbase.h" - -#include "GeometryExporter.h" -#include "ArmatureExporter.h" -#include "SceneExporter.h" - -// write bone nodes -void ArmatureExporter::add_armature_bones(Object *ob_arm, - ViewLayer *view_layer, - SceneExporter *se, - std::vector &child_objects) - -{ - // write bone nodes - - bArmature *armature = (bArmature *)ob_arm->data; - bool is_edited = armature->edbo != NULL; - - if (!is_edited) { - ED_armature_to_edit(armature); - } - - for (Bone *bone = (Bone *)armature->bonebase.first; bone; bone = bone->next) { - add_bone_node(bone, ob_arm, se, child_objects); - } - - if (!is_edited) { - ED_armature_edit_free(armature); - } -} - -void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins, - Object *ob_arm, - Bone *bone) -{ - if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) { - std::string joint_id = translate_id(id_name(ob_arm) + "_" + bone->name); - ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, joint_id)); - } - else { - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - write_bone_URLs(ins, ob_arm, child); - } - } -} - -bool ArmatureExporter::add_instance_controller(Object *ob) -{ - Object *ob_arm = bc_get_assigned_armature(ob); - bArmature *arm = (bArmature *)ob_arm->data; - - const std::string &controller_id = get_controller_id(ob_arm, ob); - - COLLADASW::InstanceController ins(mSW); - ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id)); - - Mesh *me = (Mesh *)ob->data; - if (!me->dvert) { - return false; - } - - // write root bone URLs - Bone *bone; - for (bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { - write_bone_URLs(ins, ob_arm, bone); - } - - InstanceWriter::add_material_bindings( - ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); - - ins.add(); - return true; -} - -#if 0 -void ArmatureExporter::operator()(Object *ob) -{ - Object *ob_arm = bc_get_assigned_armature(ob); -} - -bool ArmatureExporter::already_written(Object *ob_arm) -{ - return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != - written_armatures.end(); -} - -void ArmatureExporter::wrote(Object *ob_arm) -{ - written_armatures.push_back(ob_arm); -} - -void ArmatureExporter::find_objects_using_armature(Object *ob_arm, - std::vector &objects, - Scene *sce) -{ - objects.clear(); - - Base *base = (Base *)sce->base.first; - while (base) { - Object *ob = base->object; - - if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) { - objects.push_back(ob); - } - - base = base->next; - } -} -#endif - -// parent_mat is armature-space -void ArmatureExporter::add_bone_node(Bone *bone, - Object *ob_arm, - SceneExporter *se, - std::vector &child_objects) -{ - if (can_export(bone)) { - std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name); - std::string node_name = std::string(bone->name); - std::string node_sid = get_joint_sid(bone); - - COLLADASW::Node node(mSW); - - node.setType(COLLADASW::Node::JOINT); - node.setNodeId(node_id); - node.setNodeName(node_name); - node.setNodeSid(node_sid); - - if (this->export_settings.get_use_blender_profile()) { - if (!is_export_root(bone)) { - if (bone->flag & BONE_CONNECTED) { - node.addExtraTechniqueParameter("blender", "connect", true); - } - } - std::string layers = BoneExtended::get_bone_layers(bone->layer); - node.addExtraTechniqueParameter("blender", "layer", layers); - - bArmature *armature = (bArmature *)ob_arm->data; - EditBone *ebone = bc_get_edit_bone(armature, bone->name); - if (ebone && ebone->roll != 0) { - node.addExtraTechniqueParameter("blender", "roll", ebone->roll); - } - if (bc_is_leaf_bone(bone)) { - Vector head, tail; - const BCMatrix &global_transform = this->export_settings.get_global_transform(); - if (this->export_settings.get_apply_global_orientation()) { - bc_add_global_transform(head, bone->arm_head, global_transform); - bc_add_global_transform(tail, bone->arm_tail, global_transform); - } - else { - copy_v3_v3(head, bone->arm_head); - copy_v3_v3(tail, bone->arm_tail); - } - node.addExtraTechniqueParameter("blender", "tip_x", tail[0] - head[0]); - node.addExtraTechniqueParameter("blender", "tip_y", tail[1] - head[1]); - node.addExtraTechniqueParameter("blender", "tip_z", tail[2] - head[2]); - } - } - - node.start(); - - add_bone_transform(ob_arm, bone, node); - - // Write nodes of childobjects, remove written objects from list - std::vector::iterator iter = child_objects.begin(); - - while (iter != child_objects.end()) { - Object *ob = *iter; - if (ob->partype == PARBONE && STREQ(ob->parsubstr, bone->name)) { - float backup_parinv[4][4]; - copy_m4_m4(backup_parinv, ob->parentinv); - - // crude, temporary change to parentinv - // so transform gets exported correctly. - - // Add bone tail- translation... don't know why - // bone parenting is against the tail of a bone - // and not it's head, seems arbitrary. - ob->parentinv[3][1] += bone->length; - - // OPEN_SIM_COMPATIBILITY - // TODO: when such objects are animated as - // single matrix the tweak must be applied - // to the result. - if (export_settings.get_open_sim()) { - // tweak objects parentinverse to match compatibility - float temp[4][4]; - - copy_m4_m4(temp, bone->arm_mat); - temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; - - mul_m4_m4m4(ob->parentinv, temp, ob->parentinv); - } - - se->writeNode(ob); - copy_m4_m4(ob->parentinv, backup_parinv); - iter = child_objects.erase(iter); - } - else { - iter++; - } - } - - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - add_bone_node(child, ob_arm, se, child_objects); - } - node.end(); - } - else { - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - add_bone_node(child, ob_arm, se, child_objects); - } - } -} - -bool ArmatureExporter::is_export_root(Bone *bone) -{ - Bone *entry = bone->parent; - while (entry) { - if (can_export(entry)) { - return false; - } - entry = entry->parent; - } - return can_export(bone); -} - -void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node &node) -{ - // bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); - - float mat[4][4]; - float bone_rest_mat[4][4]; /* derived from bone->arm_mat */ - float parent_rest_mat[4][4]; /* derived from bone->parent->arm_mat */ - - bool has_restmat = bc_get_property_matrix(bone, "rest_mat", mat); - - if (!has_restmat) { - - /* Have no restpose matrix stored, try old style <= Blender 2.78 */ - - bc_create_restpose_mat(this->export_settings, bone, bone_rest_mat, bone->arm_mat, true); - - if (is_export_root(bone)) { - copy_m4_m4(mat, bone_rest_mat); - } - else { - Matrix parent_inverse; - bc_create_restpose_mat( - this->export_settings, bone->parent, parent_rest_mat, bone->parent->arm_mat, true); - - invert_m4_m4(parent_inverse, parent_rest_mat); - mul_m4_m4m4(mat, parent_inverse, bone_rest_mat); - } - - // OPEN_SIM_COMPATIBILITY - - if (export_settings.get_open_sim()) { - // Remove rotations vs armature from transform - // parent_rest_rot * mat * irest_rot - Matrix workmat; - copy_m4_m4(workmat, bone_rest_mat); - - workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f; - invert_m4(workmat); - - mul_m4_m4m4(mat, mat, workmat); - - if (!is_export_root(bone)) { - copy_m4_m4(workmat, parent_rest_mat); - workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f; - - mul_m4_m4m4(mat, workmat, mat); - } - } - } - - if (this->export_settings.get_limit_precision()) { - BCMatrix::sanitize(mat, LIMITTED_PRECISION); - } - - TransformWriter::add_joint_transform(node, mat, NULL, this->export_settings, has_restmat); -} - -std::string ArmatureExporter::get_controller_id(Object *ob_arm, Object *ob) -{ - return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + - SKIN_CONTROLLER_ID_SUFFIX; -} diff --git a/source/blender/collada/ArmatureExporter.h b/source/blender/collada/ArmatureExporter.h deleted file mode 100644 index da6d6f79ef5..00000000000 --- a/source/blender/collada/ArmatureExporter.h +++ /dev/null @@ -1,107 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __ARMATUREEXPORTER_H__ -#define __ARMATUREEXPORTER_H__ - -#include -#include -//#include - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryControllers.h" -#include "COLLADASWInputList.h" -#include "COLLADASWNode.h" - -#include "DNA_armature_types.h" -#include "DNA_listBase.h" -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_constraint_types.h" -#include "DNA_scene_types.h" - -#include "TransformWriter.h" -#include "InstanceWriter.h" - -#include "ExportSettings.h" - -class SceneExporter; - -// XXX exporter writes wrong data for shared armatures. A separate -// controller should be written for each armature-mesh binding how do -// we make controller ids then? -class ArmatureExporter : public COLLADASW::LibraryControllers, - protected TransformWriter, - protected InstanceWriter { - public: - // XXX exporter writes wrong data for shared armatures. A separate - // controller should be written for each armature-mesh binding how do - // we make controller ids then? - ArmatureExporter(BlenderContext &blender_context, - COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings) - : COLLADASW::LibraryControllers(sw), - blender_context(blender_context), - export_settings(export_settings) - { - } - - void add_armature_bones(Object *ob_arm, - ViewLayer *view_layer, - SceneExporter *se, - std::vector &child_objects); - - bool add_instance_controller(Object *ob); - - private: - BlenderContext &blender_context; - BCExportSettings &export_settings; - -#if 0 - std::vector written_armatures; - - bool already_written(Object *ob_arm); - - void wrote(Object *ob_arm); - - void find_objects_using_armature(Object *ob_arm, std::vector &objects, Scene *sce); -#endif - - // Scene, SceneExporter and the list of child_objects - // are required for writing bone parented objects - void add_bone_node(Bone *bone, - Object *ob_arm, - SceneExporter *se, - std::vector &child_objects); - - inline bool can_export(Bone *bone) - { - return !(export_settings.get_deform_bones_only() && bone->flag & BONE_NO_DEFORM); - } - - bool is_export_root(Bone *bone); - void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node &node); - - std::string get_controller_id(Object *ob_arm, Object *ob); - - void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); -}; - -#endif diff --git a/source/blender/collada/ArmatureImporter.cpp b/source/blender/collada/ArmatureImporter.cpp deleted file mode 100644 index b8c534f97dd..00000000000 --- a/source/blender/collada/ArmatureImporter.cpp +++ /dev/null @@ -1,1115 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include - -#include "COLLADAFWUniqueId.h" - -extern "C" { -#include "BKE_action.h" -#include "BKE_object.h" -#include "BKE_armature.h" -#include "BLI_string.h" -#include "BLI_listbase.h" -#include "ED_armature.h" -} - -#include "DEG_depsgraph.h" - -#include "collada_utils.h" -#include "ArmatureImporter.h" - -/* use node name, or fall back to original id if not present (name is optional) */ -template static const char *bc_get_joint_name(T *node) -{ - const std::string &id = node->getName(); - return id.size() ? id.c_str() : node->getOriginalId().c_str(); -} - -ArmatureImporter::ArmatureImporter(UnitConverter *conv, - MeshImporterBase *mesh, - Main *bmain, - Scene *sce, - ViewLayer *view_layer, - const ImportSettings *import_settings) - : TransformReader(conv), - m_bmain(bmain), - scene(sce), - view_layer(view_layer), - unit_converter(conv), - import_settings(import_settings), - empty(NULL), - mesh_importer(mesh) -{ -} - -ArmatureImporter::~ArmatureImporter() -{ - /* free skin controller data if we forget to do this earlier */ - std::map::iterator it; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - it->second.free(); - } -} - -#if 0 -JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); -{ - const COLLADAFW::UniqueId &joint_id = node->getUniqueId(); - - if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) { - fprintf( - stderr, "Cannot find a joint index by joint id for %s.\n", node->getOriginalId().c_str()); - return NULL; - } - - int joint_index = joint_id_to_joint_index_map[joint_id]; - - return &joint_index_to_joint_info_map[joint_index]; -} -#endif - -int ArmatureImporter::create_bone(SkinInfo *skin, - COLLADAFW::Node *node, - EditBone *parent, - int totchild, - float parent_mat[4][4], - bArmature *arm, - std::vector &layer_labels) -{ - float mat[4][4]; - float joint_inv_bind_mat[4][4]; - float joint_bind_mat[4][4]; - int chain_length = 0; - - /* Checking if bone is already made. */ - std::vector::iterator it; - it = std::find(finished_joints.begin(), finished_joints.end(), node); - if (it != finished_joints.end()) { - return chain_length; - } - - EditBone *bone = ED_armature_ebone_add(arm, bc_get_joint_name(node)); - totbone++; - - /* - * We use the inv_bind_shape matrix to apply the armature bind pose as its rest pose. - */ - - std::map::iterator skin_it; - bool bone_is_skinned = false; - for (skin_it = skin_by_data_uid.begin(); skin_it != skin_by_data_uid.end(); skin_it++) { - - SkinInfo *b = &skin_it->second; - if (b->get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { - - /* get original world-space matrix */ - invert_m4_m4(mat, joint_inv_bind_mat); - copy_m4_m4(joint_bind_mat, mat); - /* And make local to armature */ - Object *ob_arm = skin->BKE_armature_from_object(); - if (ob_arm) { - float invmat[4][4]; - invert_m4_m4(invmat, ob_arm->obmat); - mul_m4_m4m4(mat, invmat, mat); - } - - bone_is_skinned = true; - break; - } - } - - /* create a bone even if there's no joint data for it (i.e. it has no influence) */ - if (!bone_is_skinned) { - get_node_mat(mat, node, NULL, NULL, parent_mat); - } - - if (parent) { - bone->parent = parent; - } - - float loc[3], size[3], rot[3][3]; - BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(arm); - BoneExtended &be = add_bone_extended(bone, node, totchild, layer_labels, extended_bones); - int layer = be.get_bone_layers(); - if (layer) { - bone->layer = layer; - } - arm->layer |= layer; // ensure that all populated bone layers are visible after import - - float *tail = be.get_tail(); - int use_connect = be.get_use_connect(); - - switch (use_connect) { - case 1: - bone->flag |= BONE_CONNECTED; - break; - case -1: /* Connect type not specified */ - case 0: - bone->flag &= ~BONE_CONNECTED; - break; - } - - if (be.has_roll()) { - bone->roll = be.get_roll(); - } - else { - float angle; - mat4_to_loc_rot_size(loc, rot, size, mat); - mat3_to_vec_roll(rot, NULL, &angle); - bone->roll = angle; - } - copy_v3_v3(bone->head, mat[3]); - - if (bone_is_skinned && this->import_settings->keep_bind_info) { - float rest_mat[4][4]; - get_node_mat(rest_mat, node, NULL, NULL, NULL); - bc_set_IDPropertyMatrix(bone, "bind_mat", joint_bind_mat); - bc_set_IDPropertyMatrix(bone, "rest_mat", rest_mat); - } - - add_v3_v3v3(bone->tail, bone->head, tail); /* tail must be non zero */ - - /* find smallest bone length in armature (used later for leaf bone length) */ - if (parent) { - - if (use_connect == 1) { - copy_v3_v3(parent->tail, bone->head); - } - - /* guess reasonable leaf bone length */ - float length = len_v3v3(parent->head, bone->head); - if ((length < leaf_bone_length || totbone == 0) && length > MINIMUM_BONE_LENGTH) { - leaf_bone_length = length; - } - } - - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - - for (unsigned int i = 0; i < children.getCount(); i++) { - int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm, layer_labels); - if (cl > chain_length) { - chain_length = cl; - } - } - - bone->length = len_v3v3(bone->head, bone->tail); - joint_by_uid[node->getUniqueId()] = node; - finished_joints.push_back(node); - - be.set_chain_length(chain_length + 1); - - return chain_length + 1; -} - -/** - * Collada only knows Joints, hence bones at the end of a bone chain - * don't have a defined length. This function guesses reasonable - * tail locations for the affected bones (nodes which don't have any connected child) - * Hint: The extended_bones set gets populated in ArmatureImporter::create_bone - */ -void ArmatureImporter::fix_leaf_bone_hierarchy(bArmature *armature, - Bone *bone, - bool fix_orientation) -{ - if (bone == NULL) { - return; - } - - if (bc_is_leaf_bone(bone)) { - BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(armature); - BoneExtended *be = extended_bones[bone->name]; - EditBone *ebone = bc_get_edit_bone(armature, bone->name); - fix_leaf_bone(armature, ebone, be, fix_orientation); - } - - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - fix_leaf_bone_hierarchy(armature, child, fix_orientation); - } -} - -void ArmatureImporter::fix_leaf_bone(bArmature *armature, - EditBone *ebone, - BoneExtended *be, - bool fix_orientation) -{ - if (be == NULL || !be->has_tail()) { - - /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ - float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; - - float vec[3]; - - if (fix_orientation && ebone->parent != NULL) { - EditBone *parent = ebone->parent; - sub_v3_v3v3(vec, ebone->head, parent->head); - if (len_squared_v3(vec) < MINIMUM_BONE_LENGTH) { - sub_v3_v3v3(vec, parent->tail, parent->head); - } - } - else { - vec[2] = 0.1f; - sub_v3_v3v3(vec, ebone->tail, ebone->head); - } - - normalize_v3_v3(vec, vec); - mul_v3_fl(vec, leaf_length); - add_v3_v3v3(ebone->tail, ebone->head, vec); - } -} - -void ArmatureImporter::fix_parent_connect(bArmature *armature, Bone *bone) -{ - /* armature has no bones */ - if (bone == NULL) { - return; - } - - if (bone->parent && bone->flag & BONE_CONNECTED) { - copy_v3_v3(bone->parent->tail, bone->head); - } - - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - fix_parent_connect(armature, child); - } -} - -void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone, int clip) -{ - BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(armature); - BoneExtended *dominant_child = NULL; - int maxlen = 0; - - if (parentbone == NULL) { - return; - } - - Bone *child = (Bone *)parentbone->childbase.first; - if (child && (import_settings->find_chains || child->next == NULL)) { - for (; child; child = child->next) { - BoneExtended *be = extended_bones[child->name]; - if (be != NULL) { - int chain_len = be->get_chain_length(); - if (chain_len <= clip) { - if (chain_len > maxlen) { - dominant_child = be; - maxlen = chain_len; - } - else if (chain_len == maxlen) { - dominant_child = NULL; - } - } - } - } - } - - BoneExtended *pbe = extended_bones[parentbone->name]; - if (dominant_child != NULL) { - /* Found a valid chain. Now connect current bone with that chain.*/ - EditBone *pebone = bc_get_edit_bone(armature, parentbone->name); - EditBone *cebone = bc_get_edit_bone(armature, dominant_child->get_name()); - if (pebone && !(cebone->flag & BONE_CONNECTED)) { - float vec[3]; - sub_v3_v3v3(vec, cebone->head, pebone->head); - - /* - * It is possible that the child's head is located on the parents head. - * When this happens, then moving the parent's tail to the child's head - * would result in a zero sized bone and Blender would silently remove the bone. - * So we move the tail only when the resulting bone has a minimum length: - */ - - if (len_squared_v3(vec) > MINIMUM_BONE_LENGTH) { - copy_v3_v3(pebone->tail, cebone->head); - pbe->set_tail(pebone->tail); /* to make fix_leafbone happy ...*/ - if (pbe && pbe->get_chain_length() >= this->import_settings->min_chain_length) { - - BoneExtended *cbe = extended_bones[cebone->name]; - cbe->set_use_connect(true); - - cebone->flag |= BONE_CONNECTED; - pbe->set_leaf_bone(false); - printf("Connect Bone chain: parent (%s --> %s) child)\n", pebone->name, cebone->name); - } - } - } - for (Bone *ch = (Bone *)parentbone->childbase.first; ch; ch = ch->next) { - ArmatureImporter::connect_bone_chains(armature, ch, UNLIMITED_CHAIN_MAX); - } - } - else if (maxlen > 1 && maxlen > this->import_settings->min_chain_length) { - /* Try again with smaller chain length */ - ArmatureImporter::connect_bone_chains(armature, parentbone, maxlen - 1); - } - else { - /* can't connect this Bone. Proceed with children ... */ - if (pbe) { - pbe->set_leaf_bone(true); - } - for (Bone *ch = (Bone *)parentbone->childbase.first; ch; ch = ch->next) { - ArmatureImporter::connect_bone_chains(armature, ch, UNLIMITED_CHAIN_MAX); - } - } -} - -#if 0 -void ArmatureImporter::set_leaf_bone_shapes(Object *ob_arm) -{ - bPose *pose = ob_arm->pose; - - std::vector::iterator it; - for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { - LeafBone &leaf = *it; - - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, leaf.name); - if (pchan) { - pchan->custom = get_empty_for_leaves(); - } - else { - fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name); - } - } -} - -void ArmatureImporter::set_euler_rotmode() -{ - /* just set rotmode = ROT_MODE_EUL on pose channel for each joint */ - - std::map::iterator it; - - for (it = joint_by_uid.begin(); it != joint_by_uid.end(); it++) { - - COLLADAFW::Node *joint = it->second; - - std::map::iterator sit; - - for (sit = skin_by_data_uid.begin(); sit != skin_by_data_uid.end(); sit++) { - SkinInfo &skin = sit->second; - - if (skin.uses_joint_or_descendant(joint)) { - bPoseChannel *pchan = skin.get_pose_channel_from_node(joint); - - if (pchan) { - pchan->rotmode = ROT_MODE_EUL; - } - else { - fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint)); - } - - break; - } - } - } -} -#endif - -Object *ArmatureImporter::get_empty_for_leaves() -{ - if (empty) { - return empty; - } - - empty = bc_add_object(m_bmain, scene, view_layer, OB_EMPTY, NULL); - empty->empty_drawtype = OB_EMPTY_SPHERE; - - return empty; -} - -#if 0 -Object *ArmatureImporter::find_armature(COLLADAFW::Node *node) -{ - JointData *jd = get_joint_data(node); - if (jd) { - return jd->ob_arm; - } - - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - for (int i = 0; i < children.getCount(); i++) { - Object *ob_arm = find_armature(children[i]); - if (ob_arm) { - return ob_arm; - } - } - - return NULL; -} - -ArmatureJoints &ArmatureImporter::get_armature_joints(Object *ob_arm) -{ - /* try finding it */ - std::vector::iterator it; - for (it = armature_joints.begin(); it != armature_joints.end(); it++) { - if ((*it).ob_arm == ob_arm) { - return *it; - } - } - - /* not found, create one */ - ArmatureJoints aj; - aj.ob_arm = ob_arm; - armature_joints.push_back(aj); - - return armature_joints.back(); -} -#endif -void ArmatureImporter::create_armature_bones(Main *bmain, std::vector &ob_arms) -{ - std::vector::iterator ri; - std::vector layer_labels; - - /* if there is an armature created for root_joint next root_joint */ - for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { - COLLADAFW::Node *node = *ri; - if (get_armature_for_joint(node) != NULL) { - continue; - } - - Object *ob_arm = joint_parent_map[node->getUniqueId()]; - if (!ob_arm) { - continue; - } - - bArmature *armature = (bArmature *)ob_arm->data; - if (!armature) { - continue; - } - - char *bone_name = (char *)bc_get_joint_name(node); - Bone *bone = BKE_armature_find_bone_name(armature, bone_name); - if (bone) { - fprintf(stderr, - "Reuse of child bone [%s] as root bone in same Armature is not supported.\n", - bone_name); - continue; - } - - ED_armature_to_edit(armature); - armature->layer = 0; // layer is set according to imported bone set in create_bone() - - create_bone(NULL, node, NULL, node->getChildNodes().getCount(), NULL, armature, layer_labels); - if (this->import_settings->find_chains) { - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - } - - /* exit armature edit mode to populate the Armature object */ - ED_armature_from_edit(bmain, armature); - ED_armature_edit_free(armature); - ED_armature_to_edit(armature); - - fix_leaf_bone_hierarchy( - armature, (Bone *)armature->bonebase.first, this->import_settings->fix_orientation); - unskinned_armature_map[node->getUniqueId()] = ob_arm; - - ED_armature_from_edit(bmain, armature); - ED_armature_edit_free(armature); - - set_bone_transformation_type(node, ob_arm); - - int index = std::find(ob_arms.begin(), ob_arms.end(), ob_arm) - ob_arms.begin(); - if (index == 0) { - ob_arms.push_back(ob_arm); - } - - DEG_id_tag_update(&ob_arm->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - } -} - -Object *ArmatureImporter::create_armature_bones(Main *bmain, SkinInfo &skin) -{ - /* just do like so: - * - get armature - * - enter editmode - * - add edit bones and head/tail properties using matrices and parent-child info - * - exit edit mode - * - set a sphere shape to leaf bones */ - - Object *ob_arm = NULL; - - /* - * find if there's another skin sharing at least one bone with this skin - * if so, use that skin's armature - */ - - /** - * Pseudocode: - * - * find_node_in_tree(node, root_joint) - * - * skin::find_root_joints(root_joints): - * std::vector root_joints; - * for each root in root_joints: - * for each joint in joints: - * if find_node_in_tree(joint, root): - * if (std::find(root_joints.begin(), root_joints.end(), root) == - * root_joints.end()) root_joints.push_back(root); - * - * for (each skin B with armature) { - * find all root joints for skin B - * - * for each joint X in skin A: - * for each root joint R in skin B: - * if (find_node_in_tree(X, R)) { - * shared = 1; - * goto endloop; - * } - * } - * - * endloop: - */ - - SkinInfo *a = &skin; - Object *shared = NULL; - std::vector skin_root_joints; - std::vector layer_labels; - - std::map::iterator it; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - SkinInfo *b = &it->second; - if (b == a || b->BKE_armature_from_object() == NULL) { - continue; - } - - skin_root_joints.clear(); - - b->find_root_joints(root_joints, joint_by_uid, skin_root_joints); - - std::vector::iterator ri; - for (ri = skin_root_joints.begin(); ri != skin_root_joints.end(); ri++) { - COLLADAFW::Node *node = *ri; - if (a->uses_joint_or_descendant(node)) { - shared = b->BKE_armature_from_object(); - break; - } - } - - if (shared != NULL) { - break; - } - } - - if (!shared && this->joint_parent_map.size() > 0) { - /* All armatures have been created while creating the Node tree. - * The Collada exporter currently does not create a - * strict relationship between geometries and armatures - * So when we reimport a Blender collada file, then we have - * to guess what is meant. - * XXX This is not safe when we have more than one armatures - * in the import. */ - shared = this->joint_parent_map.begin()->second; - } - - if (shared) { - ob_arm = skin.set_armature(shared); - } - else { - ob_arm = skin.create_armature(m_bmain, scene, view_layer); // once for every armature - } - - /* enter armature edit mode */ - bArmature *armature = (bArmature *)ob_arm->data; - ED_armature_to_edit(armature); - - totbone = 0; - // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on - /* default row */ - - /* create bones */ - /* TODO: - * check if bones have already been created for a given joint */ - - std::vector::iterator ri; - for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { - COLLADAFW::Node *node = *ri; - /* for shared armature check if bone tree is already created */ - if (shared && std::find(skin_root_joints.begin(), skin_root_joints.end(), node) != - skin_root_joints.end()) { - continue; - } - - /* since root_joints may contain joints for multiple controllers, we need to filter */ - if (skin.uses_joint_or_descendant(node)) { - - create_bone( - &skin, node, NULL, node->getChildNodes().getCount(), NULL, armature, layer_labels); - - if (joint_parent_map.find(node->getUniqueId()) != joint_parent_map.end() && - !skin.get_parent()) { - skin.set_parent(joint_parent_map[node->getUniqueId()]); - } - } - } - - /* exit armature edit mode to populate the Armature object */ - ED_armature_from_edit(bmain, armature); - ED_armature_edit_free(armature); - - for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { - COLLADAFW::Node *node = *ri; - set_bone_transformation_type(node, ob_arm); - } - - ED_armature_to_edit(armature); - if (this->import_settings->find_chains) { - connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); - } - fix_leaf_bone_hierarchy( - armature, (Bone *)armature->bonebase.first, this->import_settings->fix_orientation); - ED_armature_from_edit(bmain, armature); - ED_armature_edit_free(armature); - - DEG_id_tag_update(&ob_arm->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); - - return ob_arm; -} - -void ArmatureImporter::set_bone_transformation_type(const COLLADAFW::Node *node, Object *ob_arm) -{ - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bc_get_joint_name(node)); - if (pchan) { - pchan->rotmode = (node_is_decomposed(node)) ? ROT_MODE_EUL : ROT_MODE_QUAT; - } - - COLLADAFW::NodePointerArray childnodes = node->getChildNodes(); - for (int index = 0; index < childnodes.getCount(); index++) { - node = childnodes[index]; - set_bone_transformation_type(node, ob_arm); - } -} - -void ArmatureImporter::set_pose(Object *ob_arm, - COLLADAFW::Node *root_node, - const char *parentname, - float parent_mat[4][4]) -{ - const char *bone_name = bc_get_joint_name(root_node); - float mat[4][4]; - float obmat[4][4]; - - /* object-space */ - get_node_mat(obmat, root_node, NULL, NULL); - bool is_decomposed = node_is_decomposed(root_node); - - // if (*edbone) - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone_name); - pchan->rotmode = (is_decomposed) ? ROT_MODE_EUL : ROT_MODE_QUAT; - - // else fprintf ( "", - - /* get world-space */ - if (parentname) { - mul_m4_m4m4(mat, parent_mat, obmat); - bPoseChannel *parchan = BKE_pose_channel_find_name(ob_arm->pose, parentname); - - mul_m4_m4m4(pchan->pose_mat, parchan->pose_mat, mat); - } - else { - - copy_m4_m4(mat, obmat); - float invObmat[4][4]; - invert_m4_m4(invObmat, ob_arm->obmat); - mul_m4_m4m4(pchan->pose_mat, invObmat, mat); - } - -#if 0 - float angle = 0.0f; - mat4_to_axis_angle(ax, &angle, mat); - pchan->bone->roll = angle; -#endif - - COLLADAFW::NodePointerArray &children = root_node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - set_pose(ob_arm, children[i], bone_name, mat); - } -} - -bool ArmatureImporter::node_is_decomposed(const COLLADAFW::Node *node) -{ - const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); - for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { - COLLADAFW::Transformation *transform = nodeTransforms[i]; - COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); - if (tm_type == COLLADAFW::Transformation::MATRIX) { - return false; - } - } - return true; -} - -/** - * root - if this joint is the top joint in hierarchy, if a joint - * is a child of a node (not joint), root should be true since - * this is where we build armature bones from - */ -void ArmatureImporter::add_root_joint(COLLADAFW::Node *node, Object *parent) -{ - root_joints.push_back(node); - if (parent) { - joint_parent_map[node->getUniqueId()] = parent; - } -} - -#if 0 -void ArmatureImporter::add_root_joint(COLLADAFW::Node *node) -{ - // root_joints.push_back(node); - Object *ob_arm = find_armature(node); - if (ob_arm) { - get_armature_joints(ob_arm).root_joints.push_back(node); - } -# ifdef COLLADA_DEBUG - else { - fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node)); - } -# endif -} -#endif - -/* here we add bones to armatures, having armatures previously created in write_controller */ -void ArmatureImporter::make_armatures(bContext *C, std::vector &objects_to_scale) -{ - Main *bmain = CTX_data_main(C); - std::vector ob_arms; - std::map::iterator it; - - /* TODO: Make this work for more than one armature in the import file. */ - leaf_bone_length = FLT_MAX; - - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - - SkinInfo &skin = it->second; - - Object *ob_arm = create_armature_bones(bmain, skin); - - /* link armature with a mesh object */ - const COLLADAFW::UniqueId &uid = skin.get_controller_uid(); - const COLLADAFW::UniqueId *guid = get_geometry_uid(uid); - if (guid != NULL) { - Object *ob = mesh_importer->get_object_by_geom_uid(*guid); - if (ob) { - skin.link_armature(C, ob, joint_by_uid, this); - - std::vector::iterator ob_it = std::find( - objects_to_scale.begin(), objects_to_scale.end(), ob); - - if (ob_it != objects_to_scale.end()) { - int index = ob_it - objects_to_scale.begin(); - objects_to_scale.erase(objects_to_scale.begin() + index); - } - - if (std::find(objects_to_scale.begin(), objects_to_scale.end(), ob_arm) == - objects_to_scale.end()) { - objects_to_scale.push_back(ob_arm); - } - - if (std::find(ob_arms.begin(), ob_arms.end(), ob_arm) == ob_arms.end()) { - ob_arms.push_back(ob_arm); - } - } - else { - fprintf(stderr, "Cannot find object to link armature with.\n"); - } - } - else { - fprintf(stderr, "Cannot find geometry to link armature with.\n"); - } - - /* set armature parent if any */ - Object *par = skin.get_parent(); - if (par) { - bc_set_parent(skin.BKE_armature_from_object(), par, C, false); - } - - /* free memory stolen from SkinControllerData */ - skin.free(); - } - - /* for bones without skins */ - create_armature_bones(bmain, ob_arms); - - /* Fix bone relations */ - std::vector::iterator ob_arm_it; - for (ob_arm_it = ob_arms.begin(); ob_arm_it != ob_arms.end(); ob_arm_it++) { - - Object *ob_arm = *ob_arm_it; - bArmature *armature = (bArmature *)ob_arm->data; - - /* and step back to edit mode to fix the leaf nodes */ - ED_armature_to_edit(armature); - - fix_parent_connect(armature, (Bone *)armature->bonebase.first); - - ED_armature_from_edit(bmain, armature); - ED_armature_edit_free(armature); - } -} - -#if 0 -/* link with meshes, create vertex groups, assign weights */ -void ArmatureImporter::link_armature(Object *ob_arm, - const COLLADAFW::UniqueId &geom_id, - const COLLADAFW::UniqueId &controller_data_id) -{ - Object *ob = mesh_importer->get_object_by_geom_uid(geom_id); - - if (!ob) { - fprintf(stderr, "Cannot find object by geometry UID.\n"); - return; - } - - if (skin_by_data_uid.find(controller_data_id) == skin_by_data_uid.end()) { - fprintf(stderr, "Cannot find skin info by controller data UID.\n"); - return; - } - - SkinInfo &skin = skin_by_data_uid[conroller_data_id]; - - /* create vertex groups */ -} -#endif - -bool ArmatureImporter::write_skin_controller_data(const COLLADAFW::SkinControllerData *data) -{ - /* at this stage we get vertex influence info that should go into me->verts and ob->defbase - * there's no info to which object this should be long so we associate it with - * skin controller data UID. */ - - /* don't forget to call BKE_object_defgroup_unique_name before we copy */ - - /* controller data uid -> [armature] -> joint data, - * [mesh object] */ - - SkinInfo skin(unit_converter); - skin.borrow_skin_controller_data(data); - - /* store join inv bind matrix to use it later in armature construction */ - const COLLADAFW::Matrix4Array &inv_bind_mats = data->getInverseBindMatrices(); - for (unsigned int i = 0; i < data->getJointsCount(); i++) { - skin.add_joint(inv_bind_mats[i]); - } - - skin_by_data_uid[data->getUniqueId()] = skin; - - return true; -} - -bool ArmatureImporter::write_controller(const COLLADAFW::Controller *controller) -{ - /* - create and store armature object */ - const COLLADAFW::UniqueId &con_id = controller->getUniqueId(); - - if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) { - COLLADAFW::SkinController *co = (COLLADAFW::SkinController *)controller; - /* to be able to find geom id by controller id */ - geom_uid_by_controller_uid[con_id] = co->getSource(); - - const COLLADAFW::UniqueId &data_uid = co->getSkinControllerData(); - if (skin_by_data_uid.find(data_uid) == skin_by_data_uid.end()) { - fprintf(stderr, "Cannot find skin by controller data UID.\n"); - return true; - } - - skin_by_data_uid[data_uid].set_controller(co); - } - /* morph controller */ - else if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_MORPH) { - COLLADAFW::MorphController *co = (COLLADAFW::MorphController *)controller; - /* to be able to find geom id by controller id */ - geom_uid_by_controller_uid[con_id] = co->getSource(); - /* Shape keys are applied in DocumentImporter->finish() */ - morph_controllers.push_back(co); - } - - return true; -} - -void ArmatureImporter::make_shape_keys(bContext *C) -{ - Main *bmain = CTX_data_main(C); - std::vector::iterator mc; - float weight; - - for (mc = morph_controllers.begin(); mc != morph_controllers.end(); mc++) { - /* Controller data */ - COLLADAFW::UniqueIdArray &morphTargetIds = (*mc)->getMorphTargets(); - COLLADAFW::FloatOrDoubleArray &morphWeights = (*mc)->getMorphWeights(); - - /* Prereq: all the geometries must be imported and mesh objects must be made */ - Object *source_ob = this->mesh_importer->get_object_by_geom_uid((*mc)->getSource()); - - if (source_ob) { - - Mesh *source_me = (Mesh *)source_ob->data; - /* insert key to source mesh */ - Key *key = source_me->key = BKE_key_add(bmain, (ID *)source_me); - key->type = KEY_RELATIVE; - KeyBlock *kb; - - /* insert basis key */ - kb = BKE_keyblock_add_ctime(key, "Basis", false); - BKE_keyblock_convert_from_mesh(source_me, key, kb); - - /* insert other shape keys */ - for (int i = 0; i < morphTargetIds.getCount(); i++) { - /* better to have a separate map of morph objects, - * This'll do for now since only mesh morphing is imported */ - - Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]); - - if (me) { - me->key = key; - std::string morph_name = *this->mesh_importer->get_geometry_name(me->id.name); - - kb = BKE_keyblock_add_ctime(key, morph_name.c_str(), false); - BKE_keyblock_convert_from_mesh(me, key, kb); - - /* apply weights */ - weight = morphWeights.getFloatValues()->getData()[i]; - kb->curval = weight; - } - else { - fprintf(stderr, "Morph target geometry not found.\n"); - } - } - } - else { - fprintf(stderr, "Morph target object not found.\n"); - } - } -} - -COLLADAFW::UniqueId *ArmatureImporter::get_geometry_uid(const COLLADAFW::UniqueId &controller_uid) -{ - if (geom_uid_by_controller_uid.find(controller_uid) == geom_uid_by_controller_uid.end()) { - return NULL; - } - - return &geom_uid_by_controller_uid[controller_uid]; -} - -Object *ArmatureImporter::get_armature_for_joint(COLLADAFW::Node *node) -{ - std::map::iterator it; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - SkinInfo &skin = it->second; - - if (skin.uses_joint_or_descendant(node)) { - return skin.BKE_armature_from_object(); - } - } - - std::map::iterator arm; - for (arm = unskinned_armature_map.begin(); arm != unskinned_armature_map.end(); arm++) { - if (arm->first == node->getUniqueId()) { - return arm->second; - } - } - return NULL; -} - -void ArmatureImporter::set_tags_map(TagsMap &tagsMap) -{ - this->uid_tags_map = tagsMap; -} - -void ArmatureImporter::get_rna_path_for_joint(COLLADAFW::Node *node, - char *joint_path, - size_t count) -{ - BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bc_get_joint_name(node)); -} - -/* gives a world-space mat */ -bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) -{ - std::map::iterator it; - bool found = false; - for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { - SkinInfo &skin = it->second; - if ((found = skin.get_joint_inv_bind_matrix(m, joint))) { - invert_m4(m); - break; - } - } - - return found; -} - -BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, - COLLADAFW::Node *node, - int sibcount, - std::vector &layer_labels, - BoneExtensionMap &extended_bones) -{ - BoneExtended *be = new BoneExtended(bone); - extended_bones[bone->name] = be; - - TagsMap::iterator etit; - ExtraTags *et = 0; - etit = uid_tags_map.find(node->getUniqueId().toAscii()); - - bool has_connect = false; - int connect_type = -1; - - if (etit != uid_tags_map.end()) { - - float tail[3] = {FLT_MAX, FLT_MAX, FLT_MAX}; - float roll = 0; - std::string layers; - - et = etit->second; - - bool has_tail = false; - has_tail |= et->setData("tip_x", &tail[0]); - has_tail |= et->setData("tip_y", &tail[1]); - has_tail |= et->setData("tip_z", &tail[2]); - - has_connect = et->setData("connect", &connect_type); - bool has_roll = et->setData("roll", &roll); - - layers = et->setData("layer", layers); - - if (has_tail && !has_connect) { - /* got a bone tail definition but no connect info -> bone is not connected */ - has_connect = true; - connect_type = 0; - } - - be->set_bone_layers(layers, layer_labels); - if (has_tail) { - be->set_tail(tail); - } - if (has_roll) { - be->set_roll(roll); - } - } - - if (!has_connect && this->import_settings->auto_connect) { - /* auto connect only whyen parent has exactly one child*/ - connect_type = sibcount == 1; - } - - be->set_use_connect(connect_type); - be->set_leaf_bone(true); - - return *be; -} diff --git a/source/blender/collada/ArmatureImporter.h b/source/blender/collada/ArmatureImporter.h deleted file mode 100644 index da92c04e5dc..00000000000 --- a/source/blender/collada/ArmatureImporter.h +++ /dev/null @@ -1,187 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __ARMATUREIMPORTER_H__ -#define __ARMATUREIMPORTER_H__ - -#include "COLLADAFWNode.h" -#include "COLLADAFWUniqueId.h" -#include "COLLADAFWMorphController.h" - -extern "C" { -#include "BKE_context.h" -#include "BKE_key.h" - -#include "DNA_armature_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_key_types.h" - -#include "ED_armature.h" -} - -#include "AnimationImporter.h" -#include "MeshImporter.h" -#include "SkinInfo.h" -#include "TransformReader.h" -#include "ExtraTags.h" - -#include -#include - -#include "collada_internal.h" -#include "collada_utils.h" -#include "ImportSettings.h" - -#define UNLIMITED_CHAIN_MAX INT_MAX -#define MINIMUM_BONE_LENGTH 0.000001f - -class ArmatureImporter : private TransformReader { - private: - Main *m_bmain; - Scene *scene; - ViewLayer *view_layer; - UnitConverter *unit_converter; - const ImportSettings *import_settings; - - // std::map joint_index_to_joint_info_map; - // std::map joint_id_to_joint_index_map; - BoneExtensionManager bone_extension_manager; - // int bone_direction_row; // XXX not used - float leaf_bone_length; - int totbone; - // XXX not used - // float min_angle; // minimum angle between bone head-tail and a row of bone matrix - -#if 0 - struct ArmatureJoints { - Object *ob_arm; - std::vector root_joints; - }; - std::vector armature_joints; -#endif - - Object *empty; // empty for leaf bones - - std::map geom_uid_by_controller_uid; - std::map joint_by_uid; // contains all joints - std::vector root_joints; - std::vector finished_joints; - std::vector morph_controllers; - std::map joint_parent_map; - std::map unskinned_armature_map; - - MeshImporterBase *mesh_importer; - - // This is used to store data passed in write_controller_data. - // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members - // so that arrays don't get freed until we free them explicitly. - - std::map skin_by_data_uid; // data UID = skin controller data UID -#if 0 - JointData *get_joint_data(COLLADAFW::Node *node); -#endif - - int create_bone(SkinInfo *skin, - COLLADAFW::Node *node, - EditBone *parent, - int totchild, - float parent_mat[4][4], - bArmature *arm, - std::vector &layer_labels); - - BoneExtended &add_bone_extended(EditBone *bone, - COLLADAFW::Node *node, - int sibcount, - std::vector &layer_labels, - BoneExtensionMap &extended_bones); - - void fix_leaf_bone_hierarchy(bArmature *armature, Bone *bone, bool fix_orientation); - void fix_leaf_bone(bArmature *armature, EditBone *ebone, BoneExtended *be, bool fix_orientation); - void fix_parent_connect(bArmature *armature, Bone *bone); - void connect_bone_chains(bArmature *armature, Bone *bone, const int max_chain_length); - - void set_pose(Object *ob_arm, - COLLADAFW::Node *root_node, - const char *parentname, - float parent_mat[4][4]); - - void set_bone_transformation_type(const COLLADAFW::Node *node, Object *ob_arm); - bool node_is_decomposed(const COLLADAFW::Node *node); -#if 0 - void set_leaf_bone_shapes(Object *ob_arm); - void set_euler_rotmode(); -#endif - - Object *get_empty_for_leaves(); - -#if 0 - Object *find_armature(COLLADAFW::Node *node); - - ArmatureJoints &get_armature_joints(Object *ob_arm); -#endif - - Object *create_armature_bones(Main *bmain, SkinInfo &skin); - void create_armature_bones(Main *bmain, std::vector &arm_objs); - - /** TagsMap typedef for uid_tags_map. */ - typedef std::map TagsMap; - TagsMap uid_tags_map; - - public: - ArmatureImporter(UnitConverter *conv, - MeshImporterBase *mesh, - Main *bmain, - Scene *sce, - ViewLayer *view_layer, - const ImportSettings *import_settings); - ~ArmatureImporter(); - - void add_root_joint(COLLADAFW::Node *node, Object *parent); - - // here we add bones to armatures, having armatures previously created in write_controller - void make_armatures(bContext *C, std::vector &objects_to_scale); - - void make_shape_keys(bContext *C); - -#if 0 - // link with meshes, create vertex groups, assign weights - void link_armature(Object *ob_arm, - const COLLADAFW::UniqueId &geom_id, - const COLLADAFW::UniqueId &controller_data_id); -#endif - - bool write_skin_controller_data(const COLLADAFW::SkinControllerData *data); - - bool write_controller(const COLLADAFW::Controller *controller); - - COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId &controller_uid); - - Object *get_armature_for_joint(COLLADAFW::Node *node); - - void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count); - - // gives a world-space mat - bool get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint); - - void set_tags_map(TagsMap &tags_map); -}; - -#endif diff --git a/source/blender/collada/BCAnimationCurve.cpp b/source/blender/collada/BCAnimationCurve.cpp deleted file mode 100644 index 36800d611d2..00000000000 --- a/source/blender/collada/BCAnimationCurve.cpp +++ /dev/null @@ -1,691 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#include "BCAnimationCurve.h" - -BCAnimationCurve::BCAnimationCurve() -{ - this->curve_key.set_object_type(BC_ANIMATION_TYPE_OBJECT); - this->fcurve = NULL; - this->curve_is_local_copy = false; -} - -BCAnimationCurve::BCAnimationCurve(const BCAnimationCurve &other) -{ - this->min = other.min; - this->max = other.max; - this->fcurve = other.fcurve; - this->curve_key = other.curve_key; - this->curve_is_local_copy = false; - this->id_ptr = other.id_ptr; - - /* The fcurve of the new instance is a copy and can be modified */ - - get_edit_fcurve(); -} - -BCAnimationCurve::BCAnimationCurve(BCCurveKey key, Object *ob, FCurve *fcu) -{ - this->min = 0; - this->max = 0; - this->curve_key = key; - this->fcurve = fcu; - this->curve_is_local_copy = false; - init_pointer_rna(ob); -} - -BCAnimationCurve::BCAnimationCurve(const BCCurveKey &key, Object *ob) -{ - this->curve_key = key; - this->fcurve = NULL; - this->curve_is_local_copy = false; - init_pointer_rna(ob); -} - -void BCAnimationCurve::init_pointer_rna(Object *ob) -{ - switch (this->curve_key.get_animation_type()) { - case BC_ANIMATION_TYPE_BONE: { - bArmature *arm = (bArmature *)ob->data; - RNA_id_pointer_create(&arm->id, &id_ptr); - } break; - case BC_ANIMATION_TYPE_OBJECT: { - RNA_id_pointer_create(&ob->id, &id_ptr); - } break; - case BC_ANIMATION_TYPE_MATERIAL: { - Material *ma = BKE_object_material_get(ob, curve_key.get_subindex() + 1); - RNA_id_pointer_create(&ma->id, &id_ptr); - } break; - case BC_ANIMATION_TYPE_CAMERA: { - Camera *camera = (Camera *)ob->data; - RNA_id_pointer_create(&camera->id, &id_ptr); - } break; - case BC_ANIMATION_TYPE_LIGHT: { - Light *lamp = (Light *)ob->data; - RNA_id_pointer_create(&lamp->id, &id_ptr); - } break; - default: - fprintf( - stderr, "BC_animation_curve_type %d not supported", this->curve_key.get_array_index()); - break; - } -} - -void BCAnimationCurve::delete_fcurve(FCurve *fcu) -{ - free_fcurve(fcu); -} - -FCurve *BCAnimationCurve::create_fcurve(int array_index, const char *rna_path) -{ - FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); - fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); - fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); - fcu->array_index = array_index; - return fcu; -} - -void BCAnimationCurve::create_bezt(float frame, float output) -{ - FCurve *fcu = get_edit_fcurve(); - BezTriple bez; - memset(&bez, 0, sizeof(BezTriple)); - bez.vec[1][0] = frame; - bez.vec[1][1] = output; - bez.ipo = U.ipo_new; /* use default interpolation mode here... */ - bez.f1 = bez.f2 = bez.f3 = SELECT; - bez.h1 = bez.h2 = HD_AUTO; - insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); - calchandles_fcurve(fcu); -} - -BCAnimationCurve::~BCAnimationCurve() -{ - if (curve_is_local_copy && fcurve) { - // fprintf(stderr, "removed fcurve %s\n", fcurve->rna_path); - delete_fcurve(fcurve); - this->fcurve = NULL; - } -} - -const bool BCAnimationCurve::is_of_animation_type(BC_animation_type type) const -{ - return curve_key.get_animation_type() == type; -} - -const std::string BCAnimationCurve::get_channel_target() const -{ - const std::string path = curve_key.get_path(); - - if (bc_startswith(path, "pose.bones")) { - return bc_string_after(path, "pose.bones"); - } - return bc_string_after(path, "."); -} - -const std::string BCAnimationCurve::get_channel_type() const -{ - const std::string channel = get_channel_target(); - return bc_string_after(channel, "."); -} - -const std::string BCAnimationCurve::get_channel_posebone() const -{ - const std::string channel = get_channel_target(); - std::string pose_bone_name = bc_string_before(channel, "."); - if (pose_bone_name == channel) { - pose_bone_name = ""; - } - else { - pose_bone_name = bc_string_after(pose_bone_name, "\"["); - pose_bone_name = bc_string_before(pose_bone_name, "]\""); - } - return pose_bone_name; -} - -const std::string BCAnimationCurve::get_animation_name(Object *ob) const -{ - std::string name; - - switch (curve_key.get_animation_type()) { - case BC_ANIMATION_TYPE_OBJECT: { - name = id_name(ob); - } break; - - case BC_ANIMATION_TYPE_BONE: { - if (fcurve == NULL || fcurve->rna_path == NULL) { - name = ""; - } - else { - const char *boneName = BLI_str_quoted_substrN(fcurve->rna_path, "pose.bones["); - name = (boneName) ? id_name(ob) + "_" + std::string(boneName) : ""; - } - } break; - - case BC_ANIMATION_TYPE_CAMERA: { - Camera *camera = (Camera *)ob->data; - name = id_name(ob) + "-" + id_name(camera) + "-camera"; - } break; - - case BC_ANIMATION_TYPE_LIGHT: { - Light *lamp = (Light *)ob->data; - name = id_name(ob) + "-" + id_name(lamp) + "-light"; - } break; - - case BC_ANIMATION_TYPE_MATERIAL: { - Material *ma = BKE_object_material_get(ob, this->curve_key.get_subindex() + 1); - name = id_name(ob) + "-" + id_name(ma) + "-material"; - } break; - - default: { - name = ""; - } - } - - return name; -} - -const int BCAnimationCurve::get_channel_index() const -{ - return curve_key.get_array_index(); -} - -const int BCAnimationCurve::get_subindex() const -{ - return curve_key.get_subindex(); -} - -const std::string BCAnimationCurve::get_rna_path() const -{ - return curve_key.get_path(); -} - -const int BCAnimationCurve::sample_count() const -{ - if (fcurve == NULL) { - return 0; - } - return fcurve->totvert; -} - -const int BCAnimationCurve::closest_index_above(const float sample_frame, const int start_at) const -{ - if (fcurve == NULL) { - return -1; - } - - const int cframe = fcurve->bezt[start_at].vec[1][0]; // inacurate! - - if (fabs(cframe - sample_frame) < 0.00001) { - return start_at; - } - return (fcurve->totvert > start_at + 1) ? start_at + 1 : start_at; -} - -const int BCAnimationCurve::closest_index_below(const float sample_frame) const -{ - if (fcurve == NULL) { - return -1; - } - - float lower_frame = sample_frame; - float upper_frame = sample_frame; - int lower_index = 0; - int upper_index = 0; - - for (int fcu_index = 0; fcu_index < fcurve->totvert; fcu_index++) { - upper_index = fcu_index; - - const int cframe = fcurve->bezt[fcu_index].vec[1][0]; // inacurate! - if (cframe <= sample_frame) { - lower_frame = cframe; - lower_index = fcu_index; - } - if (cframe >= sample_frame) { - upper_frame = cframe; - break; - } - } - - if (lower_index == upper_index) { - return lower_index; - } - - const float fraction = float(sample_frame - lower_frame) / (upper_frame - lower_frame); - return (fraction < 0.5) ? lower_index : upper_index; -} - -const int BCAnimationCurve::get_interpolation_type(float sample_frame) const -{ - const int index = closest_index_below(sample_frame); - if (index < 0) { - return BEZT_IPO_BEZ; - } - return fcurve->bezt[index].ipo; -} - -const FCurve *BCAnimationCurve::get_fcurve() const -{ - return fcurve; -} - -FCurve *BCAnimationCurve::get_edit_fcurve() -{ - if (!curve_is_local_copy) { - const int index = curve_key.get_array_index(); - const std::string &path = curve_key.get_path(); - fcurve = create_fcurve(index, path.c_str()); - - /* Caution here: - * Replacing the pointer here is OK only because the original value - * of FCurve was a const pointer into Blender territory. We do not - * touch that! We use the local copy to prepare data for export. */ - - curve_is_local_copy = true; - } - return fcurve; -} - -void BCAnimationCurve::clean_handles() -{ - if (fcurve == NULL) { - fcurve = get_edit_fcurve(); - } - - /* Keep old bezt data for copy)*/ - BezTriple *old_bezts = fcurve->bezt; - int totvert = fcurve->totvert; - fcurve->bezt = NULL; - fcurve->totvert = 0; - - for (int i = 0; i < totvert; i++) { - BezTriple *bezt = &old_bezts[i]; - float x = bezt->vec[1][0]; - float y = bezt->vec[1][1]; - insert_vert_fcurve(fcurve, x, y, (eBezTriple_KeyframeType)BEZKEYTYPE(bezt), INSERTKEY_NOFLAGS); - BezTriple *lastb = fcurve->bezt + (fcurve->totvert - 1); - lastb->f1 = lastb->f2 = lastb->f3 = 0; - } - - /* now free the memory used by the old BezTriples */ - if (old_bezts) { - MEM_freeN(old_bezts); - } -} - -const bool BCAnimationCurve::is_transform_curve() const -{ - std::string channel_type = this->get_channel_type(); - return (is_rotation_curve() || channel_type == "scale" || channel_type == "location"); -} - -const bool BCAnimationCurve::is_rotation_curve() const -{ - std::string channel_type = this->get_channel_type(); - return (channel_type == "rotation" || channel_type == "rotation_euler" || - channel_type == "rotation_quaternion"); -} - -const float BCAnimationCurve::get_value(const float frame) -{ - if (fcurve) { - return evaluate_fcurve(fcurve, frame); - } - return 0; // TODO: handle case where neither sample nor fcu exist -} - -void BCAnimationCurve::update_range(float val) -{ - if (val < min) { - min = val; - } - if (val > max) { - max = val; - } -} - -void BCAnimationCurve::init_range(float val) -{ - min = max = val; -} - -void BCAnimationCurve::adjust_range(const int frame_index) -{ - if (fcurve && fcurve->totvert > 1) { - const float eval = evaluate_fcurve(fcurve, frame_index); - - int first_frame = fcurve->bezt[0].vec[1][0]; - if (first_frame == frame_index) { - init_range(eval); - } - else { - update_range(eval); - } - } -} - -void BCAnimationCurve::add_value(const float val, const int frame_index) -{ - FCurve *fcu = get_edit_fcurve(); - fcu->auto_smoothing = U.auto_smoothing_new; - insert_vert_fcurve(fcu, frame_index, val, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NOFLAGS); - - if (fcu->totvert == 1) { - init_range(val); - } - else { - update_range(val); - } -} - -bool BCAnimationCurve::add_value_from_matrix(const BCSample &sample, const int frame_index) -{ - int array_index = curve_key.get_array_index(); - - /* transformation curves are fed directly from the transformation matrix - * to resolve parent inverse matrix issues with object hierarchies. - * Maybe this can be unified with the - */ - const std::string channel_target = get_channel_target(); - float val = 0; - /* Pick the value from the sample according to the definition of the FCurve */ - bool good = sample.get_value(channel_target, array_index, &val); - if (good) { - add_value(val, frame_index); - } - return good; -} - -bool BCAnimationCurve::add_value_from_rna(const int frame_index) -{ - PointerRNA ptr; - PropertyRNA *prop; - float value = 0.0f; - int array_index = curve_key.get_array_index(); - const std::string full_path = curve_key.get_full_path(); - - /* get property to read from, and get value as appropriate */ - bool path_resolved = RNA_path_resolve_full( - &id_ptr, full_path.c_str(), &ptr, &prop, &array_index); - if (!path_resolved && array_index == 0) { - const std::string rna_path = curve_key.get_path(); - path_resolved = RNA_path_resolve_full(&id_ptr, rna_path.c_str(), &ptr, &prop, &array_index); - } - - if (path_resolved) { - bool is_array = RNA_property_array_check(prop); - if (is_array) { - /* array */ - if ((array_index >= 0) && (array_index < RNA_property_array_length(&ptr, prop))) { - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get_index(&ptr, prop, array_index); - break; - case PROP_INT: - value = (float)RNA_property_int_get_index(&ptr, prop, array_index); - break; - case PROP_FLOAT: - value = RNA_property_float_get_index(&ptr, prop, array_index); - break; - default: - break; - } - } - else { - fprintf(stderr, - "Out of Bounds while reading data for Curve %s\n", - curve_key.get_full_path().c_str()); - return false; - } - } - else { - /* not an array */ - switch (RNA_property_type(prop)) { - case PROP_BOOLEAN: - value = (float)RNA_property_boolean_get(&ptr, prop); - break; - case PROP_INT: - value = (float)RNA_property_int_get(&ptr, prop); - break; - case PROP_FLOAT: - value = RNA_property_float_get(&ptr, prop); - break; - case PROP_ENUM: - value = (float)RNA_property_enum_get(&ptr, prop); - break; - default: - fprintf(stderr, - "property type %d not supported for Curve %s\n", - RNA_property_type(prop), - curve_key.get_full_path().c_str()); - return false; - break; - } - } - } - else { - /* path couldn't be resolved */ - fprintf(stderr, "Path not recognized for Curve %s\n", curve_key.get_full_path().c_str()); - return false; - } - - add_value(value, frame_index); - return true; -} - -void BCAnimationCurve::get_value_map(BCValueMap &value_map) -{ - value_map.clear(); - if (fcurve == NULL) { - return; - } - - for (int i = 0; i < fcurve->totvert; i++) { - const float frame = fcurve->bezt[i].vec[1][0]; - const float val = fcurve->bezt[i].vec[1][1]; - value_map[frame] = val; - } -} - -void BCAnimationCurve::get_frames(BCFrames &frames) const -{ - frames.clear(); - if (fcurve) { - for (int i = 0; i < fcurve->totvert; i++) { - const float val = fcurve->bezt[i].vec[1][0]; - frames.push_back(val); - } - } -} - -void BCAnimationCurve::get_values(BCValues &values) const -{ - values.clear(); - if (fcurve) { - for (int i = 0; i < fcurve->totvert; i++) { - const float val = fcurve->bezt[i].vec[1][1]; - values.push_back(val); - } - } -} - -bool BCAnimationCurve::is_animated() -{ - static float MIN_DISTANCE = 0.00001; - return fabs(max - min) > MIN_DISTANCE; -} - -bool BCAnimationCurve::is_keyframe(int frame) -{ - if (this->fcurve == NULL) { - return false; - } - - for (int i = 0; i < fcurve->totvert; i++) { - const int cframe = nearbyint(fcurve->bezt[i].vec[1][0]); - if (cframe == frame) { - return true; - } - if (cframe > frame) { - break; - } - } - return false; -} - -/* Needed for adding a BCAnimationCurve into a BCAnimationCurveSet */ -inline bool operator<(const BCAnimationCurve &lhs, const BCAnimationCurve &rhs) -{ - std::string lhtgt = lhs.get_channel_target(); - std::string rhtgt = rhs.get_channel_target(); - if (lhtgt == rhtgt) { - const int lha = lhs.get_channel_index(); - const int rha = rhs.get_channel_index(); - return lha < rha; - } - else { - return lhtgt < rhtgt; - } -} - -BCCurveKey::BCCurveKey() -{ - this->key_type = BC_ANIMATION_TYPE_OBJECT; - this->rna_path = ""; - this->curve_array_index = 0; - this->curve_subindex = -1; -} - -BCCurveKey::BCCurveKey(const BC_animation_type type, - const std::string path, - const int array_index, - const int subindex) -{ - this->key_type = type; - this->rna_path = path; - this->curve_array_index = array_index; - this->curve_subindex = subindex; -} - -void BCCurveKey::operator=(const BCCurveKey &other) -{ - this->key_type = other.key_type; - this->rna_path = other.rna_path; - this->curve_array_index = other.curve_array_index; - this->curve_subindex = other.curve_subindex; -} - -const std::string BCCurveKey::get_full_path() const -{ - return this->rna_path + '[' + std::to_string(this->curve_array_index) + ']'; -} - -const std::string BCCurveKey::get_path() const -{ - return this->rna_path; -} - -const int BCCurveKey::get_array_index() const -{ - return this->curve_array_index; -} - -const int BCCurveKey::get_subindex() const -{ - return this->curve_subindex; -} - -void BCCurveKey::set_object_type(BC_animation_type object_type) -{ - this->key_type = object_type; -} - -const BC_animation_type BCCurveKey::get_animation_type() const -{ - return this->key_type; -} - -const bool BCCurveKey::operator<(const BCCurveKey &other) const -{ - /* needed for using this class as key in maps and sets */ - if (this->key_type != other.key_type) { - return this->key_type < other.key_type; - } - - if (this->curve_subindex != other.curve_subindex) { - return this->curve_subindex < other.curve_subindex; - } - - if (this->rna_path != other.rna_path) { - return this->rna_path < other.rna_path; - } - - return this->curve_array_index < other.curve_array_index; -} - -BCBezTriple::BCBezTriple(BezTriple &bezt) : bezt(bezt) -{ -} - -const float BCBezTriple::get_frame() const -{ - return bezt.vec[1][0]; -} - -const float BCBezTriple::get_time(Scene *scene) const -{ - return FRA2TIME(bezt.vec[1][0]); -} - -const float BCBezTriple::get_value() const -{ - return bezt.vec[1][1]; -} - -const float BCBezTriple::get_angle() const -{ - return RAD2DEGF(get_value()); -} - -void BCBezTriple::get_in_tangent(Scene *scene, float point[2], bool as_angle) const -{ - get_tangent(scene, point, as_angle, 0); -} - -void BCBezTriple::get_out_tangent(Scene *scene, float point[2], bool as_angle) const -{ - get_tangent(scene, point, as_angle, 2); -} - -void BCBezTriple::get_tangent(Scene *scene, float point[2], bool as_angle, int index) const -{ - point[0] = FRA2TIME(bezt.vec[index][0]); - if (bezt.ipo != BEZT_IPO_BEZ) { - /* We're in a mixed interpolation scenario, set zero as it's irrelevant but value might contain - * unused data */ - point[0] = 0; - point[1] = 0; - } - else if (as_angle) { - point[1] = RAD2DEGF(bezt.vec[index][1]); - } - else { - point[1] = bezt.vec[index][1]; - } -} diff --git a/source/blender/collada/BCAnimationCurve.h b/source/blender/collada/BCAnimationCurve.h deleted file mode 100644 index 7b523ac53ca..00000000000 --- a/source/blender/collada/BCAnimationCurve.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#ifndef __BCANIMATIONCURVE_H__ -#define __BCANIMATIONCURVE_H__ - -#include "collada_utils.h" -#include "BCSampleData.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "BKE_fcurve.h" -#include "BKE_armature.h" -#include "BKE_material.h" -#include "ED_anim_api.h" -#include "ED_keyframing.h" -#include "ED_keyframes_edit.h" -} - -typedef float(TangentPoint)[2]; - -typedef std::set BCFrameSet; -typedef std::vector BCFrames; -typedef std::vector BCValues; -typedef std::vector BCTimes; -typedef std::map BCValueMap; - -typedef enum BC_animation_type { - BC_ANIMATION_TYPE_OBJECT, - BC_ANIMATION_TYPE_BONE, - BC_ANIMATION_TYPE_CAMERA, - BC_ANIMATION_TYPE_MATERIAL, - BC_ANIMATION_TYPE_LIGHT, -} BC_animation_type; - -class BCCurveKey { - private: - BC_animation_type key_type; - std::string rna_path; - int curve_array_index; - int curve_subindex; /* only needed for materials */ - - public: - BCCurveKey(); - BCCurveKey(const BC_animation_type type, - const std::string path, - const int array_index, - const int subindex = -1); - void operator=(const BCCurveKey &other); - const std::string get_full_path() const; - const std::string get_path() const; - const int get_array_index() const; - const int get_subindex() const; - void set_object_type(BC_animation_type object_type); - const BC_animation_type get_animation_type() const; - const bool operator<(const BCCurveKey &other) const; -}; - -class BCBezTriple { - public: - BezTriple &bezt; - - BCBezTriple(BezTriple &bezt); - const float get_frame() const; - const float get_time(Scene *scene) const; - const float get_value() const; - const float get_angle() const; - void get_in_tangent(Scene *scene, float point[2], bool as_angle) const; - void get_out_tangent(Scene *scene, float point[2], bool as_angle) const; - void get_tangent(Scene *scene, float point[2], bool as_angle, int index) const; -}; - -class BCAnimationCurve { - private: - BCCurveKey curve_key; - float min = 0; - float max = 0; - - bool curve_is_local_copy = false; - FCurve *fcurve; - PointerRNA id_ptr; - void init_pointer_rna(Object *ob); - void delete_fcurve(FCurve *fcu); - FCurve *create_fcurve(int array_index, const char *rna_path); - void create_bezt(float frame, float output); - void update_range(float val); - void init_range(float val); - - public: - BCAnimationCurve(); - BCAnimationCurve(const BCAnimationCurve &other); - BCAnimationCurve(const BCCurveKey &key, Object *ob); - BCAnimationCurve(BCCurveKey key, Object *ob, FCurve *fcu); - ~BCAnimationCurve(); - - const bool is_of_animation_type(BC_animation_type type) const; - const int get_interpolation_type(float sample_frame) const; - bool is_animated(); - const bool is_transform_curve() const; - const bool is_rotation_curve() const; - bool is_keyframe(int frame); - void adjust_range(int frame); - - const std::string get_animation_name(Object *ob) const; /* xxx: this is collada specific */ - const std::string get_channel_target() const; - const std::string get_channel_type() const; - const std::string get_channel_posebone() const; // returns "" if channel is not a bone channel - - const int get_channel_index() const; - const int get_subindex() const; - const std::string get_rna_path() const; - const FCurve *get_fcurve() const; - const int sample_count() const; - - const float get_value(const float frame); - void get_values(BCValues &values) const; - void get_value_map(BCValueMap &value_map); - - void get_frames(BCFrames &frames) const; - - /* Curve edit functions create a copy of the underlaying FCurve */ - FCurve *get_edit_fcurve(); - bool add_value_from_rna(const int frame); - bool add_value_from_matrix(const BCSample &sample, const int frame); - void add_value(const float val, const int frame); - void clean_handles(); - - /* experimental stuff */ - const int closest_index_above(const float sample_frame, const int start_at) const; - const int closest_index_below(const float sample_frame) const; -}; - -typedef std::map BCAnimationCurveMap; - -#endif /* __BCANIMATIONCURVE_H__ */ diff --git a/source/blender/collada/BCAnimationSampler.cpp b/source/blender/collada/BCAnimationSampler.cpp deleted file mode 100644 index e6996e95a5b..00000000000 --- a/source/blender/collada/BCAnimationSampler.cpp +++ /dev/null @@ -1,662 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#include -#include -#include // std::find - -#include "ExportSettings.h" -#include "BCAnimationCurve.h" -#include "BCAnimationSampler.h" -#include "collada_utils.h" -#include "BCMath.h" - -extern "C" { -#include "BKE_action.h" -#include "BKE_constraint.h" -#include "BKE_key.h" -#include "BKE_main.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BLI_listbase.h" -#include "DNA_anim_types.h" -#include "DNA_scene_types.h" -#include "DNA_key_types.h" -#include "DNA_constraint_types.h" -#include "ED_object.h" -} - -static std::string EMPTY_STRING; -static BCAnimationCurveMap BCEmptyAnimationCurves; - -BCAnimationSampler::BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &object_set) - : export_settings(export_settings) -{ - BCObjectSet::iterator it; - for (it = object_set.begin(); it != object_set.end(); ++it) { - Object *ob = *it; - add_object(ob); - } -} - -BCAnimationSampler::~BCAnimationSampler() -{ - BCAnimationObjectMap::iterator it; - for (it = objects.begin(); it != objects.end(); ++it) { - BCAnimation *animation = it->second; - delete animation; - } -} - -void BCAnimationSampler::add_object(Object *ob) -{ - BlenderContext blender_context = export_settings.get_blender_context(); - BCAnimation *animation = new BCAnimation(blender_context.get_context(), ob); - objects[ob] = animation; - - initialize_keyframes(animation->frame_set, ob); - initialize_curves(animation->curve_map, ob); -} - -BCAnimationCurveMap *BCAnimationSampler::get_curves(Object *ob) -{ - BCAnimation &animation = *objects[ob]; - if (animation.curve_map.size() == 0) { - initialize_curves(animation.curve_map, ob); - } - return &animation.curve_map; -} - -static void get_sample_frames(BCFrameSet &sample_frames, - int sampling_rate, - bool keyframe_at_end, - Scene *scene) -{ - sample_frames.clear(); - - if (sampling_rate < 1) { - return; // no sample frames in this case - } - - float sfra = scene->r.sfra; - float efra = scene->r.efra; - - int frame_index; - for (frame_index = nearbyint(sfra); frame_index < efra; frame_index += sampling_rate) { - sample_frames.insert(frame_index); - } - - if (frame_index >= efra && keyframe_at_end) { - sample_frames.insert(efra); - } -} - -static bool is_object_keyframe(Object *ob, int frame_index) -{ - return false; -} - -static void add_keyframes_from(bAction *action, BCFrameSet &frameset) -{ - if (action) { - FCurve *fcu = NULL; - for (fcu = (FCurve *)action->curves.first; fcu; fcu = fcu->next) { - BezTriple *bezt = fcu->bezt; - for (int i = 0; i < fcu->totvert; bezt++, i++) { - int frame_index = nearbyint(bezt->vec[1][0]); - frameset.insert(frame_index); - } - } - } -} - -void BCAnimationSampler::check_property_is_animated( - BCAnimation &animation, float *ref, float *val, std::string data_path, int length) -{ - for (int array_index = 0; array_index < length; array_index++) { - if (!bc_in_range(ref[length], val[length], 0.00001)) { - BCCurveKey key(BC_ANIMATION_TYPE_OBJECT, data_path, array_index); - BCAnimationCurveMap::iterator it = animation.curve_map.find(key); - if (it == animation.curve_map.end()) { - animation.curve_map[key] = new BCAnimationCurve(key, animation.get_reference()); - } - } - } -} - -void BCAnimationSampler::update_animation_curves(BCAnimation &animation, - BCSample &sample, - Object *ob, - int frame) -{ - BCAnimationCurveMap::iterator it; - for (it = animation.curve_map.begin(); it != animation.curve_map.end(); ++it) { - BCAnimationCurve *curve = it->second; - if (curve->is_transform_curve()) { - curve->add_value_from_matrix(sample, frame); - } - else { - curve->add_value_from_rna(frame); - } - } -} - -BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim) -{ - BCSample &ob_sample = sample_data.add(ob, frame_index); - // if (export_settings.get_apply_global_orientation()) { - // const BCMatrix &global_transform = export_settings.get_global_transform(); - // ob_sample.get_matrix(global_transform); - //} - - if (ob->type == OB_ARMATURE) { - bPoseChannel *pchan; - for (pchan = (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan = pchan->next) { - Bone *bone = pchan->bone; - Matrix bmat; - if (bc_bone_matrix_local_get(ob, bone, bmat, for_opensim)) { - - ob_sample.add_bone_matrix(bone, bmat); - } - } - } - return ob_sample; -} - -void BCAnimationSampler::sample_scene(BCExportSettings &export_settings, bool keyframe_at_end) -{ - BlenderContext blender_context = export_settings.get_blender_context(); - int sampling_rate = export_settings.get_sampling_rate(); - bool for_opensim = export_settings.get_open_sim(); - bool keep_keyframes = export_settings.get_keep_keyframes(); - BC_export_animation_type export_animation_type = export_settings.get_export_animation_type(); - - Scene *scene = blender_context.get_scene(); - BCFrameSet scene_sample_frames; - get_sample_frames(scene_sample_frames, sampling_rate, keyframe_at_end, scene); - BCFrameSet::iterator it; - - int startframe = scene->r.sfra; - int endframe = scene->r.efra; - - for (int frame_index = startframe; frame_index <= endframe; frame_index++) { - /* Loop over all frames and decide for each frame if sampling is necessary */ - bool is_scene_sample_frame = false; - bool needs_update = true; - if (scene_sample_frames.find(frame_index) != scene_sample_frames.end()) { - bc_update_scene(blender_context, frame_index); - needs_update = false; - is_scene_sample_frame = true; - } - - bool needs_sampling = is_scene_sample_frame || keep_keyframes || - export_animation_type == BC_ANIMATION_EXPORT_KEYS; - if (!needs_sampling) { - continue; - } - - BCAnimationObjectMap::iterator obit; - for (obit = objects.begin(); obit != objects.end(); ++obit) { - Object *ob = obit->first; - BCAnimation *animation = obit->second; - BCFrameSet &object_keyframes = animation->frame_set; - if (is_scene_sample_frame || object_keyframes.find(frame_index) != object_keyframes.end()) { - - if (needs_update) { - bc_update_scene(blender_context, frame_index); - needs_update = false; - } - - BCSample &sample = sample_object(ob, frame_index, for_opensim); - update_animation_curves(*animation, sample, ob, frame_index); - } - } - } -} - -bool BCAnimationSampler::is_animated_by_constraint(Object *ob, - ListBase *conlist, - std::set &animated_objects) -{ - bConstraint *con; - for (con = (bConstraint *)conlist->first; con; con = con->next) { - ListBase targets = {NULL, NULL}; - - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - - if (!bc_validateConstraints(con)) { - continue; - } - - if (cti && cti->get_constraint_targets) { - bConstraintTarget *ct; - Object *obtar; - cti->get_constraint_targets(con, &targets); - for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) { - obtar = ct->tar; - if (obtar) { - if (animated_objects.find(obtar) != animated_objects.end()) { - return true; - } - } - } - } - } - return false; -} - -void BCAnimationSampler::find_depending_animated(std::set &animated_objects, - std::set &candidates) -{ - bool found_more; - do { - found_more = false; - std::set::iterator it; - for (it = candidates.begin(); it != candidates.end(); ++it) { - Object *cob = *it; - ListBase *conlist = get_active_constraints(cob); - if (is_animated_by_constraint(cob, conlist, animated_objects)) { - animated_objects.insert(cob); - candidates.erase(cob); - found_more = true; - break; - } - } - } while (found_more && candidates.size() > 0); -} - -void BCAnimationSampler::get_animated_from_export_set(std::set &animated_objects, - LinkNode &export_set) -{ - /* Check if this object is animated. That is: Check if it has its own action, or: - * - * - Check if it has constraints to other objects. - * - at least one of the other objects is animated as well. - */ - - animated_objects.clear(); - std::set static_objects; - std::set candidates; - - LinkNode *node; - for (node = &export_set; node; node = node->next) { - Object *cob = (Object *)node->link; - if (bc_has_animations(cob)) { - animated_objects.insert(cob); - } - else { - ListBase conlist = cob->constraints; - if (conlist.first) { - candidates.insert(cob); - } - } - } - find_depending_animated(animated_objects, candidates); -} - -void BCAnimationSampler::get_object_frames(BCFrames &frames, Object *ob) -{ - sample_data.get_frames(ob, frames); -} - -void BCAnimationSampler::get_bone_frames(BCFrames &frames, Object *ob, Bone *bone) -{ - sample_data.get_frames(ob, bone, frames); -} - -bool BCAnimationSampler::get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone) -{ - sample_data.get_matrices(ob, bone, samples); - return bc_is_animated(samples); -} - -bool BCAnimationSampler::get_object_samples(BCMatrixSampleMap &samples, Object *ob) -{ - sample_data.get_matrices(ob, samples); - return bc_is_animated(samples); -} - -#if 0 -/** - * Add sampled values to #FCurve - * If no #FCurve exists, create a temporary #FCurve; - * \note The temporary #FCurve will later be removed when the - * #BCAnimationSampler is removed (by its destructor). - * - * \param curve: The curve to which the data is added. - * \param matrices: The set of matrix values from where the data is taken. - * \param animation_type: - * - #BC_ANIMATION_EXPORT_SAMPLES: Use all matrix data. - * - #BC_ANIMATION_EXPORT_KEYS: Only take data from matrices for keyframes. - */ -void BCAnimationSampler::add_value_set(BCAnimationCurve &curve, - BCFrameSampleMap &samples, - BC_export_animation_type animation_type) -{ - int array_index = curve.get_array_index(); - const BC_animation_transform_type tm_type = curve.get_transform_type(); - - BCFrameSampleMap::iterator it; - for (it = samples.begin(); it != samples.end(); ++it) { - const int frame_index = nearbyint(it->first); - if (animation_type == BC_ANIMATION_EXPORT_SAMPLES || curve.is_keyframe(frame_index)) { - - const BCSample *sample = it->second; - float val = 0; - - int subindex = curve.get_subindex(); - bool good; - if (subindex == -1) { - good = sample->get_value(tm_type, array_index, &val); - } - else { - good = sample->get_value(tm_type, array_index, &val, subindex); - } - - if (good) { - curve.add_value(val, frame_index); - } - } - } - curve.remove_unused_keyframes(); - curve.calchandles(); -} -#endif - -void BCAnimationSampler::generate_transform(Object *ob, - const BCCurveKey &key, - BCAnimationCurveMap &curves) -{ - BCAnimationCurveMap::const_iterator it = curves.find(key); - if (it == curves.end()) { - curves[key] = new BCAnimationCurve(key, ob); - } -} - -void BCAnimationSampler::generate_transforms(Object *ob, - const std::string prep, - const BC_animation_type type, - BCAnimationCurveMap &curves) -{ - generate_transform(ob, BCCurveKey(type, prep + "location", 0), curves); - generate_transform(ob, BCCurveKey(type, prep + "location", 1), curves); - generate_transform(ob, BCCurveKey(type, prep + "location", 2), curves); - generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 0), curves); - generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 1), curves); - generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 2), curves); - generate_transform(ob, BCCurveKey(type, prep + "scale", 0), curves); - generate_transform(ob, BCCurveKey(type, prep + "scale", 1), curves); - generate_transform(ob, BCCurveKey(type, prep + "scale", 2), curves); -} - -void BCAnimationSampler::generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves) -{ - std::string prep = "pose.bones[\"" + std::string(bone->name) + "\"]."; - generate_transforms(ob, prep, BC_ANIMATION_TYPE_BONE, curves); - - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - generate_transforms(ob, child, curves); - } -} - -/** - * Collect all keyframes from all animation curves related to the object. - * The bc_get... functions check for NULL and correct object type. - * The #add_keyframes_from() function checks for NULL. - */ -void BCAnimationSampler::initialize_keyframes(BCFrameSet &frameset, Object *ob) -{ - frameset.clear(); - add_keyframes_from(bc_getSceneObjectAction(ob), frameset); - add_keyframes_from(bc_getSceneCameraAction(ob), frameset); - add_keyframes_from(bc_getSceneLightAction(ob), frameset); - - for (int a = 0; a < ob->totcol; a++) { - Material *ma = BKE_object_material_get(ob, a + 1); - add_keyframes_from(bc_getSceneMaterialAction(ma), frameset); - } -} - -void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object *ob) -{ - BC_animation_type object_type = BC_ANIMATION_TYPE_OBJECT; - - bAction *action = bc_getSceneObjectAction(ob); - if (action) { - FCurve *fcu = (FCurve *)action->curves.first; - - for (; fcu; fcu = fcu->next) { - object_type = BC_ANIMATION_TYPE_OBJECT; - if (ob->type == OB_ARMATURE) { - char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); - if (boneName) { - object_type = BC_ANIMATION_TYPE_BONE; - } - } - - /* Adding action curves on object */ - BCCurveKey key(object_type, fcu->rna_path, fcu->array_index); - curves[key] = new BCAnimationCurve(key, ob, fcu); - } - } - - /* Add missing curves */ - object_type = BC_ANIMATION_TYPE_OBJECT; - generate_transforms(ob, EMPTY_STRING, object_type, curves); - if (ob->type == OB_ARMATURE) { - bArmature *arm = (bArmature *)ob->data; - for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next) { - generate_transforms(ob, root_bone, curves); - } - } - - /* Add curves on Object->data actions */ - action = NULL; - if (ob->type == OB_CAMERA) { - action = bc_getSceneCameraAction(ob); - object_type = BC_ANIMATION_TYPE_CAMERA; - } - else if (ob->type == OB_LAMP) { - action = bc_getSceneLightAction(ob); - object_type = BC_ANIMATION_TYPE_LIGHT; - } - - if (action) { - /* Add light action or Camera action */ - FCurve *fcu = (FCurve *)action->curves.first; - for (; fcu; fcu = fcu->next) { - BCCurveKey key(object_type, fcu->rna_path, fcu->array_index); - curves[key] = new BCAnimationCurve(key, ob, fcu); - } - } - - /* Add curves on Object->material actions*/ - object_type = BC_ANIMATION_TYPE_MATERIAL; - for (int a = 0; a < ob->totcol; a++) { - /* Export Material parameter animations. */ - Material *ma = BKE_object_material_get(ob, a + 1); - if (ma) { - action = bc_getSceneMaterialAction(ma); - if (action) { - /* isMatAnim = true; */ - FCurve *fcu = (FCurve *)action->curves.first; - for (; fcu; fcu = fcu->next) { - BCCurveKey key(object_type, fcu->rna_path, fcu->array_index, a); - curves[key] = new BCAnimationCurve(key, ob, fcu); - } - } - } - } -} - -/* ==================================================================== */ - -BCSample &BCSampleFrame::add(Object *ob) -{ - BCSample *sample = new BCSample(ob); - sampleMap[ob] = sample; - return *sample; -} - -/* Get the matrix for the given key, returns Unity when the key does not exist */ -const BCSample *BCSampleFrame::get_sample(Object *ob) const -{ - BCSampleMap::const_iterator it = sampleMap.find(ob); - if (it == sampleMap.end()) { - return NULL; - } - return it->second; -} - -const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob) const -{ - BCSampleMap::const_iterator it = sampleMap.find(ob); - if (it == sampleMap.end()) { - return NULL; - } - BCSample *sample = it->second; - return &sample->get_matrix(); -} - -/* Get the matrix for the given Bone, returns Unity when the Objewct is not sampled */ -const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob, Bone *bone) const -{ - BCSampleMap::const_iterator it = sampleMap.find(ob); - if (it == sampleMap.end()) { - return NULL; - } - - BCSample *sample = it->second; - const BCMatrix *bc_bone = sample->get_matrix(bone); - return bc_bone; -} - -/* Check if the key is in this BCSampleFrame */ -const bool BCSampleFrame::has_sample_for(Object *ob) const -{ - return sampleMap.find(ob) != sampleMap.end(); -} - -/* Check if the Bone is in this BCSampleFrame */ -const bool BCSampleFrame::has_sample_for(Object *ob, Bone *bone) const -{ - const BCMatrix *bc_bone = get_sample_matrix(ob, bone); - return (bc_bone); -} - -/* ==================================================================== */ - -BCSample &BCSampleFrameContainer::add(Object *ob, int frame_index) -{ - BCSampleFrame &frame = sample_frames[frame_index]; - return frame.add(ob); -} - -/* ====================================================== */ -/* Below are the getters which we need to export the data */ -/* ====================================================== */ - -/* Return either the BCSampleFrame or NULL if frame does not exist*/ -BCSampleFrame *BCSampleFrameContainer::get_frame(int frame_index) -{ - BCSampleFrameMap::iterator it = sample_frames.find(frame_index); - BCSampleFrame *frame = (it == sample_frames.end()) ? NULL : &it->second; - return frame; -} - -/* Return a list of all frames that need to be sampled */ -const int BCSampleFrameContainer::get_frames(std::vector &frames) const -{ - frames.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - frames.push_back(it->first); - } - return frames.size(); -} - -const int BCSampleFrameContainer::get_frames(Object *ob, BCFrames &frames) const -{ - frames.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - const BCSampleFrame &frame = it->second; - if (frame.has_sample_for(ob)) { - frames.push_back(it->first); - } - } - return frames.size(); -} - -const int BCSampleFrameContainer::get_frames(Object *ob, Bone *bone, BCFrames &frames) const -{ - frames.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - const BCSampleFrame &frame = it->second; - if (frame.has_sample_for(ob, bone)) { - frames.push_back(it->first); - } - } - return frames.size(); -} - -const int BCSampleFrameContainer::get_samples(Object *ob, BCFrameSampleMap &samples) const -{ - samples.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - const BCSampleFrame &frame = it->second; - const BCSample *sample = frame.get_sample(ob); - if (sample) { - samples[it->first] = sample; - } - } - return samples.size(); -} - -const int BCSampleFrameContainer::get_matrices(Object *ob, BCMatrixSampleMap &samples) const -{ - samples.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - const BCSampleFrame &frame = it->second; - const BCMatrix *matrix = frame.get_sample_matrix(ob); - if (matrix) { - samples[it->first] = matrix; - } - } - return samples.size(); -} - -const int BCSampleFrameContainer::get_matrices(Object *ob, - Bone *bone, - BCMatrixSampleMap &samples) const -{ - samples.clear(); // safety; - BCSampleFrameMap::const_iterator it; - for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { - const BCSampleFrame &frame = it->second; - const BCMatrix *sample = frame.get_sample_matrix(ob, bone); - if (sample) { - samples[it->first] = sample; - } - } - return samples.size(); -} diff --git a/source/blender/collada/BCAnimationSampler.h b/source/blender/collada/BCAnimationSampler.h deleted file mode 100644 index 96138d0cbca..00000000000 --- a/source/blender/collada/BCAnimationSampler.h +++ /dev/null @@ -1,194 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __BCANIMATIONSAMPLER_H__ -#define __BCANIMATIONSAMPLER_H__ - -#include "BCAnimationCurve.h" -#include "BCSampleData.h" -#include "collada_utils.h" - -extern "C" { -#include "BKE_action.h" -#include "BKE_lib_id.h" -#include "BLI_math_rotation.h" -#include "DNA_action_types.h" -} - -/* Collection of animation curves */ -class BCAnimation { - private: - Object *reference = NULL; - bContext *mContext; - - public: - BCFrameSet frame_set; - BCAnimationCurveMap curve_map; - - BCAnimation(bContext *C, Object *ob) : mContext(C) - { - Main *bmain = CTX_data_main(mContext); - reference = BKE_object_copy(bmain, ob); - } - - ~BCAnimation() - { - BCAnimationCurveMap::iterator it; - for (it = curve_map.begin(); it != curve_map.end(); ++it) { - delete it->second; - } - - if (reference && reference->id.us == 0) { - Main *bmain = CTX_data_main(mContext); - BKE_id_delete(bmain, &reference->id); - } - curve_map.clear(); - } - - Object *get_reference() - { - return reference; - } -}; - -typedef std::map BCAnimationObjectMap; - -class BCSampleFrame { - - /* Each frame on the timeline that needs to be sampled will have - * one BCSampleFrame where we collect sample information about all objects - * that need to be sampled for that frame. */ - - private: - BCSampleMap sampleMap; - - public: - ~BCSampleFrame() - { - BCSampleMap::iterator it; - for (it = sampleMap.begin(); it != sampleMap.end(); ++it) { - BCSample *sample = it->second; - delete sample; - } - sampleMap.clear(); - } - - BCSample &add(Object *ob); - - /* Following methods return NULL if object is not in the sampleMap*/ - const BCSample *get_sample(Object *ob) const; - const BCMatrix *get_sample_matrix(Object *ob) const; - const BCMatrix *get_sample_matrix(Object *ob, Bone *bone) const; - - const bool has_sample_for(Object *ob) const; - const bool has_sample_for(Object *ob, Bone *bone) const; -}; - -typedef std::map BCSampleFrameMap; - -class BCSampleFrameContainer { - - /* - * The BCSampleFrameContainer stores a map of BCSampleFrame objects - * with the timeline frame as key. - * - * Some details on the purpose: - * An Animation is made of multiple FCurves where each FCurve can - * have multiple keyframes. When we want to export the animation we - * also can decide whether we want to export the keyframes or a set - * of sample frames at equidistant locations (sample period). - * In any case we must resample first need to resample it fully - * to resolve things like: - * - * - animations by constraints - * - animations by drivers - * - * For this purpose we need to step through the entire animation and - * then sample each frame that contains at least one keyFrame or - * sampleFrame. Then for each frame we have to store the transform - * information for all exported objects in a BCSampleframe - * - * The entire set of BCSampleframes is finally collected into - * a BCSampleframneContainer - */ - - private: - BCSampleFrameMap sample_frames; - - public: - ~BCSampleFrameContainer() - { - } - - BCSample &add(Object *ob, int frame_index); - BCSampleFrame *get_frame(int frame_index); // returns NULL if frame does not exist - - const int get_frames(std::vector &frames) const; - const int get_frames(Object *ob, BCFrames &frames) const; - const int get_frames(Object *ob, Bone *bone, BCFrames &frames) const; - - const int get_samples(Object *ob, BCFrameSampleMap &samples) const; - const int get_matrices(Object *ob, BCMatrixSampleMap &matrices) const; - const int get_matrices(Object *ob, Bone *bone, BCMatrixSampleMap &bones) const; -}; - -class BCAnimationSampler { - private: - BCExportSettings &export_settings; - BCSampleFrameContainer sample_data; - BCAnimationObjectMap objects; - - void generate_transform(Object *ob, const BCCurveKey &key, BCAnimationCurveMap &curves); - void generate_transforms(Object *ob, - const std::string prep, - const BC_animation_type type, - BCAnimationCurveMap &curves); - void generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves); - - void initialize_curves(BCAnimationCurveMap &curves, Object *ob); - void initialize_keyframes(BCFrameSet &frameset, Object *ob); - BCSample &sample_object(Object *ob, int frame_index, bool for_opensim); - void update_animation_curves(BCAnimation &animation, - BCSample &sample, - Object *ob, - int frame_index); - void check_property_is_animated( - BCAnimation &animation, float *ref, float *val, std::string data_path, int length); - - public: - BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &animated_subset); - ~BCAnimationSampler(); - - void add_object(Object *ob); - - void sample_scene(BCExportSettings &export_settings, bool keyframe_at_end); - - BCAnimationCurveMap *get_curves(Object *ob); - void get_object_frames(BCFrames &frames, Object *ob); - bool get_object_samples(BCMatrixSampleMap &samples, Object *ob); - void get_bone_frames(BCFrames &frames, Object *ob, Bone *bone); - bool get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone); - - static void get_animated_from_export_set(std::set &animated_objects, - LinkNode &export_set); - static void find_depending_animated(std::set &animated_objects, - std::set &candidates); - static bool is_animated_by_constraint(Object *ob, - ListBase *conlist, - std::set &animated_objects); -}; - -#endif /* __BCANIMATIONSAMPLER_H__ */ diff --git a/source/blender/collada/BCMath.cpp b/source/blender/collada/BCMath.cpp deleted file mode 100644 index ec9977c1469..00000000000 --- a/source/blender/collada/BCMath.cpp +++ /dev/null @@ -1,244 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#include "BCMath.h" -#include "BlenderContext.h" - -void BCQuat::rotate_to(Matrix &mat_to) -{ - Quat qd; - Matrix matd; - Matrix mati; - Matrix mat_from; - - quat_to_mat4(mat_from, q); - - /* Calculate the difference matrix matd between mat_from and mat_to */ - invert_m4_m4(mati, mat_from); - mul_m4_m4m4(matd, mati, mat_to); - - mat4_to_quat(qd, matd); - - mul_qt_qtqt(q, qd, q); /* rotate to the final rotation to mat_to */ -} - -BCMatrix::BCMatrix(const BCMatrix &mat) -{ - set_transform(mat.matrix); -} - -BCMatrix::BCMatrix(Matrix &mat) -{ - set_transform(mat); -} - -BCMatrix::BCMatrix(Object *ob) -{ - set_transform(ob); -} - -BCMatrix::BCMatrix() -{ - unit(); -} - -BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis global_up_axis) -{ - float mrot[3][3]; - float mat[4][4]; - mat3_from_axis_conversion( - BC_DEFAULT_FORWARD, BC_DEFAULT_UP, global_forward_axis, global_up_axis, mrot); - - transpose_m3(mrot); // TODO: Verify that mat3_from_axis_conversion() returns a transposed matrix - copy_m4_m3(mat, mrot); - set_transform(mat); -} - -void BCMatrix::add_transform(const Matrix &mat, bool inverse) -{ - add_transform(this->matrix, mat, this->matrix, inverse); -} - -void BCMatrix::add_transform(const BCMatrix &mat, bool inverse) -{ - add_transform(this->matrix, mat.matrix, this->matrix, inverse); -} - -void BCMatrix::apply_transform(const BCMatrix &mat, bool inverse) -{ - apply_transform(this->matrix, mat.matrix, this->matrix, inverse); -} - -void BCMatrix::add_transform(Matrix &to, const Matrix &transform, const Matrix &from, bool inverse) -{ - if (inverse) { - Matrix globinv; - invert_m4_m4(globinv, transform); - add_transform(to, globinv, from, /*inverse=*/false); - } - else { - mul_m4_m4m4(to, transform, from); - } -} - -void BCMatrix::apply_transform(Matrix &to, - const Matrix &transform, - const Matrix &from, - bool inverse) -{ - Matrix globinv; - invert_m4_m4(globinv, transform); - if (inverse) { - add_transform(to, globinv, from, /*inverse=*/false); - } - else { - mul_m4_m4m4(to, transform, from); - mul_m4_m4m4(to, to, globinv); - } -} - -void BCMatrix::add_inverted_transform(Matrix &to, const Matrix &transform, const Matrix &from) -{ - Matrix workmat; - invert_m4_m4(workmat, transform); - mul_m4_m4m4(to, workmat, from); -} - -void BCMatrix::set_transform(Object *ob) -{ - Matrix lmat; - - BKE_object_matrix_local_get(ob, lmat); - copy_m4_m4(matrix, lmat); - - mat4_decompose(this->loc, this->q, this->size, lmat); - quat_to_compatible_eul(this->rot, ob->rot, this->q); -} - -void BCMatrix::set_transform(Matrix &mat) -{ - copy_m4_m4(matrix, mat); - mat4_decompose(this->loc, this->q, this->size, mat); - quat_to_eul(this->rot, this->q); -} - -void BCMatrix::copy(Matrix &out, Matrix &in) -{ - /* destination comes first: */ - memcpy(out, in, sizeof(Matrix)); -} - -void BCMatrix::transpose(Matrix &mat) -{ - transpose_m4(mat); -} - -void BCMatrix::sanitize(Matrix &mat, int precision) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - double val = (double)mat[i][j]; - val = double_round(val, precision); - mat[i][j] = (float)val; - } - } -} - -void BCMatrix::sanitize(DMatrix &mat, int precision) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - mat[i][j] = double_round(mat[i][j], precision); - } - } -} - -void BCMatrix::unit() -{ - unit_m4(this->matrix); - mat4_decompose(this->loc, this->q, this->size, this->matrix); - quat_to_eul(this->rot, this->q); -} - -/* We need double here because the OpenCollada API needs it. - * precision = -1 indicates to not limit the precision. */ -void BCMatrix::get_matrix(DMatrix &mat, const bool transposed, const int precision) const -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - float val = (transposed) ? matrix[j][i] : matrix[i][j]; - if (precision >= 0) { - val = floor((val * pow(10, precision) + 0.5)) / pow(10, precision); - } - mat[i][j] = val; - } - } -} - -void BCMatrix::get_matrix(Matrix &mat, - const bool transposed, - const int precision, - const bool inverted) const -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - float val = (transposed) ? matrix[j][i] : matrix[i][j]; - if (precision >= 0) { - val = floor((val * pow(10, precision) + 0.5)) / pow(10, precision); - } - mat[i][j] = val; - } - } - - if (inverted) { - invert_m4(mat); - } -} - -const bool BCMatrix::in_range(const BCMatrix &other, float distance) const -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - if (fabs(other.matrix[i][j] - matrix[i][j]) > distance) { - return false; - } - } - } - return true; -} - -float (&BCMatrix::location() const)[3] -{ - return loc; -} - -float (&BCMatrix::rotation() const)[3] -{ - return rot; -} - -float (&BCMatrix::scale() const)[3] -{ - return size; -} - -float (&BCMatrix::quat() const)[4] -{ - return q; -} diff --git a/source/blender/collada/BCMath.h b/source/blender/collada/BCMath.h deleted file mode 100644 index 9ecea85b08c..00000000000 --- a/source/blender/collada/BCMath.h +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __BCMATH_H__ -#define __BCMATH_H__ - -#include "BlenderTypes.h" - -extern "C" { -#include "BKE_object.h" -#include "BLI_math.h" -} - -class BCQuat { - private: - mutable Quat q; - - public: - BCQuat(const BCQuat &other) - { - copy_v4_v4(q, other.q); - } - - BCQuat(Quat &other) - { - copy_v4_v4(q, other); - } - - BCQuat() - { - unit_qt(q); - } - - Quat &quat() - { - return q; - } - - void rotate_to(Matrix &mat_to); -}; - -class BCMatrix { - - private: - mutable float matrix[4][4]; - mutable float size[3]; - mutable float rot[3]; - mutable float loc[3]; - mutable float q[4]; - - void unit(); - void copy(Matrix &r, Matrix &a); - - public: - float (&location() const)[3]; - float (&rotation() const)[3]; - float (&scale() const)[3]; - float (&quat() const)[4]; - - BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis global_up_axis); - BCMatrix(const BCMatrix &mat); - BCMatrix(Matrix &mat); - BCMatrix(Object *ob); - BCMatrix(); - - void get_matrix(DMatrix &matrix, const bool transposed = false, const int precision = -1) const; - void get_matrix(Matrix &matrix, - const bool transposed = false, - const int precision = -1, - const bool inverted = false) const; - void set_transform(Object *ob); - void set_transform(Matrix &mat); - void add_transform(Matrix &to, - const Matrix &transform, - const Matrix &from, - const bool inverted = false); - void apply_transform(Matrix &to, - const Matrix &transform, - const Matrix &from, - const bool inverted = false); - void add_inverted_transform(Matrix &to, const Matrix &transform, const Matrix &from); - void add_transform(const Matrix &matrix, const bool inverted = false); - void add_transform(const BCMatrix &matrix, const bool inverted = false); - void apply_transform(const BCMatrix &matrix, const bool inverted = false); - - const bool in_range(const BCMatrix &other, float distance) const; - - static void sanitize(Matrix &matrix, int precision); - static void sanitize(DMatrix &matrix, int precision); - static void transpose(Matrix &matrix); -}; - -#endif /* __BCMATH_H__ */ diff --git a/source/blender/collada/BCSampleData.cpp b/source/blender/collada/BCSampleData.cpp deleted file mode 100644 index 7e23a2de00f..00000000000 --- a/source/blender/collada/BCSampleData.cpp +++ /dev/null @@ -1,97 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#include "BCSampleData.h" -#include "collada_utils.h" - -BCSample::~BCSample() -{ - BCBoneMatrixMap::iterator it; - for (it = bonemats.begin(); it != bonemats.end(); ++it) { - delete it->second; - } -} - -void BCSample::add_bone_matrix(Bone *bone, Matrix &mat) -{ - BCMatrix *matrix; - BCBoneMatrixMap::const_iterator it = bonemats.find(bone); - if (it != bonemats.end()) { - throw std::invalid_argument("bone " + std::string(bone->name) + " already defined before"); - } - matrix = new BCMatrix(mat); - bonemats[bone] = matrix; -} - -/* Get channel value */ -const bool BCSample::get_value(std::string channel_target, const int array_index, float *val) const -{ - std::string bname = bc_string_before(channel_target, "."); - std::string channel_type = bc_string_after(channel_target, "."); - - const BCMatrix *matrix = &obmat; - if (bname != channel_target) { - bname = bname.substr(2); - bname = bc_string_before(bname, "\""); - BCBoneMatrixMap::const_iterator it; - for (it = bonemats.begin(); it != bonemats.end(); ++it) { - Bone *bone = it->first; - if (bname == bone->name) { - matrix = it->second; - break; - } - } - } - else { - matrix = &obmat; - } - - if (channel_type == "location") { - *val = matrix->location()[array_index]; - } - else if (channel_type == "scale") { - *val = matrix->scale()[array_index]; - } - else if (channel_type == "rotation" || channel_type == "rotation_euler") { - *val = matrix->rotation()[array_index]; - } - else if (channel_type == "rotation_quaternion") { - *val = matrix->quat()[array_index]; - } - else { - *val = 0; - return false; - } - - return true; -} - -const BCMatrix *BCSample::get_matrix(Bone *bone) const -{ - BCBoneMatrixMap::const_iterator it = bonemats.find(bone); - if (it == bonemats.end()) { - return NULL; - } - return it->second; -} - -const BCMatrix &BCSample::get_matrix() const -{ - return obmat; -} diff --git a/source/blender/collada/BCSampleData.h b/source/blender/collada/BCSampleData.h deleted file mode 100644 index 07ecb544c71..00000000000 --- a/source/blender/collada/BCSampleData.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2008 Blender Foundation. - * All rights reserved. - */ - -#ifndef __BCSAMPLEDATA_H__ -#define __BCSAMPLEDATA_H__ - -#include -#include -#include - -#include "ExportSettings.h" -#include "BCSampleData.h" -#include "BCMath.h" - -extern "C" { -#include "BKE_object.h" -#include "BLI_math_rotation.h" -#include "DNA_object_types.h" -#include "DNA_armature_types.h" -#include "DNA_material_types.h" -#include "DNA_light_types.h" -#include "DNA_camera_types.h" -} - -typedef std::map BCBoneMatrixMap; - -class BCSample { - private: - BCMatrix obmat; - BCBoneMatrixMap bonemats; /* For Armature animation */ - - public: - BCSample(Object *ob) : obmat(ob) - { - } - - ~BCSample(); - - void add_bone_matrix(Bone *bone, Matrix &mat); - - const bool get_value(std::string channel_target, const int array_index, float *val) const; - const BCMatrix &get_matrix() const; - const BCMatrix *get_matrix(Bone *bone) const; // returns NULL if bone is not animated -}; - -typedef std::map BCSampleMap; -typedef std::map BCFrameSampleMap; -typedef std::map BCMatrixSampleMap; - -#endif /* __BCSAMPLEDATA_H__ */ diff --git a/source/blender/collada/BlenderContext.cpp b/source/blender/collada/BlenderContext.cpp deleted file mode 100644 index a9783a9b9c4..00000000000 --- a/source/blender/collada/BlenderContext.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -#include "BlenderContext.h" -#include "ExportSettings.h" - -#include "BKE_scene.h" - -bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer) -{ - Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, view_layer); - return (root == ob); -} - -/** - * Returns the highest selected ancestor - * returns NULL if no ancestor is selected - * IMPORTANT: This function expects that all exported objects have set: - * ob->id.tag & LIB_TAG_DOIT - */ -Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set, - Object *ob, - ViewLayer *view_layer) -{ - Object *ancestor = ob; - while (ob->parent) { - if (bc_is_in_Export_set(export_set, ob->parent, view_layer)) { - ancestor = ob->parent; - } - ob = ob->parent; - } - return ancestor; -} - -void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer) -{ - Base *base; - for (base = (Base *)view_layer->object_bases.first; base; base = base->next) { - Object *cob = base->object; - if (cob->parent == ob) { - switch (ob->type) { - case OB_MESH: - case OB_CAMERA: - case OB_LAMP: - case OB_EMPTY: - case OB_ARMATURE: - child_set.push_back(cob); - default: - break; - } - } - } -} - -bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer) -{ - bool to_export = (BLI_linklist_index(export_set, ob) != -1); - - if (!to_export) { - /* Mark this object as to_export even if it is not in the - export list, but it contains children to export */ - - std::vector children; - bc_get_children(children, ob, view_layer); - for (int i = 0; i < children.size(); i++) { - if (bc_is_in_Export_set(export_set, children[i], view_layer)) { - to_export = true; - break; - } - } - } - return to_export; -} - -int bc_is_marked(Object *ob) -{ - return ob && (ob->id.tag & LIB_TAG_DOIT); -} - -void bc_remove_mark(Object *ob) -{ - ob->id.tag &= ~LIB_TAG_DOIT; -} - -void bc_set_mark(Object *ob) -{ - ob->id.tag |= LIB_TAG_DOIT; -} - -BlenderContext::BlenderContext(bContext *C) -{ - context = C; - main = CTX_data_main(C); - scene = CTX_data_scene(C); - view_layer = CTX_data_view_layer(C); - depsgraph = nullptr; // create only when needed -} - -bContext *BlenderContext::get_context() -{ - return context; -} - -Depsgraph *BlenderContext::get_depsgraph() -{ - if (!depsgraph) { - depsgraph = BKE_scene_get_depsgraph(main, scene, view_layer, true); - } - return depsgraph; -} - -Scene *BlenderContext::get_scene() -{ - return scene; -} - -Scene *BlenderContext::get_evaluated_scene() -{ - Scene *scene_eval = DEG_get_evaluated_scene(get_depsgraph()); - return scene_eval; -} - -Object *BlenderContext::get_evaluated_object(Object *ob) -{ - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - return ob_eval; -} - -ViewLayer *BlenderContext::get_view_layer() -{ - return view_layer; -} - -Main *BlenderContext::get_main() -{ - return main; -} diff --git a/source/blender/collada/BlenderContext.h b/source/blender/collada/BlenderContext.h deleted file mode 100644 index 50781e8eede..00000000000 --- a/source/blender/collada/BlenderContext.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __BLENDERCONTEXT_H__ -#define __BLENDERCONTEXT_H__ - -#ifdef __cplusplus - -extern "C" { -#endif - -#include "DNA_object_types.h" -#include "BLI_linklist.h" -#include "BKE_context.h" -#include "BKE_main.h" -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" -#include "DNA_layer_types.h" -#include "BlenderTypes.h" - -static const BC_global_forward_axis BC_DEFAULT_FORWARD = BC_GLOBAL_FORWARD_Y; -static const BC_global_up_axis BC_DEFAULT_UP = BC_GLOBAL_UP_Z; - -bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer); -bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer); -Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set, - Object *ob, - ViewLayer *view_layer); -int bc_is_marked(Object *ob); -void bc_remove_mark(Object *ob); -void bc_set_mark(Object *ob); - -#ifdef __cplusplus -} - -class BlenderContext { - private: - bContext *context; - Depsgraph *depsgraph; - Scene *scene; - ViewLayer *view_layer; - Main *main; - - public: - BlenderContext(bContext *C); - bContext *get_context(); - Depsgraph *get_depsgraph(); - Scene *get_scene(); - Scene *get_evaluated_scene(); - Object *get_evaluated_object(Object *ob); - ViewLayer *get_view_layer(); - Main *get_main(); -}; -#endif - -#endif diff --git a/source/blender/collada/BlenderTypes.h b/source/blender/collada/BlenderTypes.h deleted file mode 100644 index 0e024be2374..00000000000 --- a/source/blender/collada/BlenderTypes.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __BLENDERTYPES_H__ -#define __BLENDERTYPES_H__ - -typedef float(Vector)[3]; -typedef float(Quat)[4]; -typedef float(Color)[4]; -typedef float(Matrix)[4][4]; -typedef double(DMatrix)[4][4]; - -typedef enum BC_global_forward_axis { - BC_GLOBAL_FORWARD_X = 0, - BC_GLOBAL_FORWARD_Y = 1, - BC_GLOBAL_FORWARD_Z = 2, - BC_GLOBAL_FORWARD_MINUS_X = 3, - BC_GLOBAL_FORWARD_MINUS_Y = 4, - BC_GLOBAL_FORWARD_MINUS_Z = 5 -} BC_global_forward_axis; - -typedef enum BC_global_up_axis { - BC_GLOBAL_UP_X = 0, - BC_GLOBAL_UP_Y = 1, - BC_GLOBAL_UP_Z = 2, - BC_GLOBAL_UP_MINUS_X = 3, - BC_GLOBAL_UP_MINUS_Y = 4, - BC_GLOBAL_UP_MINUS_Z = 5 -} BC_global_up_axis; - -#endif diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt deleted file mode 100644 index 6f1c6922d63..00000000000 --- a/source/blender/collada/CMakeLists.txt +++ /dev/null @@ -1,147 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The Original Code is Copyright (C) 2006, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -remove_strict_flags() -FIND_FILE(OPENCOLLADA_ANIMATION_CLIP - NAMES - COLLADAFWAnimationClip.h - PATHS - ${OPENCOLLADA_INCLUDE_DIRS} - NO_DEFAULT_PATH -) - -if(OPENCOLLADA_ANIMATION_CLIP) - add_definitions(-DWITH_OPENCOLLADA_ANIMATION_CLIP) -endif() - -set(INC - . - ../blenkernel - ../blenlib - ../blentranslation - ../depsgraph - ../editors/include - ../imbuf - ../makesdna - ../makesrna - ../windowmanager - ../../../intern/guardedalloc - ../ikplugin - ../../../intern/iksolver/extern - ../bmesh -) - -set(INC_SYS - ${OPENCOLLADA_INCLUDE_DIRS} -) - -set(SRC - AnimationClipExporter.cpp - AnimationExporter.cpp - AnimationImporter.cpp - ArmatureExporter.cpp - ArmatureImporter.cpp - BCAnimationCurve.cpp - BCAnimationSampler.cpp - BCMath.cpp - BCSampleData.cpp - BlenderContext.cpp - CameraExporter.cpp - ControllerExporter.cpp - DocumentExporter.cpp - DocumentImporter.cpp - EffectExporter.cpp - ErrorHandler.cpp - ExportSettings.cpp - ExtraHandler.cpp - ExtraTags.cpp - GeometryExporter.cpp - ImageExporter.cpp - ImportSettings.cpp - InstanceWriter.cpp - LightExporter.cpp - MaterialExporter.cpp - Materials.cpp - MeshImporter.cpp - SceneExporter.cpp - SkinInfo.cpp - TransformReader.cpp - TransformWriter.cpp - collada.cpp - collada_internal.cpp - collada_utils.cpp - - AnimationClipExporter.h - AnimationExporter.h - AnimationImporter.h - ArmatureExporter.h - ArmatureImporter.h - BCAnimationCurve.h - BCAnimationSampler.h - BCMath.h - BCSampleData.h - BlenderContext.h - BlenderTypes.h - CameraExporter.h - ControllerExporter.h - DocumentExporter.h - DocumentImporter.h - EffectExporter.h - ErrorHandler.h - ExportSettings.h - ExtraHandler.h - ExtraTags.h - GeometryExporter.h - ImageExporter.h - ImportSettings.h - InstanceWriter.h - LightExporter.h - MaterialExporter.h - Materials.h - MeshImporter.h - SceneExporter.h - SkinInfo.h - TransformReader.h - TransformWriter.h - collada.h - collada_internal.h - collada_utils.h -) - -set(LIB - ${OPENCOLLADA_LIBRARIES} - ${PCRE_LIBRARIES} - ${XML2_LIBRARIES} -) - -if(WITH_BUILDINFO) - add_definitions(-DWITH_BUILDINFO) -endif() - -if(WITH_INTERNATIONAL) - add_definitions(-DWITH_INTERNATIONAL) -endif() - -if(CMAKE_COMPILER_IS_GNUCXX) - # COLLADAFWArray.h gives error with gcc 4.5 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") -endif() - -blender_add_lib(bf_collada "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/collada/CameraExporter.cpp b/source/blender/collada/CameraExporter.cpp deleted file mode 100644 index 74862c44270..00000000000 --- a/source/blender/collada/CameraExporter.cpp +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -#include "COLLADASWCamera.h" - -extern "C" { -#include "DNA_camera_types.h" -} -#include "CameraExporter.h" - -#include "collada_internal.h" - -CamerasExporter::CamerasExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) - : COLLADASW::LibraryCameras(sw), export_settings(export_settings) -{ -} - -template -void forEachCameraObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) -{ - LinkNode *node; - for (node = export_set; node; node = node->next) { - Object *ob = (Object *)node->link; - - if (ob->type == OB_CAMERA && ob->data) { - f(ob, sce); - } - } -} - -void CamerasExporter::exportCameras(Scene *sce) -{ - openLibrary(); - - forEachCameraObjectInExportSet(sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); -} -void CamerasExporter::operator()(Object *ob, Scene *sce) -{ - Camera *cam = (Camera *)ob->data; - std::string cam_id(get_camera_id(ob)); - std::string cam_name(id_name(cam)); - - switch (cam->type) { - case CAM_PANO: - case CAM_PERSP: { - COLLADASW::PerspectiveOptic persp(mSW); - persp.setXFov(RAD2DEGF(focallength_to_fov(cam->lens, cam->sensor_x)), "xfov"); - persp.setAspectRatio((float)(sce->r.xsch) / (float)(sce->r.ysch), false, "aspect_ratio"); - persp.setZFar(cam->clip_end, false, "zfar"); - persp.setZNear(cam->clip_start, false, "znear"); - COLLADASW::Camera ccam(mSW, &persp, cam_id, cam_name); - exportBlenderProfile(ccam, cam); - addCamera(ccam); - - break; - } - case CAM_ORTHO: - default: { - COLLADASW::OrthographicOptic ortho(mSW); - ortho.setXMag(cam->ortho_scale / 2, "xmag"); - ortho.setAspectRatio((float)(sce->r.xsch) / (float)(sce->r.ysch), false, "aspect_ratio"); - ortho.setZFar(cam->clip_end, false, "zfar"); - ortho.setZNear(cam->clip_start, false, "znear"); - COLLADASW::Camera ccam(mSW, &ortho, cam_id, cam_name); - exportBlenderProfile(ccam, cam); - addCamera(ccam); - break; - } - } -} -bool CamerasExporter::exportBlenderProfile(COLLADASW::Camera &cm, Camera *cam) -{ - cm.addExtraTechniqueParameter("blender", "shiftx", cam->shiftx); - cm.addExtraTechniqueParameter("blender", "shifty", cam->shifty); - cm.addExtraTechniqueParameter("blender", "dof_distance", cam->dof.focus_distance); - return true; -} diff --git a/source/blender/collada/CameraExporter.h b/source/blender/collada/CameraExporter.h deleted file mode 100644 index 04bcc4a5dad..00000000000 --- a/source/blender/collada/CameraExporter.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __CAMERAEXPORTER_H__ -#define __CAMERAEXPORTER_H__ - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryCameras.h" - -extern "C" { -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -} - -#include "ExportSettings.h" -#include "DNA_camera_types.h" - -class CamerasExporter : COLLADASW::LibraryCameras { - public: - CamerasExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); - void exportCameras(Scene *sce); - void operator()(Object *ob, Scene *sce); - - private: - bool exportBlenderProfile(COLLADASW::Camera &cla, Camera *cam); - BCExportSettings &export_settings; -}; - -#endif diff --git a/source/blender/collada/ControllerExporter.cpp b/source/blender/collada/ControllerExporter.cpp deleted file mode 100644 index 0119aba7dfd..00000000000 --- a/source/blender/collada/ControllerExporter.cpp +++ /dev/null @@ -1,649 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "COLLADASWBaseInputElement.h" -#include "COLLADASWInstanceController.h" -#include "COLLADASWPrimitves.h" -#include "COLLADASWSource.h" - -#include "DNA_action_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" - -#include "BKE_action.h" -#include "BKE_armature.h" - -extern "C" { -#include "BKE_global.h" -#include "BKE_idprop.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -} - -#include "ED_armature.h" - -#include "BLI_listbase.h" - -#include "GeometryExporter.h" -#include "ArmatureExporter.h" -#include "ControllerExporter.h" -#include "SceneExporter.h" - -#include "collada_utils.h" - -bool ControllerExporter::is_skinned_mesh(Object *ob) -{ - return bc_get_assigned_armature(ob) != NULL; -} - -void ControllerExporter::write_bone_URLs(COLLADASW::InstanceController &ins, - Object *ob_arm, - Bone *bone) -{ - if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) { - std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name); - ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, node_id)); - } - else { - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - write_bone_URLs(ins, ob_arm, child); - } - } -} - -bool ControllerExporter::add_instance_controller(Object *ob) -{ - Object *ob_arm = bc_get_assigned_armature(ob); - bArmature *arm = (bArmature *)ob_arm->data; - - const std::string &controller_id = get_controller_id(ob_arm, ob); - - COLLADASW::InstanceController ins(mSW); - ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id)); - - Mesh *me = (Mesh *)ob->data; - if (!me->dvert) { - return false; - } - - /* write root bone URLs */ - Bone *bone; - for (bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { - write_bone_URLs(ins, ob_arm, bone); - } - - InstanceWriter::add_material_bindings( - ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); - - ins.add(); - return true; -} - -void ControllerExporter::export_controllers() -{ - Scene *sce = blender_context.get_scene(); - openLibrary(); - - GeometryFunctor gf; - gf.forEachMeshObjectInExportSet( - sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); -} - -void ControllerExporter::operator()(Object *ob) -{ - Object *ob_arm = bc_get_assigned_armature(ob); - Key *key = BKE_key_from_object(ob); - - if (ob_arm) { - export_skin_controller(ob, ob_arm); - } - if (key && this->export_settings.get_include_shapekeys()) { - export_morph_controller(ob, key); - } -} -#if 0 - -bool ArmatureExporter::already_written(Object *ob_arm) -{ - return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != - written_armatures.end(); -} - -void ArmatureExporter::wrote(Object *ob_arm) -{ - written_armatures.push_back(ob_arm); -} - -void ArmatureExporter::find_objects_using_armature(Object *ob_arm, - std::vector &objects, - Scene *sce) -{ - objects.clear(); - - Base *base = (Base *)sce->base.first; - while (base) { - Object *ob = base->object; - - if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) { - objects.push_back(ob); - } - - base = base->next; - } -} -#endif - -std::string ControllerExporter::get_controller_id(Object *ob_arm, Object *ob) -{ - return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + - SKIN_CONTROLLER_ID_SUFFIX; -} - -std::string ControllerExporter::get_controller_id(Key *key, Object *ob) -{ - return translate_id(id_name(ob)) + MORPH_CONTROLLER_ID_SUFFIX; -} - -/* ob should be of type OB_MESH - * both args are required */ -void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) -{ - /* joint names - * joint inverse bind matrices - * vertex weights */ - - /* input: - * joint names: ob -> vertex group names - * vertex group weights: me->dvert -> groups -> index, weight */ - - bool use_instantiation = this->export_settings.get_use_object_instantiation(); - Mesh *me; - - if (((Mesh *)ob->data)->dvert == NULL) { - return; - } - - me = bc_get_mesh_copy(blender_context, - ob, - this->export_settings.get_export_mesh_type(), - this->export_settings.get_apply_modifiers(), - this->export_settings.get_triangulate()); - - std::string controller_name = id_name(ob_arm); - std::string controller_id = get_controller_id(ob_arm, ob); - - openSkin(controller_id, - controller_name, - COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation))); - - add_bind_shape_mat(ob); - - std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id); - std::string inv_bind_mat_source_id = add_inv_bind_mats_source( - ob_arm, &ob->defbase, controller_id); - - std::list vcounts; - std::list joints; - std::list weights; - - { - int i, j; - - /* def group index -> joint index */ - std::vector joint_index_by_def_index; - bDeformGroup *def; - - for (def = (bDeformGroup *)ob->defbase.first, i = 0, j = 0; def; def = def->next, i++) { - if (is_bone_defgroup(ob_arm, def)) { - joint_index_by_def_index.push_back(j++); - } - else { - joint_index_by_def_index.push_back(-1); - } - } - - int oob_counter = 0; - for (i = 0; i < me->totvert; i++) { - MDeformVert *vert = &me->dvert[i]; - std::map jw; - - /* We're normalizing the weights later */ - float sumw = 0.0f; - - for (j = 0; j < vert->totweight; j++) { - uint idx = vert->dw[j].def_nr; - if (idx >= joint_index_by_def_index.size()) { - /* XXX: Maybe better find out where and - * why the Out Of Bound indexes get created ? */ - oob_counter += 1; - } - else { - int joint_index = joint_index_by_def_index[idx]; - if (joint_index != -1 && vert->dw[j].weight > 0.0f) { - jw[joint_index] += vert->dw[j].weight; - sumw += vert->dw[j].weight; - } - } - } - - if (sumw > 0.0f) { - float invsumw = 1.0f / sumw; - vcounts.push_back(jw.size()); - for (std::map::iterator m = jw.begin(); m != jw.end(); ++m) { - joints.push_back((*m).first); - weights.push_back(invsumw * (*m).second); - } - } - else { - vcounts.push_back(0); -#if 0 - vcounts.push_back(1); - joints.push_back(-1); - weights.push_back(1.0f); -#endif - } - } - - if (oob_counter > 0) { - fprintf(stderr, - "Ignored %d Vertex weights which use index to non existing VGroup %zu.\n", - oob_counter, - joint_index_by_def_index.size()); - } - } - - std::string weights_source_id = add_weights_source(me, controller_id, weights); - add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id); - add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints); - - BKE_id_free(NULL, me); - - closeSkin(); - closeController(); -} - -void ControllerExporter::export_morph_controller(Object *ob, Key *key) -{ - bool use_instantiation = this->export_settings.get_use_object_instantiation(); - Mesh *me; - - me = bc_get_mesh_copy(blender_context, - ob, - this->export_settings.get_export_mesh_type(), - this->export_settings.get_apply_modifiers(), - this->export_settings.get_triangulate()); - - std::string controller_name = id_name(ob) + "-morph"; - std::string controller_id = get_controller_id(key, ob); - - openMorph( - controller_id, - controller_name, - COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation))); - - std::string targets_id = add_morph_targets(key, ob); - std::string morph_weights_id = add_morph_weights(key, ob); - - COLLADASW::TargetsElement targets(mSW); - - COLLADASW::InputList &input = targets.getInputList(); - - input.push_back(COLLADASW::Input( - COLLADASW::InputSemantic::MORPH_TARGET, // constant declared in COLLADASWInputList.h - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, targets_id))); - input.push_back( - COLLADASW::Input(COLLADASW::InputSemantic::MORPH_WEIGHT, - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, morph_weights_id))); - targets.add(); - - BKE_id_free(NULL, me); - - /* support for animations - * can also try the base element and param alternative */ - add_weight_extras(key); - closeMorph(); - closeController(); -} - -std::string ControllerExporter::add_morph_targets(Key *key, Object *ob) -{ - std::string source_id = translate_id(id_name(ob)) + TARGETS_SOURCE_ID_SUFFIX; - - COLLADASW::IdRefSource source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(key->totkey - 1); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("IDREF"); - - source.prepareToAppendValues(); - - KeyBlock *kb = (KeyBlock *)key->block.first; - /* skip the basis */ - kb = kb->next; - for (; kb; kb = kb->next) { - std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name); - source.appendValues(geom_id); - } - - source.finish(); - - return source_id; -} - -std::string ControllerExporter::add_morph_weights(Key *key, Object *ob) -{ - std::string source_id = translate_id(id_name(ob)) + WEIGHTS_SOURCE_ID_SUFFIX; - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(key->totkey - 1); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("MORPH_WEIGHT"); - - source.prepareToAppendValues(); - - KeyBlock *kb = (KeyBlock *)key->block.first; - /* skip the basis */ - kb = kb->next; - for (; kb; kb = kb->next) { - float weight = kb->curval; - source.appendValues(weight); - } - source.finish(); - - return source_id; -} - -/* Added to implement support for animations. */ -void ControllerExporter::add_weight_extras(Key *key) -{ - /* can also try the base element and param alternative */ - COLLADASW::BaseExtraTechnique extra; - - KeyBlock *kb = (KeyBlock *)key->block.first; - /* skip the basis */ - kb = kb->next; - for (; kb; kb = kb->next) { - /* XXX why is the weight not used here and set to 0.0? - * float weight = kb->curval; */ - extra.addExtraTechniqueParameter("KHR", "morph_weights", 0.000, "MORPH_WEIGHT_TO_TARGET"); - } -} - -void ControllerExporter::add_joints_element(ListBase *defbase, - const std::string &joints_source_id, - const std::string &inv_bind_mat_source_id) -{ - COLLADASW::JointsElement joints(mSW); - COLLADASW::InputList &input = joints.getInputList(); - - input.push_back(COLLADASW::Input( - COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id))); - input.push_back( - COLLADASW::Input(COLLADASW::InputSemantic::BINDMATRIX, - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id))); - joints.add(); -} - -void ControllerExporter::add_bind_shape_mat(Object *ob) -{ - double bind_mat[4][4]; - float f_obmat[4][4]; - BKE_object_matrix_local_get(ob, f_obmat); - - if (export_settings.get_apply_global_orientation()) { - // do nothing, rotation is going to be applied to the Data - } - else { - bc_add_global_transform(f_obmat, export_settings.get_global_transform()); - } - - // UnitConverter::mat4_to_dae_double(bind_mat, ob->obmat); - UnitConverter::mat4_to_dae_double(bind_mat, f_obmat); - if (this->export_settings.get_limit_precision()) { - BCMatrix::sanitize(bind_mat, LIMITTED_PRECISION); - } - - addBindShapeTransform(bind_mat); -} - -std::string ControllerExporter::add_joints_source(Object *ob_arm, - ListBase *defbase, - const std::string &controller_id) -{ - std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX; - - int totjoint = 0; - bDeformGroup *def; - for (def = (bDeformGroup *)defbase->first; def; def = def->next) { - if (is_bone_defgroup(ob_arm, def)) { - totjoint++; - } - } - - COLLADASW::NameSource source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(totjoint); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("JOINT"); - - source.prepareToAppendValues(); - - for (def = (bDeformGroup *)defbase->first; def; def = def->next) { - Bone *bone = get_bone_from_defgroup(ob_arm, def); - if (bone) { - source.appendValues(get_joint_sid(bone)); - } - } - - source.finish(); - - return source_id; -} - -std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, - ListBase *defbase, - const std::string &controller_id) -{ - std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX; - - int totjoint = 0; - for (bDeformGroup *def = (bDeformGroup *)defbase->first; def; def = def->next) { - if (is_bone_defgroup(ob_arm, def)) { - totjoint++; - } - } - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(totjoint); // BLI_listbase_count(defbase)); - source.setAccessorStride(16); - - source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("TRANSFORM"); - - source.prepareToAppendValues(); - - bPose *pose = ob_arm->pose; - bArmature *arm = (bArmature *)ob_arm->data; - - int flag = arm->flag; - - /* put armature in rest position */ - if (!(arm->flag & ARM_RESTPOS)) { - Depsgraph *depsgraph = blender_context.get_depsgraph(); - Scene *scene = blender_context.get_scene(); - - arm->flag |= ARM_RESTPOS; - BKE_pose_where_is(depsgraph, scene, ob_arm); - } - - for (bDeformGroup *def = (bDeformGroup *)defbase->first; def; def = def->next) { - if (is_bone_defgroup(ob_arm, def)) { - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, def->name); - - float mat[4][4]; - float world[4][4]; - float inv_bind_mat[4][4]; - - float bind_mat[4][4]; /* derived from bone->arm_mat */ - - bool has_bindmat = bc_get_property_matrix(pchan->bone, "bind_mat", bind_mat); - - if (!has_bindmat) { - - /* Have no bind matrix stored, try old style <= Blender 2.78 */ - - bc_create_restpose_mat( - this->export_settings, pchan->bone, bind_mat, pchan->bone->arm_mat, true); - - /* SL/OPEN_SIM COMPATIBILITY */ - if (export_settings.get_open_sim()) { - float loc[3]; - float rot[3] = {0, 0, 0}; - float scale[3]; - bc_decompose(bind_mat, loc, NULL, NULL, scale); - - /* Only translations, no rotation vs armature */ - loc_eulO_size_to_mat4(bind_mat, loc, rot, scale, 6); - } - } - - /* make world-space matrix (bind_mat is armature-space) */ - mul_m4_m4m4(world, ob_arm->obmat, bind_mat); - - if (!has_bindmat) { - if (export_settings.get_apply_global_orientation()) { - bc_apply_global_transform(world, export_settings.get_global_transform()); - } - } - - invert_m4_m4(mat, world); - UnitConverter::mat4_to_dae(inv_bind_mat, mat); - if (this->export_settings.get_limit_precision()) { - BCMatrix::sanitize(inv_bind_mat, LIMITTED_PRECISION); - } - source.appendValues(inv_bind_mat); - } - } - - /* back from rest position */ - if (!(flag & ARM_RESTPOS)) { - Depsgraph *depsgraph = blender_context.get_depsgraph(); - Scene *scene = blender_context.get_scene(); - arm->flag = flag; - BKE_pose_where_is(depsgraph, scene, ob_arm); - } - - source.finish(); - - return source_id; -} - -Bone *ControllerExporter::get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def) -{ - bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, def->name); - return pchan ? pchan->bone : NULL; -} - -bool ControllerExporter::is_bone_defgroup(Object *ob_arm, bDeformGroup *def) -{ - return get_bone_from_defgroup(ob_arm, def) != NULL; -} - -std::string ControllerExporter::add_weights_source(Mesh *me, - const std::string &controller_id, - const std::list &weights) -{ - std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX; - - COLLADASW::FloatSourceF source(mSW); - source.setId(source_id); - source.setArrayId(source_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(weights.size()); - source.setAccessorStride(1); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("WEIGHT"); - - source.prepareToAppendValues(); - - for (std::list::const_iterator i = weights.begin(); i != weights.end(); ++i) { - source.appendValues(*i); - } - - source.finish(); - - return source_id; -} - -void ControllerExporter::add_vertex_weights_element(const std::string &weights_source_id, - const std::string &joints_source_id, - const std::list &vcounts, - const std::list &joints) -{ - COLLADASW::VertexWeightsElement weightselem(mSW); - COLLADASW::InputList &input = weightselem.getInputList(); - - int offset = 0; - input.push_back(COLLADASW::Input( - COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), - offset++)); - input.push_back( - COLLADASW::Input(COLLADASW::InputSemantic::WEIGHT, - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), - offset++)); - - weightselem.setCount(vcounts.size()); - - /* write number of deformers per vertex */ - COLLADASW::PrimitivesBase::VCountList vcountlist; - - vcountlist.resize(vcounts.size()); - std::copy(vcounts.begin(), vcounts.end(), vcountlist.begin()); - - weightselem.prepareToAppendVCountValues(); - weightselem.appendVertexCount(vcountlist); - - weightselem.CloseVCountAndOpenVElement(); - - /* write deformer index - weight index pairs */ - int weight_index = 0; - for (std::list::const_iterator i = joints.begin(); i != joints.end(); ++i) { - weightselem.appendValues(*i, weight_index++); - } - - weightselem.finish(); -} diff --git a/source/blender/collada/ControllerExporter.h b/source/blender/collada/ControllerExporter.h deleted file mode 100644 index ce2ed9fe453..00000000000 --- a/source/blender/collada/ControllerExporter.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __CONTROLLEREXPORTER_H__ -#define __CONTROLLEREXPORTER_H__ - -#include -#include -//#include - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryControllers.h" -#include "COLLADASWInstanceController.h" -#include "COLLADASWInputList.h" -#include "COLLADASWNode.h" -#include "COLLADASWExtraTechnique.h" - -#include "DNA_armature_types.h" -#include "DNA_listBase.h" -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_constraint_types.h" -#include "DNA_scene_types.h" -#include "DNA_key_types.h" - -#include "TransformWriter.h" -#include "InstanceWriter.h" - -#include "ExportSettings.h" - -#include "BKE_key.h" - -class SceneExporter; - -class ControllerExporter : public COLLADASW::LibraryControllers, - protected TransformWriter, - protected InstanceWriter { - private: - BlenderContext &blender_context; - BCExportSettings export_settings; - - public: - // XXX exporter writes wrong data for shared armatures. A separate - // controller should be written for each armature-mesh binding how do - // we make controller ids then? - ControllerExporter(BlenderContext &blender_context, - COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings) - : COLLADASW::LibraryControllers(sw), - blender_context(blender_context), - export_settings(export_settings) - { - } - - bool is_skinned_mesh(Object *ob); - - bool add_instance_controller(Object *ob); - - void export_controllers(); - - void operator()(Object *ob); - - private: -#if 0 - std::vector written_armatures; - - bool already_written(Object *ob_arm); - - void wrote(Object *ob_arm); - - void find_objects_using_armature(Object *ob_arm, std::vector &objects, Scene *sce); -#endif - - std::string get_controller_id(Object *ob_arm, Object *ob); - - std::string get_controller_id(Key *key, Object *ob); - - // ob should be of type OB_MESH - // both args are required - void export_skin_controller(Object *ob, Object *ob_arm); - - void export_morph_controller(Object *ob, Key *key); - - void add_joints_element(ListBase *defbase, - const std::string &joints_source_id, - const std::string &inv_bind_mat_source_id); - - void add_bind_shape_mat(Object *ob); - - std::string add_morph_targets(Key *key, Object *ob); - - std::string add_morph_weights(Key *key, Object *ob); - - void add_weight_extras(Key *key); - - std::string add_joints_source(Object *ob_arm, - ListBase *defbase, - const std::string &controller_id); - - std::string add_inv_bind_mats_source(Object *ob_arm, - ListBase *defbase, - const std::string &controller_id); - - Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def); - - bool is_bone_defgroup(Object *ob_arm, bDeformGroup *def); - - std::string add_weights_source(Mesh *me, - const std::string &controller_id, - const std::list &weights); - - void add_vertex_weights_element(const std::string &weights_source_id, - const std::string &joints_source_id, - const std::list &vcount, - const std::list &joints); - - void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); -}; - -#endif diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp deleted file mode 100644 index 24a960ab287..00000000000 --- a/source/blender/collada/DocumentExporter.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include -#include -#include -#include // std::find - -#include "COLLADASWCamera.h" -#include "COLLADASWAsset.h" -#include "COLLADASWLibraryVisualScenes.h" -#include "COLLADASWNode.h" -#include "COLLADASWSource.h" -#include "COLLADASWInstanceGeometry.h" -#include "COLLADASWInputList.h" -#include "COLLADASWPrimitves.h" -#include "COLLADASWVertices.h" -#include "COLLADASWLibraryAnimations.h" -#include "COLLADASWLibraryImages.h" -#include "COLLADASWLibraryEffects.h" -#include "COLLADASWImage.h" -#include "COLLADASWEffectProfile.h" -#include "COLLADASWColorOrTexture.h" -#include "COLLADASWParamTemplate.h" -#include "COLLADASWParamBase.h" -#include "COLLADASWSurfaceInitOption.h" -#include "COLLADASWSampler.h" -#include "COLLADASWScene.h" -#include "COLLADASWTechnique.h" -#include "COLLADASWTexture.h" -#include "COLLADASWLibraryMaterials.h" -#include "COLLADASWBindMaterial.h" -#include "COLLADASWInstanceCamera.h" -#include "COLLADASWInstanceLight.h" -#include "COLLADASWConstants.h" -#include "COLLADASWLibraryControllers.h" -#include "COLLADASWInstanceController.h" -#include "COLLADASWInstanceNode.h" -#include "COLLADASWBaseInputElement.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_scene_types.h" -#include "DNA_object_types.h" -#include "DNA_collection_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_mesh_types.h" -#include "DNA_image_types.h" -#include "DNA_material_types.h" -#include "DNA_anim_types.h" -#include "DNA_action_types.h" -#include "DNA_curve_types.h" -#include "DNA_armature_types.h" -#include "DNA_modifier_types.h" -#include "DNA_userdef_types.h" - -#include "BLI_path_util.h" -#include "BLI_fileops.h" -#include "BLI_math.h" -#include "BLI_string.h" -#include "BLI_listbase.h" -#include "BLI_utildefines.h" - -#include "BKE_action.h" // pose functions -#include "BKE_animsys.h" -#include "BKE_armature.h" -#include "BKE_blender_version.h" -#include "BKE_customdata.h" -#include "BKE_fcurve.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_material.h" -#include "BKE_object.h" -#include "BKE_scene.h" -#include "BKE_appdir.h" - -#include "ED_keyframing.h" -#ifdef WITH_BUILDINFO -extern char build_commit_date[]; -extern char build_commit_time[]; -extern char build_hash[]; -#endif - -#include "RNA_access.h" -} - -#include "collada_internal.h" -#include "collada_utils.h" -#include "DocumentExporter.h" - -extern bool bc_has_object_type(LinkNode *export_set, short obtype); - -// can probably go after refactor is complete -#include "InstanceWriter.h" -#include "TransformWriter.h" - -#include "SceneExporter.h" -#include "ArmatureExporter.h" -#include "AnimationExporter.h" -#include "CameraExporter.h" -#include "ControllerExporter.h" -#include "EffectExporter.h" -#include "GeometryExporter.h" -#include "ImageExporter.h" -#include "LightExporter.h" -#include "MaterialExporter.h" - -#include - -char *bc_CustomData_get_layer_name(const struct CustomData *data, int type, int n) -{ - int layer_index = CustomData_get_layer_index(data, type); - if (layer_index < 0) { - return NULL; - } - - return data->layers[layer_index + n].name; -} - -char *bc_CustomData_get_active_layer_name(const CustomData *data, int type) -{ - /* get the layer index of the active layer of type */ - int layer_index = CustomData_get_active_layer_index(data, type); - if (layer_index < 0) { - return NULL; - } - - return data->layers[layer_index].name; -} - -DocumentExporter::DocumentExporter(BlenderContext &blender_context, ExportSettings *exportSettings) - : blender_context(blender_context), - export_settings(BCExportSettings(exportSettings, blender_context)) -{ -} - -static COLLADABU::NativeString make_temp_filepath(const char *name, const char *extension) -{ - char tempfile[FILE_MAX]; - const char *tempdir = BKE_tempdir_session(); - - if (name == NULL) { - name = "untitled"; - } - - BLI_make_file_string(NULL, tempfile, tempdir, name); - - if (extension) { - BLI_path_extension_ensure(tempfile, FILE_MAX, extension); - } - - COLLADABU::NativeString native_filename = COLLADABU::NativeString( - tempfile, COLLADABU::NativeString::ENCODING_UTF8); - return native_filename; -} - -// TODO: it would be better to instantiate animations rather than create a new one per object -// COLLADA allows this through multiple s in . -// For this to work, we need to know objects that use a certain action. - -int DocumentExporter::exportCurrentScene() -{ - Scene *sce = blender_context.get_scene(); - bContext *C = blender_context.get_context(); - - PointerRNA sceneptr, unit_settings; - PropertyRNA *system; /* unused , *scale; */ - - clear_global_id_map(); - - COLLADABU::NativeString native_filename = make_temp_filepath(NULL, ".dae"); - COLLADASW::StreamWriter *writer = new COLLADASW::StreamWriter(native_filename); - - // open - writer->startDocument(); - - // - COLLADASW::Asset asset(writer); - - RNA_id_pointer_create(&(sce->id), &sceneptr); - unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); - system = RNA_struct_find_property(&unit_settings, "system"); - // scale = RNA_struct_find_property(&unit_settings, "scale_length"); - - std::string unitname = "meter"; - float linearmeasure = RNA_float_get(&unit_settings, "scale_length"); - - switch (RNA_property_enum_get(&unit_settings, system)) { - case USER_UNIT_NONE: - case USER_UNIT_METRIC: - if (linearmeasure == 0.001f) { - unitname = "millimeter"; - } - else if (linearmeasure == 0.01f) { - unitname = "centimeter"; - } - else if (linearmeasure == 0.1f) { - unitname = "decimeter"; - } - else if (linearmeasure == 1.0f) { - unitname = "meter"; - } - else if (linearmeasure == 1000.0f) { - unitname = "kilometer"; - } - break; - case USER_UNIT_IMPERIAL: - if (linearmeasure == 0.0254f) { - unitname = "inch"; - } - else if (linearmeasure == 0.3048f) { - unitname = "foot"; - } - else if (linearmeasure == 0.9144f) { - unitname = "yard"; - } - break; - default: - break; - } - - asset.setUnit(unitname, linearmeasure); - asset.setUpAxisType(COLLADASW::Asset::Z_UP); - asset.getContributor().mAuthor = "Blender User"; - char version_buf[128]; -#ifdef WITH_BUILDINFO - BLI_snprintf(version_buf, - sizeof(version_buf), - "Blender %d.%02d.%d commit date:%s, commit time:%s, hash:%s", - BLENDER_VERSION / 100, - BLENDER_VERSION % 100, - BLENDER_SUBVERSION, - build_commit_date, - build_commit_time, - build_hash); -#else - BLI_snprintf(version_buf, - sizeof(version_buf), - "Blender %d.%02d.%d", - BLENDER_VERSION / 100, - BLENDER_VERSION % 100, - BLENDER_SUBVERSION); -#endif - asset.getContributor().mAuthoringTool = version_buf; - asset.add(); - - LinkNode *export_set = this->export_settings.get_export_set(); - // - if (bc_has_object_type(export_set, OB_CAMERA)) { - CamerasExporter ce(writer, this->export_settings); - ce.exportCameras(sce); - } - - // - if (bc_has_object_type(export_set, OB_LAMP)) { - LightsExporter le(writer, this->export_settings); - le.exportLights(sce); - } - - // - EffectsExporter ee(writer, this->export_settings, key_image_map); - ee.exportEffects(C, sce); - - // - ImagesExporter ie(writer, this->export_settings, key_image_map); - ie.exportImages(sce); - - // - MaterialsExporter me(writer, this->export_settings); - me.exportMaterials(sce); - - // - if (bc_has_object_type(export_set, OB_MESH)) { - GeometryExporter ge(blender_context, writer, this->export_settings); - ge.exportGeom(); - } - - // - ArmatureExporter arm_exporter(blender_context, writer, this->export_settings); - ControllerExporter controller_exporter(blender_context, writer, this->export_settings); - if (bc_has_object_type(export_set, OB_ARMATURE) || - this->export_settings.get_include_shapekeys()) { - controller_exporter.export_controllers(); - } - - // - - SceneExporter se(blender_context, writer, &arm_exporter, this->export_settings); - - if (this->export_settings.get_include_animations()) { - // - AnimationExporter ae(writer, this->export_settings); - ae.exportAnimations(); - } - - se.exportScene(); - - // - std::string scene_name(translate_id(id_name(sce))); - COLLADASW::Scene scene(writer, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, scene_name)); - scene.add(); - - // close - writer->endDocument(); - delete writer; - - // Finally move the created document into place - fprintf(stdout, "Collada export to: %s\n", this->export_settings.get_filepath()); - int status = BLI_rename(native_filename.c_str(), this->export_settings.get_filepath()); - if (status != 0) { - status = BLI_copy(native_filename.c_str(), this->export_settings.get_filepath()); - BLI_delete(native_filename.c_str(), false, false); - } - return status; -} - -void DocumentExporter::exportScenes(const char *filename) -{ -} - -/* - * NOTES: - * - * AnimationExporter::sample_animation enables all curves on armature, this is undesirable for a - * user - */ diff --git a/source/blender/collada/DocumentExporter.h b/source/blender/collada/DocumentExporter.h deleted file mode 100644 index 70722ae601e..00000000000 --- a/source/blender/collada/DocumentExporter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __DOCUMENTEXPORTER_H__ -#define __DOCUMENTEXPORTER_H__ - -#include "collada.h" -#include "collada_utils.h" -#include "BlenderContext.h" - -extern "C" { -#include "DNA_customdata_types.h" -} - -class DocumentExporter { - public: - DocumentExporter(BlenderContext &blender_context, ExportSettings *export_settings); - int exportCurrentScene(); - void exportScenes(const char *filename); - - private: - BlenderContext &blender_context; - BCExportSettings export_settings; - KeyImageMap key_image_map; -}; - -#endif diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp deleted file mode 100644 index 9b66ff429e1..00000000000 --- a/source/blender/collada/DocumentImporter.cpp +++ /dev/null @@ -1,1265 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* TODO: - * * name imported objects - * * import object rotation as euler */ - -#include -#include -#include // sort() - -#include "COLLADAFWRoot.h" -#include "COLLADAFWStableHeaders.h" -#include "COLLADAFWColorOrTexture.h" -#include "COLLADAFWIndexList.h" -#include "COLLADAFWMeshPrimitiveWithFaceVertexCount.h" -#include "COLLADAFWPolygons.h" -#include "COLLADAFWSampler.h" -#include "COLLADAFWTypes.h" -#include "COLLADAFWVisualScene.h" -#include "COLLADAFWArrayPrimitiveType.h" -#include "COLLADAFWLibraryNodes.h" -#include "COLLADAFWCamera.h" -#include "COLLADAFWLight.h" - -#include "COLLADASaxFWLLoader.h" -#include "COLLADASaxFWLIExtraDataCallbackHandler.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_string.h" -#include "BLI_utildefines.h" -#include "BLI_fileops.h" - -#include "BKE_camera.h" -#include "BKE_collection.h" -#include "BKE_fcurve.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_layer.h" -#include "BKE_light.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_scene.h" - -#include "BLI_path_util.h" - -#include "DNA_camera_types.h" -#include "DNA_light_types.h" - -#include "RNA_access.h" - -#include "WM_api.h" -#include "WM_types.h" -} - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" - -#include "ExtraHandler.h" -#include "ErrorHandler.h" -#include "DocumentImporter.h" -#include "TransformReader.h" - -#include "collada_internal.h" -#include "collada_utils.h" -#include "Materials.h" - -/* - * COLLADA Importer limitations: - * - no multiple scene import, all objects are added to active scene - */ - -// #define COLLADA_DEBUG -// creates empties for each imported bone on layer 2, for debugging -// #define ARMATURE_TEST - -DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_settings) - : import_settings(import_settings), - mImportStage(Fetching_Scene_data), - mContext(C), - view_layer(CTX_data_view_layer(mContext)), - armature_importer(&unit_converter, - &mesh_importer, - CTX_data_main(C), - CTX_data_scene(C), - view_layer, - import_settings), - mesh_importer( - &unit_converter, &armature_importer, CTX_data_main(C), CTX_data_scene(C), view_layer), - anim_importer(C, &unit_converter, &armature_importer, CTX_data_scene(C)) -{ -} - -DocumentImporter::~DocumentImporter() -{ - TagsMap::iterator etit; - etit = uid_tags_map.begin(); - while (etit != uid_tags_map.end()) { - delete etit->second; - etit++; - } -} - -bool DocumentImporter::import() -{ - ErrorHandler errorHandler; - COLLADASaxFWL::Loader loader(&errorHandler); - COLLADAFW::Root root(&loader, this); - ExtraHandler *ehandler = new ExtraHandler(this, &(this->anim_importer)); - - loader.registerExtraDataCallbackHandler(ehandler); - - /* deselect all to select new objects */ - BKE_view_layer_base_deselect_all(view_layer); - - std::string mFilename = std::string(this->import_settings->filepath); - const std::string encodedFilename = bc_url_encode(mFilename); - if (!root.loadDocument(encodedFilename)) { - fprintf(stderr, "COLLADAFW::Root::loadDocument() returned false on 1st pass\n"); - delete ehandler; - return false; - } - - if (errorHandler.hasError()) { - delete ehandler; - return false; - } - - /** TODO set up scene graph and such here */ - mImportStage = Fetching_Controller_data; - COLLADASaxFWL::Loader loader2; - COLLADAFW::Root root2(&loader2, this); - - if (!root2.loadDocument(encodedFilename)) { - fprintf(stderr, "COLLADAFW::Root::loadDocument() returned false on 2nd pass\n"); - delete ehandler; - return false; - } - - delete ehandler; - - return true; -} - -void DocumentImporter::cancel(const COLLADAFW::String &errorMessage) -{ - /* TODO: if possible show error info - * - * Should we get rid of invisible Meshes that were created so far - * or maybe create objects at coordinate space origin? - * - * The latter sounds better. */ -} - -void DocumentImporter::start() -{ -} - -void DocumentImporter::finish() -{ - if (mImportStage == Fetching_Controller_data) { - return; - } - - Main *bmain = CTX_data_main(mContext); - /* TODO: create a new scene except the selected - - * use current blender scene for it */ - Scene *sce = CTX_data_scene(mContext); - unit_converter.calculate_scale(*sce); - - std::vector *objects_to_scale = new std::vector(); - - /** TODO Break up and put into 2-pass parsing of DAE */ - std::vector::iterator sit; - for (sit = vscenes.begin(); sit != vscenes.end(); sit++) { - PointerRNA sceneptr, unit_settings; - PropertyRNA *system, *scale; - - /* for scene unit settings: system, scale_length */ - - RNA_id_pointer_create(&sce->id, &sceneptr); - unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); - system = RNA_struct_find_property(&unit_settings, "system"); - scale = RNA_struct_find_property(&unit_settings, "scale_length"); - - if (this->import_settings->import_units) { - - switch (unit_converter.isMetricSystem()) { - case UnitConverter::Metric: - RNA_property_enum_set(&unit_settings, system, USER_UNIT_METRIC); - break; - case UnitConverter::Imperial: - RNA_property_enum_set(&unit_settings, system, USER_UNIT_IMPERIAL); - break; - default: - RNA_property_enum_set(&unit_settings, system, USER_UNIT_NONE); - break; - } - float unit_factor = unit_converter.getLinearMeter(); - RNA_property_float_set(&unit_settings, scale, unit_factor); - fprintf(stdout, "Collada: Adjusting Blender units to Importset units: %f.\n", unit_factor); - } - - /* Write nodes to scene */ - fprintf(stderr, "+-- Import Scene --------\n"); - const COLLADAFW::NodePointerArray &roots = (*sit)->getRootNodes(); - for (unsigned int i = 0; i < roots.getCount(); i++) { - std::vector *objects_done = write_node(roots[i], NULL, sce, NULL, false); - objects_to_scale->insert( - objects_to_scale->end(), objects_done->begin(), objects_done->end()); - delete objects_done; - } - } - - mesh_importer.optimize_material_assignements(); - - armature_importer.set_tags_map(this->uid_tags_map); - armature_importer.make_armatures(mContext, *objects_to_scale); - armature_importer.make_shape_keys(mContext); - -#if 0 - armature_importer.fix_animation(); -#endif - - for (std::vector::iterator vsit = vscenes.begin(); - vsit != vscenes.end(); - vsit++) { - const COLLADAFW::NodePointerArray &roots = (*vsit)->getRootNodes(); - - for (unsigned int i = 0; i < roots.getCount(); i++) { - translate_anim_recursive(roots[i], NULL, NULL); - } - } - - if (libnode_ob.size()) { - - fprintf(stderr, "| Cleanup: free %d library nodes\n", (int)libnode_ob.size()); - /* free all library_nodes */ - std::vector::iterator it; - for (it = libnode_ob.begin(); it != libnode_ob.end(); it++) { - Object *ob = *it; - BKE_scene_collections_object_remove(bmain, sce, ob, true); - } - libnode_ob.clear(); - } - - bc_match_scale(objects_to_scale, unit_converter, !this->import_settings->import_units); - - delete objects_to_scale; - - /* update scene */ - DEG_id_tag_update(&sce->id, ID_RECALC_COPY_ON_WRITE); - DEG_relations_tag_update(bmain); - WM_event_add_notifier(mContext, NC_OBJECT | ND_TRANSFORM, NULL); -} - -void DocumentImporter::translate_anim_recursive(COLLADAFW::Node *node, - COLLADAFW::Node *par = NULL, - Object *parob = NULL) -{ - /* The split in #29246, rootmap must point at actual root when - * calculating bones in apply_curves_as_matrix. - actual root is the root node. - * This has to do with inverse bind poses being world space - * (the sources for skinned bones' restposes) and the way - * non-skinning nodes have their "restpose" recursively calculated. - * XXX TODO: design issue, how to support unrelated joints taking - * part in skinning. */ - if (par) { // && par->getType() == COLLADAFW::Node::JOINT) { - /* par is root if there's no corresp. key in root_map */ - if (root_map.find(par->getUniqueId()) == root_map.end()) { - root_map[node->getUniqueId()] = node; - } - else { - root_map[node->getUniqueId()] = root_map[par->getUniqueId()]; - } - } - -#if 0 - COLLADAFW::Transformation::TransformationType types[] = { - COLLADAFW::Transformation::ROTATE, - COLLADAFW::Transformation::SCALE, - COLLADAFW::Transformation::TRANSLATE, - COLLADAFW::Transformation::MATRIX, - }; - - Object *ob; -#endif - unsigned int i; - - if (node->getType() == COLLADAFW::Node::JOINT && par == NULL) { - /* For Skeletons without root node we have to simulate the - * root node here and recursively enter the same function - * XXX: maybe this can be made more elegant. */ - translate_anim_recursive(node, node, parob); - } - else { - anim_importer.translate_Animations( - node, root_map, object_map, FW_object_map, uid_material_map); - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - for (i = 0; i < children.getCount(); i++) { - translate_anim_recursive(children[i], node, NULL); - } - } -} - -/** - * If the imported file was made with Blender, return the Blender version used, - * otherwise return an empty std::string - */ -std::string DocumentImporter::get_import_version(const COLLADAFW::FileInfo *asset) -{ - const char AUTORING_TOOL[] = "authoring_tool"; - const std::string BLENDER("Blender "); - const COLLADAFW::FileInfo::ValuePairPointerArray &valuePairs = asset->getValuePairArray(); - for (size_t i = 0, count = valuePairs.getCount(); i < count; i++) { - const COLLADAFW::FileInfo::ValuePair *valuePair = valuePairs[i]; - const COLLADAFW::String &key = valuePair->first; - const COLLADAFW::String &value = valuePair->second; - if (key == AUTORING_TOOL) { - if (value.compare(0, BLENDER.length(), BLENDER) == 0) { - /* Was made with Blender, now get version string */ - std::string v = value.substr(BLENDER.length()); - std::string::size_type n = v.find(" "); - if (n > 0) { - return v.substr(0, n); - } - } - } - } - return ""; -} - -/** - * When this method is called, the writer must write the global document asset. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeGlobalAsset(const COLLADAFW::FileInfo *asset) -{ - unit_converter.read_asset(asset); - import_from_version = get_import_version(asset); - anim_importer.set_import_from_version(import_from_version); - return true; -} - -/** - * When this method is called, the writer must write the scene. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeScene(const COLLADAFW::Scene *scene) -{ - /* XXX could store the scene id, but do nothing for now */ - return true; -} -Object *DocumentImporter::create_camera_object(COLLADAFW::InstanceCamera *camera, Scene *sce) -{ - const COLLADAFW::UniqueId &cam_uid = camera->getInstanciatedObjectId(); - if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) { - // fprintf(stderr, "Couldn't find camera by UID.\n"); - return NULL; - } - - Main *bmain = CTX_data_main(mContext); - Object *ob = bc_add_object(bmain, sce, view_layer, OB_CAMERA, NULL); - Camera *cam = uid_camera_map[cam_uid]; - Camera *old_cam = (Camera *)ob->data; - ob->data = cam; - BKE_id_free_us(bmain, old_cam); - return ob; -} - -Object *DocumentImporter::create_light_object(COLLADAFW::InstanceLight *lamp, Scene *sce) -{ - const COLLADAFW::UniqueId &lamp_uid = lamp->getInstanciatedObjectId(); - if (uid_light_map.find(lamp_uid) == uid_light_map.end()) { - fprintf(stderr, "Couldn't find light by UID.\n"); - return NULL; - } - - Main *bmain = CTX_data_main(mContext); - Object *ob = bc_add_object(bmain, sce, view_layer, OB_LAMP, NULL); - Light *la = uid_light_map[lamp_uid]; - Light *old_light = (Light *)ob->data; - ob->data = la; - BKE_id_free_us(bmain, old_light); - return ob; -} - -Object *DocumentImporter::create_instance_node(Object *source_ob, - COLLADAFW::Node *source_node, - COLLADAFW::Node *instance_node, - Scene *sce, - bool is_library_node) -{ - // fprintf(stderr, "create under node id=%s from node id=%s\n", instance_node ? - // instance_node->getOriginalId().c_str() : NULL, source_node ? - // source_node->getOriginalId().c_str() : NULL); - - Main *bmain = CTX_data_main(mContext); - Object *obn = BKE_object_copy(bmain, source_ob); - DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - BKE_collection_object_add_from(bmain, sce, source_ob, obn); - - if (instance_node) { - anim_importer.read_node_transform(instance_node, obn); - /* if we also have a source_node (always ;), take its - * transformation matrix and apply it to the newly instantiated - * object to account for node hierarchy transforms in - * .dae */ - if (source_node) { - COLLADABU::Math::Matrix4 mat4 = source_node->getTransformationMatrix(); - COLLADABU::Math::Matrix4 bmat4 = - mat4.transpose(); // transpose to get blender row-major order - float mat[4][4]; - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - mat[i][j] = bmat4[i][j]; - } - } - /* calc new matrix and apply */ - mul_m4_m4m4(obn->obmat, obn->obmat, mat); - BKE_object_apply_mat4(obn, obn->obmat, 0, 0); - } - } - else { - anim_importer.read_node_transform(source_node, obn); - } - - /*DAG_relations_tag_update(CTX_data_main(mContext));*/ - - COLLADAFW::NodePointerArray &children = source_node->getChildNodes(); - if (children.getCount()) { - for (unsigned int i = 0; i < children.getCount(); i++) { - COLLADAFW::Node *child_node = children[i]; - const COLLADAFW::UniqueId &child_id = child_node->getUniqueId(); - if (object_map.find(child_id) == object_map.end()) { - continue; - } - COLLADAFW::InstanceNodePointerArray &inodes = child_node->getInstanceNodes(); - Object *new_child = NULL; - if (inodes.getCount()) { // \todo loop through instance nodes - const COLLADAFW::UniqueId &id = inodes[0]->getInstanciatedObjectId(); - fprintf(stderr, "Doing %d child nodes\n", (int)node_map.count(id)); - new_child = create_instance_node( - object_map.find(id)->second, node_map[id], child_node, sce, is_library_node); - } - else { - new_child = create_instance_node( - object_map.find(child_id)->second, child_node, NULL, sce, is_library_node); - } - bc_set_parent(new_child, obn, mContext, true); - - if (is_library_node) { - libnode_ob.push_back(new_child); - } - } - } - - return obn; -} - -/* to create constraints off node tags. Assumes only constraint data in - * current with blender profile. */ -void DocumentImporter::create_constraints(ExtraTags *et, Object *ob) -{ - if (et && et->isProfile("blender")) { - std::string name; - short type = 0; - et->setData("type", &type); - BKE_constraint_add_for_object(ob, "Test_con", type); - } -} - -void DocumentImporter::report_unknown_reference(const COLLADAFW::Node &node, - const std::string object_type) -{ - std::string id = node.getOriginalId(); - std::string name = node.getName(); - fprintf(stderr, - "error: node id=\"%s\", name=\"%s\" refers to an undefined %s.\n", - id.c_str(), - name.c_str(), - object_type.c_str()); -} - -std::vector *DocumentImporter::write_node(COLLADAFW::Node *node, - COLLADAFW::Node *parent_node, - Scene *sce, - Object *par, - bool is_library_node) -{ - Main *bmain = CTX_data_main(mContext); - Object *ob = NULL; - bool is_joint = node->getType() == COLLADAFW::Node::JOINT; - bool read_transform = true; - std::string id = node->getOriginalId(); - std::string name = node->getName(); - - /* if node has child nodes write them */ - COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes(); - - std::vector *objects_done = new std::vector(); - std::vector *root_objects = new std::vector(); - - fprintf( - stderr, "| %s id='%s', name='%s'\n", is_joint ? "JOINT" : "NODE ", id.c_str(), name.c_str()); - - if (is_joint) { - if (parent_node == NULL && !is_library_node) { - /* A Joint on root level is a skeleton without root node. - * Here we add the armature "on the fly": */ - par = bc_add_object(bmain, sce, view_layer, OB_ARMATURE, std::string("Armature").c_str()); - objects_done->push_back(par); - root_objects->push_back(par); - object_map.insert(std::pair(node->getUniqueId(), par)); - node_map[node->getUniqueId()] = node; - } - if (parent_node == NULL || parent_node->getType() != COLLADAFW::Node::JOINT) { - armature_importer.add_root_joint(node, par); - } - - if (parent_node == NULL) { - /* for skeletons without root node all has been done above. - * Skeletons with root node are handled further down. */ - goto finally; - } - } - else { - COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries(); - COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras(); - COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights(); - COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers(); - COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes(); - size_t geom_done = 0; - size_t camera_done = 0; - size_t lamp_done = 0; - size_t controller_done = 0; - size_t inst_done = 0; - - /* XXX linking object with the first , though a node may have more of - * them... maybe join multiple meshes into 1, and link object with it? not - * sure... */ - while (geom_done < geom.getCount()) { - ob = mesh_importer.create_mesh_object(node, geom[geom_done], false, uid_material_map); - if (ob == NULL) { - report_unknown_reference(*node, "instance_mesh"); - } - else { - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - geom_done++; - } - while (camera_done < camera.getCount()) { - ob = create_camera_object(camera[camera_done], sce); - if (ob == NULL) { - report_unknown_reference(*node, "instance_camera"); - } - else { - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - camera_done++; - } - while (lamp_done < lamp.getCount()) { - ob = create_light_object(lamp[lamp_done], sce); - if (ob == NULL) { - report_unknown_reference(*node, "instance_light"); - } - else { - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - lamp_done++; - } - while (controller_done < controller.getCount()) { - COLLADAFW::InstanceGeometry *geometry = (COLLADAFW::InstanceGeometry *) - controller[controller_done]; - ob = mesh_importer.create_mesh_object(node, geometry, true, uid_material_map); - if (ob == NULL) { - report_unknown_reference(*node, "instance_controller"); - } - else { - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - controller_done++; - } - /* XXX instance_node is not supported yet */ - while (inst_done < inst_node.getCount()) { - const COLLADAFW::UniqueId &node_id = inst_node[inst_done]->getInstanciatedObjectId(); - if (object_map.find(node_id) == object_map.end()) { - fprintf(stderr, - "Cannot find object for node referenced by .\n", - inst_node[inst_done]->getName().c_str()); - ob = NULL; - } - else { - std::pair::iterator, - std::multimap::iterator> - pair_iter = object_map.equal_range(node_id); - for (std::multimap::iterator it2 = pair_iter.first; - it2 != pair_iter.second; - it2++) { - Object *source_ob = (Object *)it2->second; - COLLADAFW::Node *source_node = node_map[node_id]; - ob = create_instance_node(source_ob, source_node, node, sce, is_library_node); - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - } - inst_done++; - - read_transform = false; - } - - /* if node is empty - create empty object - * XXX empty node may not mean it is empty object, not sure about this */ - if ((geom_done + camera_done + lamp_done + controller_done + inst_done) < 1) { - /* Check if Object is armature, by checking if immediate child is a JOINT node. */ - if (is_armature(node)) { - ob = bc_add_object(bmain, sce, view_layer, OB_ARMATURE, name.c_str()); - } - else { - ob = bc_add_object(bmain, sce, view_layer, OB_EMPTY, NULL); - } - objects_done->push_back(ob); - if (parent_node == NULL) { - root_objects->push_back(ob); - } - } - - /* XXX: if there're multiple instances, only one is stored */ - - if (!ob) { - goto finally; - } - - for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); - ++it) { - ob = *it; - std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId(); - BKE_libblock_rename(bmain, &ob->id, (char *)nodename.c_str()); - object_map.insert(std::pair(node->getUniqueId(), ob)); - node_map[node->getUniqueId()] = node; - - if (is_library_node) { - libnode_ob.push_back(ob); - } - } - - // create_constraints(et,ob); - } - - for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); - ++it) { - ob = *it; - - if (read_transform) { - anim_importer.read_node_transform(node, ob); // overwrites location set earlier - } - - if (!is_joint) { - if (par && ob) { - ob->parent = par; - ob->partype = PAROBJECT; - ob->parsubstr[0] = 0; - - // bc_set_parent(ob, par, mContext, false); - } - } - } - - if (objects_done->size() > 0) { - ob = *objects_done->begin(); - } - else { - ob = NULL; - } - - for (unsigned int i = 0; i < child_nodes.getCount(); i++) { - std::vector *child_objects; - child_objects = write_node(child_nodes[i], node, sce, ob, is_library_node); - delete child_objects; - } - -finally: - delete objects_done; - - return root_objects; -} - -/** - * When this method is called, the writer must write the entire visual scene. - * Return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeVisualScene(const COLLADAFW::VisualScene *visualScene) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - /* This method called on post process after writeGeometry, writeMaterial, etc. */ - - /* For each in : - * create an Object - * if Mesh (previously created in writeGeometry) to which corresponds exists, - * link Object with that mesh. - * - * Update: since we cannot link a Mesh with Object in - * writeGeometry because does not reference , - * we link Objects with Meshes here. - */ - vscenes.push_back(visualScene); - - return true; -} - -/** - * When this method is called, the writer must handle all nodes contained in the - * library nodes. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeLibraryNodes(const COLLADAFW::LibraryNodes *libraryNodes) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - Scene *sce = CTX_data_scene(mContext); - - const COLLADAFW::NodePointerArray &nodes = libraryNodes->getNodes(); - - fprintf(stderr, "+-- Read Library nodes ----------\n"); - for (unsigned int i = 0; i < nodes.getCount(); i++) { - std::vector *child_objects; - child_objects = write_node(nodes[i], NULL, sce, NULL, true); - delete child_objects; - } - return true; -} - -/** - * When this method is called, the writer must write the geometry. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeGeometry(const COLLADAFW::Geometry *geom) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - return mesh_importer.write_geometry(geom); -} - -/** - * When this method is called, the writer must write the material. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - Main *bmain = CTX_data_main(mContext); - const std::string &str_mat_id = cmat->getName().size() ? cmat->getName() : cmat->getOriginalId(); - Material *ma = BKE_material_add(bmain, (char *)str_mat_id.c_str()); - - this->uid_effect_map[cmat->getInstantiatedEffect()] = ma; - this->uid_material_map[cmat->getUniqueId()] = ma; - - return true; -} - -void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Material *ma) -{ - MaterialNode matNode = MaterialNode(mContext, ef, ma, uid_image_map); - - /* Direct mapping to principled BSDF Shader */ - matNode.set_diffuse(ef->getDiffuse()); - matNode.set_emission(ef->getEmission()); - matNode.set_ior(ef->getIndexOfRefraction()); - matNode.set_alpha(ef->getOpaqueMode(), ef->getTransparent(), ef->getTransparency()); - - /* following mapping still needs to be verified */ -#if 0 - // needs rework to be done for 2.81 - matNode.set_shininess(ef->getShininess()); -#endif - matNode.set_reflectivity(ef->getReflectivity()); - - /* not supported by principled BSDF */ - matNode.set_ambient(ef->getAmbient()); - matNode.set_specular(ef->getSpecular()); - matNode.set_reflective(ef->getReflective()); -} - -/** - * When this method is called, the writer must write the effect. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeEffect(const COLLADAFW::Effect *effect) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - const COLLADAFW::UniqueId &uid = effect->getUniqueId(); - - if (uid_effect_map.find(uid) == uid_effect_map.end()) { - fprintf(stderr, "Couldn't find a material by UID.\n"); - return true; - } - - Material *ma = uid_effect_map[uid]; - std::map::iterator iter; - for (iter = uid_material_map.begin(); iter != uid_material_map.end(); iter++) { - if (iter->second == ma) { - this->FW_object_map[iter->first] = effect; - break; - } - } - COLLADAFW::CommonEffectPointerArray common_efs = effect->getCommonEffects(); - if (common_efs.getCount() < 1) { - fprintf(stderr, "Couldn't find .\n"); - return true; - } - /* XXX TODO: Take all s - * Currently only first is supported */ - COLLADAFW::EffectCommon *ef = common_efs[0]; - write_profile_COMMON(ef, ma); - this->FW_object_map[effect->getUniqueId()] = effect; - - return true; -} - -/** - * When this method is called, the writer must write the camera. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - Main *bmain = CTX_data_main(mContext); - Camera *cam = NULL; - std::string cam_id, cam_name; - - ExtraTags *et = getExtraTags(camera->getUniqueId()); - cam_id = camera->getOriginalId(); - cam_name = camera->getName(); - if (cam_name.size()) { - cam = (Camera *)BKE_camera_add(bmain, (char *)cam_name.c_str()); - } - else { - cam = (Camera *)BKE_camera_add(bmain, (char *)cam_id.c_str()); - } - - if (!cam) { - fprintf(stderr, "Cannot create camera.\n"); - return true; - } - - if (et && et->isProfile("blender")) { - et->setData("shiftx", &(cam->shiftx)); - et->setData("shifty", &(cam->shifty)); - et->setData("dof_distance", &(cam->dof.focus_distance)); - } - cam->clip_start = camera->getNearClippingPlane().getValue(); - cam->clip_end = camera->getFarClippingPlane().getValue(); - - COLLADAFW::Camera::CameraType type = camera->getCameraType(); - switch (type) { - case COLLADAFW::Camera::ORTHOGRAPHIC: { - cam->type = CAM_ORTHO; - } break; - case COLLADAFW::Camera::PERSPECTIVE: { - cam->type = CAM_PERSP; - } break; - case COLLADAFW::Camera::UNDEFINED_CAMERATYPE: { - fprintf(stderr, "Current camera type is not supported.\n"); - cam->type = CAM_PERSP; - } break; - } - - switch (camera->getDescriptionType()) { - case COLLADAFW::Camera::ASPECTRATIO_AND_Y: { - switch (cam->type) { - case CAM_ORTHO: { - double ymag = 2 * camera->getYMag().getValue(); - double aspect = camera->getAspectRatio().getValue(); - double xmag = aspect * ymag; - cam->ortho_scale = (float)xmag; - } break; - case CAM_PERSP: - default: { - double yfov = camera->getYFov().getValue(); - double aspect = camera->getAspectRatio().getValue(); - - /* NOTE: Needs more testing (As we currently have no official test data for this) */ - - double xfov = 2.0f * atanf(aspect * tanf(DEG2RADF(yfov) * 0.5f)); - cam->lens = fov_to_focallength(xfov, cam->sensor_x); - } break; - } - } break; - /* XXX correct way to do following four is probably to get also render - * size and determine proper settings from that somehow */ - case COLLADAFW::Camera::ASPECTRATIO_AND_X: - case COLLADAFW::Camera::SINGLE_X: - case COLLADAFW::Camera::X_AND_Y: { - switch (cam->type) { - case CAM_ORTHO: - cam->ortho_scale = (float)camera->getXMag().getValue() * 2; - break; - case CAM_PERSP: - default: { - double x = camera->getXFov().getValue(); - /* x is in degrees, cam->lens is in millimiters */ - cam->lens = fov_to_focallength(DEG2RADF(x), cam->sensor_x); - } break; - } - } break; - case COLLADAFW::Camera::SINGLE_Y: { - switch (cam->type) { - case CAM_ORTHO: - cam->ortho_scale = (float)camera->getYMag().getValue(); - break; - case CAM_PERSP: - default: { - double yfov = camera->getYFov().getValue(); - /* yfov is in degrees, cam->lens is in millimiters */ - cam->lens = fov_to_focallength(DEG2RADF(yfov), cam->sensor_x); - } break; - } - } break; - case COLLADAFW::Camera::UNDEFINED: - /* read nothing, use blender defaults. */ - break; - } - - this->uid_camera_map[camera->getUniqueId()] = cam; - this->FW_object_map[camera->getUniqueId()] = camera; - /* XXX import camera options */ - return true; -} - -/** - * When this method is called, the writer must write the image. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeImage(const COLLADAFW::Image *image) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - const std::string &imagepath = image->getImageURI().toNativePath(); - - char dir[FILE_MAX]; - char absolute_path[FILE_MAX]; - const char *workpath; - - BLI_split_dir_part(this->import_settings->filepath, dir, sizeof(dir)); - BLI_join_dirfile(absolute_path, sizeof(absolute_path), dir, imagepath.c_str()); - if (BLI_exists(absolute_path)) { - workpath = absolute_path; - } - else { - /* Maybe imagepath was already absolute ? */ - if (!BLI_exists(imagepath.c_str())) { - fprintf(stderr, "|! Image not found: %s\n", imagepath.c_str()); - return true; - } - workpath = imagepath.c_str(); - } - - Image *ima = BKE_image_load_exists(CTX_data_main(mContext), workpath); - if (!ima) { - fprintf(stderr, "|! Cannot create image: %s\n", workpath); - return true; - } - this->uid_image_map[image->getUniqueId()] = ima; - fprintf(stderr, "| import Image: %s\n", workpath); - return true; -} - -/** - * When this method is called, the writer must write the light. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeLight(const COLLADAFW::Light *light) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - Main *bmain = CTX_data_main(mContext); - Light *lamp = NULL; - std::string la_id, la_name; - - ExtraTags *et = getExtraTags(light->getUniqueId()); -#if 0 - TagsMap::iterator etit; - ExtraTags *et = 0; - etit = uid_tags_map.find(light->getUniqueId().toAscii()); - if (etit != uid_tags_map.end()) { - et = etit->second; - } -#endif - - la_id = light->getOriginalId(); - la_name = light->getName(); - if (la_name.size()) { - lamp = (Light *)BKE_light_add(bmain, (char *)la_name.c_str()); - } - else { - lamp = (Light *)BKE_light_add(bmain, (char *)la_id.c_str()); - } - - if (!lamp) { - fprintf(stderr, "Cannot create light.\n"); - return true; - } - - /* if we find an ExtraTags for this, use that instead. */ - if (et && et->isProfile("blender")) { - et->setData("type", &(lamp->type)); - et->setData("flag", &(lamp->flag)); - et->setData("mode", &(lamp->mode)); - et->setData("gamma", &(lamp->k)); - et->setData("red", &(lamp->r)); - et->setData("green", &(lamp->g)); - et->setData("blue", &(lamp->b)); - et->setData("shadow_r", &(lamp->shdwr)); - et->setData("shadow_g", &(lamp->shdwg)); - et->setData("shadow_b", &(lamp->shdwb)); - et->setData("energy", &(lamp->energy)); - et->setData("dist", &(lamp->dist)); - et->setData("spotsize", &(lamp->spotsize)); - lamp->spotsize = DEG2RADF(lamp->spotsize); - et->setData("spotblend", &(lamp->spotblend)); - et->setData("att1", &(lamp->att1)); - et->setData("att2", &(lamp->att2)); - et->setData("falloff_type", &(lamp->falloff_type)); - et->setData("clipsta", &(lamp->clipsta)); - et->setData("clipend", &(lamp->clipend)); - et->setData("bias", &(lamp->bias)); - et->setData("soft", &(lamp->soft)); - et->setData("bufsize", &(lamp->bufsize)); - et->setData("buffers", &(lamp->buffers)); - et->setData("area_shape", &(lamp->area_shape)); - et->setData("area_size", &(lamp->area_size)); - et->setData("area_sizey", &(lamp->area_sizey)); - et->setData("area_sizez", &(lamp->area_sizez)); - } - else { - float constatt = light->getConstantAttenuation().getValue(); - float linatt = light->getLinearAttenuation().getValue(); - float quadatt = light->getQuadraticAttenuation().getValue(); - float d = 25.0f; - float att1 = 0.0f; - float att2 = 0.0f; - float e = 1.0f; - - if (light->getColor().isValid()) { - COLLADAFW::Color col = light->getColor(); - lamp->r = col.getRed(); - lamp->g = col.getGreen(); - lamp->b = col.getBlue(); - } - - if (IS_EQ(linatt, 0.0f) && quadatt > 0.0f) { - att2 = quadatt; - d = sqrt(1.0f / quadatt); - } - /* linear light */ - else if (IS_EQ(quadatt, 0.0f) && linatt > 0.0f) { - att1 = linatt; - d = (1.0f / linatt); - } - else if (IS_EQ(constatt, 1.0f)) { - att1 = 1.0f; - } - else { - /* assuming point light (const att = 1.0); */ - att1 = 1.0f; - } - - d *= (1.0f / unit_converter.getLinearMeter()); - - lamp->energy = e; - lamp->dist = d; - - switch (light->getLightType()) { - case COLLADAFW::Light::AMBIENT_LIGHT: { - lamp->type = LA_SUN; // TODO needs more thoughts - } break; - case COLLADAFW::Light::SPOT_LIGHT: { - lamp->type = LA_SPOT; - lamp->att1 = att1; - lamp->att2 = att2; - if (IS_EQ(att1, 0.0f) && att2 > 0) { - lamp->falloff_type = LA_FALLOFF_INVSQUARE; - } - if (IS_EQ(att2, 0.0f) && att1 > 0) { - lamp->falloff_type = LA_FALLOFF_INVLINEAR; - } - lamp->spotsize = DEG2RADF(light->getFallOffAngle().getValue()); - lamp->spotblend = light->getFallOffExponent().getValue(); - } break; - case COLLADAFW::Light::DIRECTIONAL_LIGHT: { - /* our sun is very strong, so pick a smaller energy level */ - lamp->type = LA_SUN; - } break; - case COLLADAFW::Light::POINT_LIGHT: { - lamp->type = LA_LOCAL; - lamp->att1 = att1; - lamp->att2 = att2; - if (IS_EQ(att1, 0.0f) && att2 > 0) { - lamp->falloff_type = LA_FALLOFF_INVSQUARE; - } - if (IS_EQ(att2, 0.0f) && att1 > 0) { - lamp->falloff_type = LA_FALLOFF_INVLINEAR; - } - } break; - case COLLADAFW::Light::UNDEFINED: { - fprintf(stderr, "Current light type is not supported.\n"); - lamp->type = LA_LOCAL; - } break; - } - } - - this->uid_light_map[light->getUniqueId()] = lamp; - this->FW_object_map[light->getUniqueId()] = light; - return true; -} - -/* this function is called only for animations that pass COLLADAFW::validate */ -bool DocumentImporter::writeAnimation(const COLLADAFW::Animation *anim) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - return anim_importer.write_animation(anim); -} - -/* called on post-process stage after writeVisualScenes */ -bool DocumentImporter::writeAnimationList(const COLLADAFW::AnimationList *animationList) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - /* return true; */ - return anim_importer.write_animation_list(animationList); -} - -#if WITH_OPENCOLLADA_ANIMATION_CLIP -/* Since opencollada 1.6.68 - * called on post-process stage after writeVisualScenes */ -bool DocumentImporter::writeAnimationClip(const COLLADAFW::AnimationClip *animationClip) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - return true; - /* TODO: implement import of AnimationClips */ - // return animation_clip_importer.write_animation_clip(animationClip); -} -#endif - -/** - * When this method is called, the writer must write the skin controller data. - * \return The writer should return true, if writing succeeded, false otherwise. - */ -bool DocumentImporter::writeSkinControllerData(const COLLADAFW::SkinControllerData *skin) -{ - return armature_importer.write_skin_controller_data(skin); -} - -/* this is called on postprocess, before writeVisualScenes */ -bool DocumentImporter::writeController(const COLLADAFW::Controller *controller) -{ - if (mImportStage == Fetching_Controller_data) { - return true; - } - - return armature_importer.write_controller(controller); -} - -bool DocumentImporter::writeFormulas(const COLLADAFW::Formulas *formulas) -{ - return true; -} - -bool DocumentImporter::writeKinematicsScene(const COLLADAFW::KinematicsScene *kinematicsScene) -{ - return true; -} - -ExtraTags *DocumentImporter::getExtraTags(const COLLADAFW::UniqueId &uid) -{ - if (uid_tags_map.find(uid.toAscii()) == uid_tags_map.end()) { - return NULL; - } - return uid_tags_map[uid.toAscii()]; -} - -bool DocumentImporter::addExtraTags(const COLLADAFW::UniqueId &uid, ExtraTags *extra_tags) -{ - uid_tags_map[uid.toAscii()] = extra_tags; - return true; -} - -bool DocumentImporter::is_armature(COLLADAFW::Node *node) -{ - COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes(); - for (unsigned int i = 0; i < child_nodes.getCount(); i++) { - if (child_nodes[i]->getType() == COLLADAFW::Node::JOINT) { - return true; - } - else { - continue; - } - } - - /* no child is JOINT */ - return false; -} diff --git a/source/blender/collada/DocumentImporter.h b/source/blender/collada/DocumentImporter.h deleted file mode 100644 index e47c844f7c6..00000000000 --- a/source/blender/collada/DocumentImporter.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __DOCUMENTIMPORTER_H__ -#define __DOCUMENTIMPORTER_H__ - -#include "COLLADAFWIWriter.h" -#include "COLLADAFWMaterial.h" -#include "COLLADAFWEffect.h" -#include "COLLADAFWColor.h" -#include "COLLADAFWImage.h" -#include "COLLADAFWInstanceGeometry.h" -#include "COLLADAFWController.h" -#include "COLLADAFWMorphController.h" -#include "COLLADAFWSkinController.h" -#include "COLLADAFWEffectCommon.h" - -#include "BKE_object.h" -#include "BKE_constraint.h" - -#include "TransformReader.h" -#include "AnimationImporter.h" -#include "ArmatureImporter.h" -#include "ControllerExporter.h" -#include "MeshImporter.h" -#include "ImportSettings.h" - -struct bContext; - -/** Importer class. */ -class DocumentImporter : COLLADAFW::IWriter { - public: - //! Enumeration to denote the stage of import - enum ImportStage { - Fetching_Scene_data, /* First pass to collect all data except controller */ - Fetching_Controller_data, /* Second pass to collect controller data */ - }; - /** Constructor */ - DocumentImporter(bContext *C, const ImportSettings *import_settings); - - /** Destructor */ - ~DocumentImporter(); - - /** Function called by blender UI */ - bool import(); - - /** these should not be here */ - Object *create_camera_object(COLLADAFW::InstanceCamera *, Scene *); - Object *create_light_object(COLLADAFW::InstanceLight *, Scene *); - Object *create_instance_node(Object *, COLLADAFW::Node *, COLLADAFW::Node *, Scene *, bool); - void create_constraints(ExtraTags *et, Object *ob); - std::vector *write_node(COLLADAFW::Node *, COLLADAFW::Node *, Scene *, Object *, bool); - void write_profile_COMMON(COLLADAFW::EffectCommon *, Material *); - - void translate_anim_recursive(COLLADAFW::Node *, COLLADAFW::Node *, Object *); - - /** - * This method will be called if an error in the loading process occurred and the loader cannot - * continue to load. The writer should undo all operations that have been performed. - * \param errorMessage: A message containing information about the error that occurred. - */ - void cancel(const COLLADAFW::String &errorMessage); - - /** This is the method called. The writer hast to prepare to receive data.*/ - void start(); - - /** This method is called after the last write* method. No other methods will be called after - * this.*/ - void finish(); - - bool writeGlobalAsset(const COLLADAFW::FileInfo *); - std::string get_import_version(const COLLADAFW::FileInfo *asset); - - bool writeScene(const COLLADAFW::Scene *); - - bool writeVisualScene(const COLLADAFW::VisualScene *); - - bool writeLibraryNodes(const COLLADAFW::LibraryNodes *); - - bool writeAnimation(const COLLADAFW::Animation *); - - bool writeAnimationList(const COLLADAFW::AnimationList *); - -#if WITH_OPENCOLLADA_ANIMATION_CLIP - // Please enable this when building with Collada 1.6.65 or newer (also in DocumentImporter.cpp) - bool writeAnimationClip(const COLLADAFW::AnimationClip *animationClip); -#endif - - bool writeGeometry(const COLLADAFW::Geometry *); - - bool writeMaterial(const COLLADAFW::Material *); - - bool writeEffect(const COLLADAFW::Effect *); - - bool writeCamera(const COLLADAFW::Camera *); - - bool writeImage(const COLLADAFW::Image *); - - bool writeLight(const COLLADAFW::Light *); - - bool writeSkinControllerData(const COLLADAFW::SkinControllerData *); - - bool writeController(const COLLADAFW::Controller *); - - bool writeFormulas(const COLLADAFW::Formulas *); - - bool writeKinematicsScene(const COLLADAFW::KinematicsScene *); - - /** Add element and data for UniqueId */ - bool addExtraTags(const COLLADAFW::UniqueId &uid, ExtraTags *extra_tags); - /** Get an extisting ExtraTags for uid */ - ExtraTags *getExtraTags(const COLLADAFW::UniqueId &uid); - - bool is_armature(COLLADAFW::Node *node); - - private: - const ImportSettings *import_settings; - - /** Current import stage we're in. */ - ImportStage mImportStage; - - bContext *mContext; - ViewLayer *view_layer; - - UnitConverter unit_converter; - ArmatureImporter armature_importer; - MeshImporter mesh_importer; - AnimationImporter anim_importer; - - /** TagsMap typedef for uid_tags_map. */ - typedef std::map TagsMap; - /** Tags map of unique id as a string and ExtraTags instance. */ - TagsMap uid_tags_map; - - UidImageMap uid_image_map; - std::map uid_material_map; - std::map uid_effect_map; - std::map uid_camera_map; - std::map uid_light_map; - std::map material_texture_mapping_map; - std::multimap object_map; - std::map node_map; - std::vector vscenes; - std::vector libnode_ob; - - std::map - root_map; // find root joint by child joint uid, for bone tree evaluation during resampling - std::map FW_object_map; - - std::string import_from_version; - - void report_unknown_reference(const COLLADAFW::Node &node, const std::string object_type); -}; - -#endif diff --git a/source/blender/collada/EffectExporter.cpp b/source/blender/collada/EffectExporter.cpp deleted file mode 100644 index a1174fdff56..00000000000 --- a/source/blender/collada/EffectExporter.cpp +++ /dev/null @@ -1,312 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include - -#include "COLLADASWEffectProfile.h" -#include "COLLADAFWColorOrTexture.h" - -#include "EffectExporter.h" -#include "DocumentExporter.h" -#include "MaterialExporter.h" - -#include "collada_internal.h" -#include "collada_utils.h" - -extern "C" { -#include "DNA_mesh_types.h" -#include "DNA_world_types.h" - -#include "BKE_collection.h" -#include "BKE_customdata.h" -#include "BKE_mesh.h" -#include "BKE_material.h" -} - -static std::string getActiveUVLayerName(Object *ob) -{ - Mesh *me = (Mesh *)ob->data; - - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - if (num_layers) { - return std::string(bc_CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV)); - } - - return ""; -} - -EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings, - KeyImageMap &key_image_map) - : COLLADASW::LibraryEffects(sw), export_settings(export_settings), key_image_map(key_image_map) -{ -} - -bool EffectsExporter::hasEffects(Scene *sce) -{ - FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { - int a; - for (a = 0; a < ob->totcol; a++) { - Material *ma = BKE_object_material_get(ob, a + 1); - - // no material, but check all of the slots - if (!ma) { - continue; - } - - return true; - } - } - FOREACH_SCENE_OBJECT_END; - return false; -} - -void EffectsExporter::exportEffects(bContext *C, Scene *sce) -{ - if (hasEffects(sce)) { - this->mContext = C; - this->scene = sce; - openLibrary(); - MaterialFunctor mf; - mf.forEachMaterialInExportSet( - sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); - } -} - -void EffectsExporter::set_shader_type(COLLADASW::EffectProfile &ep, Material *ma) -{ - /* XXX check if BLINN and PHONG can be supported as well */ - ep.setShaderType(COLLADASW::EffectProfile::LAMBERT); -} - -void EffectsExporter::set_transparency(COLLADASW::EffectProfile &ep, Material *ma) -{ - double alpha = bc_get_alpha(ma); - if (alpha < 1) { - // workaround use to avoid wrong handling of by other tools - COLLADASW::ColorOrTexture cot = bc_get_cot(0, 0, 0, alpha); - ep.setTransparent(cot, false, "alpha"); - ep.setOpaque(COLLADASW::EffectProfile::A_ONE); - } -} - -void EffectsExporter::set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma) -{ - COLLADASW::ColorOrTexture cot = bc_get_base_color(ma); - ep.setDiffuse(cot, false, "diffuse"); -} - -void EffectsExporter::set_ambient(COLLADASW::EffectProfile &ep, Material *ma) -{ - COLLADASW::ColorOrTexture cot = bc_get_ambient(ma); - ep.setAmbient(cot, false, "ambient"); -} -void EffectsExporter::set_specular(COLLADASW::EffectProfile &ep, Material *ma) -{ - COLLADASW::ColorOrTexture cot = bc_get_specular(ma); - ep.setSpecular(cot, false, "specular"); -} -void EffectsExporter::set_reflective(COLLADASW::EffectProfile &ep, Material *ma) -{ - COLLADASW::ColorOrTexture cot = bc_get_reflective(ma); - ep.setReflective(cot, false, "reflective"); -} - -void EffectsExporter::set_reflectivity(COLLADASW::EffectProfile &ep, Material *ma) -{ - double reflectivity = bc_get_reflectivity(ma); - if (reflectivity > 0.0) { - ep.setReflectivity(reflectivity, false, "specular"); - } -} - -void EffectsExporter::set_emission(COLLADASW::EffectProfile &ep, Material *ma) -{ - COLLADASW::ColorOrTexture cot = bc_get_emission(ma); - ep.setEmission(cot, false, "emission"); -} - -void EffectsExporter::set_ior(COLLADASW::EffectProfile &ep, Material *ma) -{ - double alpha = bc_get_ior(ma); - ep.setIndexOfRefraction(alpha, false, "ior"); -} - -void EffectsExporter::set_shininess(COLLADASW::EffectProfile &ep, Material *ma) -{ - double shininess = bc_get_shininess(ma); - ep.setShininess(shininess, false, "shininess"); -} - -void EffectsExporter::get_images(Material *ma, KeyImageMap &material_image_map) -{ - if (!ma->use_nodes) { - return; - } - - MaterialNode material = MaterialNode(mContext, ma, key_image_map); - Image *image = material.get_diffuse_image(); - if (image == nullptr) { - return; - } - - std::string uid(id_name(image)); - std::string key = translate_id(uid); - - if (material_image_map.find(key) == material_image_map.end()) { - material_image_map[key] = image; - key_image_map[key] = image; - } -} - -void EffectsExporter::create_image_samplers(COLLADASW::EffectProfile &ep, - KeyImageMap &material_image_map, - std::string &active_uv) -{ - KeyImageMap::iterator iter; - - for (iter = material_image_map.begin(); iter != material_image_map.end(); iter++) { - - Image *image = iter->second; - std::string uid(id_name(image)); - std::string key = translate_id(uid); - - COLLADASW::Sampler *sampler = new COLLADASW::Sampler( - COLLADASW::Sampler::SAMPLER_TYPE_2D, - key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX, - key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); - - sampler->setImageId(key); - - ep.setDiffuse(createTexture(image, active_uv, sampler), false, "diffuse"); - } -} - -void EffectsExporter::operator()(Material *ma, Object *ob) -{ - KeyImageMap material_image_map; - - openEffect(get_effect_id(ma)); - - COLLADASW::EffectProfile ep(mSW); - ep.setProfileType(COLLADASW::EffectProfile::COMMON); - ep.openProfile(); - set_shader_type(ep, ma); // creates a Lambert Shader for now - - COLLADASW::ColorOrTexture cot; - - set_diffuse_color(ep, ma); - set_emission(ep, ma); - set_ior(ep, ma); - set_reflectivity(ep, ma); - set_transparency(ep, ma); - - /* TODO: */ - // set_shininess(ep, ma); shininess not supported for lambert - // set_ambient(ep, ma); - // set_specular(ep, ma); - - get_images(ma, material_image_map); - std::string active_uv(getActiveUVLayerName(ob)); - create_image_samplers(ep, material_image_map, active_uv); - -#if 0 - unsigned int a, b; - for (a = 0, b = 0; a < tex_indices.size(); a++) { - MTex *t = ma->mtex[tex_indices[a]]; - Image *ima = t->tex->ima; - - // Image not set for texture - if (!ima) { - continue; - } - - std::string key(id_name(ima)); - key = translate_id(key); - - // create only one / pair for each unique image - if (im_samp_map.find(key) == im_samp_map.end()) { - // - COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D, - key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX, - key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); - sampler.setImageId(key); - // copy values to arrays since they will live longer - samplers[a] = sampler; - - // store pointers so they can be used later when we create s - samp_surf[b] = &samplers[a]; - //samp_surf[b][1] = &surfaces[a]; - - im_samp_map[key] = b; - b++; - } - } - - for (a = 0; a < tex_indices.size(); a++) { - MTex *t = ma->mtex[tex_indices[a]]; - Image *ima = t->tex->ima; - - if (!ima) { - continue; - } - - std::string key(id_name(ima)); - key = translate_id(key); - int i = im_samp_map[key]; - std::string uvname = strlen(t->uvname) ? t->uvname : active_uv; - COLLADASW::Sampler *sampler = (COLLADASW::Sampler *) - samp_surf[i]; // possibly uninitialized memory ... - writeTextures(ep, key, sampler, t, ima, uvname); - } -#endif - - // performs the actual writing - ep.addProfileElements(); - ep.addExtraTechniques(mSW); - - ep.closeProfile(); - closeEffect(); -} - -COLLADASW::ColorOrTexture EffectsExporter::createTexture(Image *ima, - std::string &uv_layer_name, - COLLADASW::Sampler *sampler - /*COLLADASW::Surface *surface*/) -{ - - COLLADASW::Texture texture(translate_id(id_name(ima))); - texture.setTexcoord(uv_layer_name); - // texture.setSurface(*surface); - texture.setSampler(*sampler); - - COLLADASW::ColorOrTexture cot(texture); - return cot; -} - -COLLADASW::ColorOrTexture EffectsExporter::getcol(float r, float g, float b, float a) -{ - COLLADASW::Color color(r, g, b, a); - COLLADASW::ColorOrTexture cot(color); - return cot; -} diff --git a/source/blender/collada/EffectExporter.h b/source/blender/collada/EffectExporter.h deleted file mode 100644 index 57df844233c..00000000000 --- a/source/blender/collada/EffectExporter.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __EFFECTEXPORTER_H__ -#define __EFFECTEXPORTER_H__ - -#include -#include - -#include "COLLADASWColorOrTexture.h" -#include "COLLADASWStreamWriter.h" -#include "COLLADASWSampler.h" -#include "COLLADASWLibraryEffects.h" - -#include "DNA_image_types.h" -#include "DNA_material_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "ExportSettings.h" -#include "collada_utils.h" - -class EffectsExporter : COLLADASW::LibraryEffects { - public: - EffectsExporter(COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings, - KeyImageMap &key_image_map); - void exportEffects(bContext *C, Scene *sce); - - void operator()(Material *ma, Object *ob); - - COLLADASW::ColorOrTexture createTexture(Image *ima, - std::string &uv_layer_name, - COLLADASW::Sampler *sampler - /*COLLADASW::Surface *surface*/); - - COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a); - - private: - void set_shader_type(COLLADASW::EffectProfile &ep, Material *ma); - - void set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma); - void set_emission(COLLADASW::EffectProfile &ep, Material *ma); - void set_ior(COLLADASW::EffectProfile &ep, Material *ma); - void set_shininess(COLLADASW::EffectProfile &ep, Material *ma); - void set_reflectivity(COLLADASW::EffectProfile &ep, Material *ma); - void set_transparency(COLLADASW::EffectProfile &ep, Material *ma); - void set_ambient(COLLADASW::EffectProfile &ep, Material *ma); - void set_specular(COLLADASW::EffectProfile &ep, Material *ma); - void set_reflective(COLLADASW::EffectProfile &ep, Material *ma); - - void get_images(Material *ma, KeyImageMap &uid_image_map); - void create_image_samplers(COLLADASW::EffectProfile &ep, - KeyImageMap &uid_image_map, - std::string &active_uv); - - void writeTextures(COLLADASW::EffectProfile &ep, - std::string &key, - COLLADASW::Sampler *sampler, - MTex *t, - Image *ima, - std::string &uvname); - - bool hasEffects(Scene *sce); - - BCExportSettings &export_settings; - KeyImageMap &key_image_map; - Scene *scene; - bContext *mContext; -}; - -#endif diff --git a/source/blender/collada/ErrorHandler.cpp b/source/blender/collada/ErrorHandler.cpp deleted file mode 100644 index 286bcbfb759..00000000000 --- a/source/blender/collada/ErrorHandler.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ -#include "ErrorHandler.h" -#include - -#include "COLLADASaxFWLIError.h" -#include "COLLADASaxFWLSaxParserError.h" -#include "COLLADASaxFWLSaxFWLError.h" - -#include "GeneratedSaxParserParserError.h" - -#include - -#include "BLI_utildefines.h" - -//-------------------------------------------------------------------- -ErrorHandler::ErrorHandler() : mError(false) -{ -} - -//-------------------------------------------------------------------- -ErrorHandler::~ErrorHandler() -{ -} - -//-------------------------------------------------------------------- -bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error) -{ - /* This method must return false when Collada should continue. - * See https://github.com/KhronosGroup/OpenCOLLADA/issues/442 - */ - bool isError = true; - std::string error_context; - std::string error_message; - - if (error->getErrorClass() == COLLADASaxFWL::IError::ERROR_SAXPARSER) { - error_context = "Schema validation"; - - COLLADASaxFWL::SaxParserError *saxParserError = (COLLADASaxFWL::SaxParserError *)error; - const GeneratedSaxParser::ParserError &parserError = saxParserError->getError(); - error_message = parserError.getErrorMessage(); - - if (parserError.getErrorType() == - GeneratedSaxParser::ParserError::ERROR_VALIDATION_MIN_OCCURS_UNMATCHED) { - if (STREQ(parserError.getElement(), "effect")) { - isError = false; - } - } - - else if (parserError.getErrorType() == - GeneratedSaxParser::ParserError:: - ERROR_VALIDATION_SEQUENCE_PREVIOUS_SIBLING_NOT_PRESENT) { - if (!(STREQ(parserError.getElement(), "extra") && - STREQ(parserError.getAdditionalText().c_str(), "sibling: fx_profile_abstract"))) { - isError = false; - } - } - - else if (parserError.getErrorType() == - GeneratedSaxParser::ParserError::ERROR_COULD_NOT_OPEN_FILE) { - isError = true; - error_context = "File access"; - } - - else if (parserError.getErrorType() == - GeneratedSaxParser::ParserError::ERROR_REQUIRED_ATTRIBUTE_MISSING) { - isError = true; - } - - else { - isError = (parserError.getSeverity() != - GeneratedSaxParser::ParserError::Severity::SEVERITY_ERROR_NONCRITICAL); - } - } - else if (error->getErrorClass() == COLLADASaxFWL::IError::ERROR_SAXFWL) { - error_context = "Sax FWL"; - COLLADASaxFWL::SaxFWLError *saxFWLError = (COLLADASaxFWL::SaxFWLError *)error; - error_message = saxFWLError->getErrorMessage(); - - /* - * Accept non critical errors as warnings (i.e. texture not found) - * This makes the importer more graceful, so it now imports what makes sense. - */ - - isError = (saxFWLError->getSeverity() != COLLADASaxFWL::IError::SEVERITY_ERROR_NONCRITICAL); - } - else { - error_context = "OpenCollada"; - error_message = error->getFullErrorMessage(); - isError = true; - } - - std::string severity = (isError) ? "Error" : "Warning"; - std::cout << error_context << " (" << severity << "): " << error_message << std::endl; - if (isError) { - std::cout << "The Collada import has been forced to stop." << std::endl; - std::cout << "Please fix the reported error and then try again."; - mError = true; - } - return isError; -} diff --git a/source/blender/collada/ErrorHandler.h b/source/blender/collada/ErrorHandler.h deleted file mode 100644 index f040855244d..00000000000 --- a/source/blender/collada/ErrorHandler.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __ERRORHANDLER_H__ -#define __ERRORHANDLER_H__ - -#include -#include -#include -#include // sort() - -#include "COLLADASaxFWLIErrorHandler.h" - -/** \brief Handler class for parser errors - */ -class ErrorHandler : public COLLADASaxFWL::IErrorHandler { - public: - /** Constructor. */ - ErrorHandler(); - - /** Destructor. */ - virtual ~ErrorHandler(); - /** handle any error thrown by the parser. */ - bool virtual handleError(const COLLADASaxFWL::IError *error); - /** True if there was an error during parsing. */ - bool hasError() - { - return mError; - } - - private: - /** Disable default copy ctor. */ - ErrorHandler(const ErrorHandler &pre); - /** Disable default assignment operator. */ - const ErrorHandler &operator=(const ErrorHandler &pre); - /** Hold error status. */ - bool mError; -}; - -#endif /* __ERRORHANDLER_H__ */ diff --git a/source/blender/collada/ExportSettings.cpp b/source/blender/collada/ExportSettings.cpp deleted file mode 100644 index da3c0de0fdf..00000000000 --- a/source/blender/collada/ExportSettings.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "ExportSettings.h" diff --git a/source/blender/collada/ExportSettings.h b/source/blender/collada/ExportSettings.h deleted file mode 100644 index 1e158418120..00000000000 --- a/source/blender/collada/ExportSettings.h +++ /dev/null @@ -1,295 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __EXPORTSETTINGS_H__ -#define __EXPORTSETTINGS_H__ - -#ifdef __cplusplus -# include -# include "BCMath.h" - -extern "C" { -#endif - -#include "BLI_linklist.h" -#include "BlenderContext.h" - -typedef enum BC_export_mesh_type { - BC_MESH_TYPE_VIEW, - BC_MESH_TYPE_RENDER, -} BC_export_mesh_type; - -typedef enum BC_export_transformation_type { - BC_TRANSFORMATION_TYPE_MATRIX, - BC_TRANSFORMATION_TYPE_DECOMPOSED, -} BC_export_transformation_type; - -typedef enum BC_export_animation_type { - BC_ANIMATION_EXPORT_SAMPLES, - BC_ANIMATION_EXPORT_KEYS, -} BC_export_animation_type; - -typedef enum BC_ui_export_section { - BC_UI_SECTION_MAIN, - BC_UI_SECTION_GEOMETRY, - BC_UI_SECTION_ARMATURE, - BC_UI_SECTION_ANIMATION, - BC_UI_SECTION_COLLADA, -} BC_ui_export_section; - -typedef struct ExportSettings { - bool apply_modifiers; - BC_global_forward_axis global_forward; - BC_global_up_axis global_up; - bool apply_global_orientation; - - BC_export_mesh_type export_mesh_type; - - bool selected; - bool include_children; - bool include_armatures; - bool include_shapekeys; - bool deform_bones_only; - bool include_animations; - bool include_all_actions; - int sampling_rate; - bool keep_smooth_curves; - bool keep_keyframes; - bool keep_flat_curves; - - bool active_uv_only; - BC_export_animation_type export_animation_type; - bool use_texture_copies; - - bool triangulate; - bool use_object_instantiation; - bool use_blender_profile; - bool sort_by_name; - BC_export_transformation_type object_transformation_type; - BC_export_transformation_type animation_transformation_type; - - bool open_sim; - bool limit_precision; - bool keep_bind_info; - - char *filepath; - LinkNode *export_set; -} ExportSettings; - -#ifdef __cplusplus -} - -void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer); - -class BCExportSettings { - - private: - const ExportSettings &export_settings; - BlenderContext &blender_context; - const BCMatrix global_transform; - - public: - BCExportSettings(ExportSettings *exportSettings, BlenderContext &blenderContext) - : export_settings(*exportSettings), - blender_context(blenderContext), - global_transform(BCMatrix(exportSettings->global_forward, exportSettings->global_up)) - - { - } - - const BCMatrix &get_global_transform() - { - return global_transform; - } - - bool get_apply_modifiers() - { - return export_settings.apply_modifiers; - } - - BC_global_forward_axis get_global_forward() - { - return export_settings.global_forward; - } - - BC_global_up_axis get_global_up() - { - return export_settings.global_up; - } - - bool get_apply_global_orientation() - { - return export_settings.apply_global_orientation; - } - - BC_export_mesh_type get_export_mesh_type() - { - return export_settings.export_mesh_type; - } - - bool get_selected() - { - return export_settings.selected; - } - - bool get_include_children() - { - return export_settings.include_children; - } - - bool get_include_armatures() - { - return export_settings.include_armatures; - } - - bool get_include_shapekeys() - { - return export_settings.include_shapekeys; - } - - bool get_deform_bones_only() - { - return export_settings.deform_bones_only; - } - - bool get_include_animations() - { - return export_settings.include_animations; - } - - bool get_include_all_actions() - { - return export_settings.include_all_actions; - } - - int get_sampling_rate() - { - return export_settings.sampling_rate; - } - - bool get_keep_smooth_curves() - { - return export_settings.keep_smooth_curves; - } - - bool get_keep_keyframes() - { - return export_settings.keep_keyframes; - } - - bool get_keep_flat_curves() - { - return export_settings.keep_flat_curves; - } - - bool get_active_uv_only() - { - return export_settings.active_uv_only; - } - - BC_export_animation_type get_export_animation_type() - { - return export_settings.export_animation_type; - } - - bool get_use_texture_copies() - { - return export_settings.use_texture_copies; - } - - bool get_triangulate() - { - return export_settings.triangulate; - } - - bool get_use_object_instantiation() - { - return export_settings.use_object_instantiation; - } - - bool get_use_blender_profile() - { - return export_settings.use_blender_profile; - } - - bool get_sort_by_name() - { - return export_settings.sort_by_name; - } - - BC_export_transformation_type get_object_transformation_type() - { - return export_settings.object_transformation_type; - } - - BC_export_transformation_type get_animation_transformation_type() - { - return export_settings.animation_transformation_type; - } - - bool get_open_sim() - { - return export_settings.open_sim; - } - - bool get_limit_precision() - { - return export_settings.limit_precision; - } - - bool get_keep_bind_info() - { - return export_settings.keep_bind_info; - } - - char *get_filepath() - { - return export_settings.filepath; - } - - LinkNode *get_export_set() - { - return export_settings.export_set; - } - - BlenderContext &get_blender_context() - { - return blender_context; - } - - Scene *get_scene() - { - return blender_context.get_scene(); - } - - ViewLayer *get_view_layer() - { - return blender_context.get_view_layer(); - } - - bool is_export_root(Object *ob) - { - return bc_is_base_node(get_export_set(), ob, get_view_layer()); - } -}; - -#endif - -#endif diff --git a/source/blender/collada/ExtraHandler.cpp b/source/blender/collada/ExtraHandler.cpp deleted file mode 100644 index 4875ee72b0f..00000000000 --- a/source/blender/collada/ExtraHandler.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include "BLI_string.h" - -#include "ExtraHandler.h" - -ExtraHandler::ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp) : currentExtraTags(0) -{ - this->dimp = dimp; - this->aimp = aimp; -} - -ExtraHandler::~ExtraHandler() -{ -} - -bool ExtraHandler::elementBegin(const char *elementName, const char **attributes) -{ - /* \todo attribute handling for profile tags */ - currentElement = std::string(elementName); - // addToSidTree(attributes[0], attributes[1]); - return true; -} - -bool ExtraHandler::elementEnd(const char *elementName) -{ - return true; -} - -bool ExtraHandler::textData(const char *text, size_t textLength) -{ - char buf[1024]; - - if (currentElement.length() == 0 || currentExtraTags == 0) { - return false; - } - - BLI_strncpy(buf, text, textLength + 1); - currentExtraTags->addTag(currentElement, std::string(buf)); - return true; -} - -bool ExtraHandler::parseElement(const char *profileName, - const unsigned long &elementHash, - const COLLADAFW::UniqueId &uniqueId) -{ - /* implement for backwards compatibility, new version added object parameter */ - return parseElement(profileName, elementHash, uniqueId, NULL); -} - -bool ExtraHandler::parseElement(const char *profileName, - const unsigned long &elementHash, - const COLLADAFW::UniqueId &uniqueId, - COLLADAFW::Object *object) -{ - if (BLI_strcaseeq(profileName, "blender")) { -#if 0 - printf("In parseElement for supported profile %s for id %s\n", - profileName, - uniqueId.toAscii().c_str()); -#endif - currentUid = uniqueId; - ExtraTags *et = dimp->getExtraTags(uniqueId); - if (!et) { - et = new ExtraTags(std::string(profileName)); - dimp->addExtraTags(uniqueId, et); - } - currentExtraTags = et; - return true; - } - // printf("In parseElement for unsupported profile %s for id %s\n", profileName, - // uniqueId.toAscii().c_str()); - return false; -} diff --git a/source/blender/collada/ExtraHandler.h b/source/blender/collada/ExtraHandler.h deleted file mode 100644 index 021eb8e9663..00000000000 --- a/source/blender/collada/ExtraHandler.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __EXTRAHANDLER_H__ -#define __EXTRAHANDLER_H__ - -#include -#include -#include -#include // sort() - -#include "COLLADASaxFWLIExtraDataCallbackHandler.h" -#include "COLLADASaxFWLFilePartLoader.h" -#include "COLLADASWInstanceController.h" - -#include "DocumentImporter.h" -#include "AnimationImporter.h" - -/** \brief Handler class for \ data, through which different - * profiles can be handled - */ -class ExtraHandler : public COLLADASaxFWL::IExtraDataCallbackHandler { - public: - /** Constructor. */ - ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp); - - /** Destructor. */ - virtual ~ExtraHandler(); - - /** Handle the beginning of an element. */ - bool elementBegin(const char *elementName, const char **attributes); - - /** Handle the end of an element. */ - bool elementEnd(const char *elementName); - - /** Receive the data in text format. */ - bool textData(const char *text, size_t textLength); - - /** Method to ask, if the current callback handler want to read the data of the given extra - * element. */ - bool parseElement(const char *profileName, - const unsigned long &elementHash, - const COLLADAFW::UniqueId &uniqueId, - COLLADAFW::Object *object); - - /** For backwards compatibility with older OpenCollada, new version added object parameter */ - bool parseElement(const char *profileName, - const unsigned long &elementHash, - const COLLADAFW::UniqueId &uniqueId); - - private: - /** Disable default copy constructor. */ - ExtraHandler(const ExtraHandler &pre); - /** Disable default assignment operator. */ - const ExtraHandler &operator=(const ExtraHandler &pre); - - /** Handle to DocumentImporter for interface to extra element data saving. */ - DocumentImporter *dimp; - AnimationImporter *aimp; - /** Holds Id of element for which XML elements are handled. */ - COLLADAFW::UniqueId currentUid; - ExtraTags *currentExtraTags; - std::string currentElement; -}; - -#endif /* __EXTRAHANDLER_H__ */ diff --git a/source/blender/collada/ExtraTags.cpp b/source/blender/collada/ExtraTags.cpp deleted file mode 100644 index 496ba3891f7..00000000000 --- a/source/blender/collada/ExtraTags.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include -#include "BLI_string.h" - -#include - -#include "ExtraTags.h" - -ExtraTags::ExtraTags(std::string profile) -{ - this->profile = profile; - this->tags = std::map(); -} - -ExtraTags::~ExtraTags() -{ -} - -bool ExtraTags::isProfile(std::string profile) -{ - return this->profile == profile; -} - -bool ExtraTags::addTag(std::string tag, std::string data) -{ - tags[tag] = data; - - return true; -} - -int ExtraTags::asInt(std::string tag, bool *ok) -{ - if (tags.find(tag) == tags.end()) { - *ok = false; - return -1; - } - *ok = true; - return atoi(tags[tag].c_str()); -} - -float ExtraTags::asFloat(std::string tag, bool *ok) -{ - if (tags.find(tag) == tags.end()) { - *ok = false; - return -1.0f; - } - *ok = true; - return (float)atof(tags[tag].c_str()); -} - -std::string ExtraTags::asString(std::string tag, bool *ok) -{ - if (tags.find(tag) == tags.end()) { - *ok = false; - return ""; - } - *ok = true; - return tags[tag]; -} - -bool ExtraTags::setData(std::string tag, short *data) -{ - bool ok = false; - int tmp = asInt(tag, &ok); - if (ok) { - *data = (short)tmp; - } - return ok; -} - -bool ExtraTags::setData(std::string tag, int *data) -{ - bool ok = false; - int tmp = asInt(tag, &ok); - if (ok) { - *data = tmp; - } - return ok; -} - -bool ExtraTags::setData(std::string tag, float *data) -{ - bool ok = false; - float tmp = asFloat(tag, &ok); - if (ok) { - *data = tmp; - } - return ok; -} - -bool ExtraTags::setData(std::string tag, char *data) -{ - bool ok = false; - int tmp = asInt(tag, &ok); - if (ok) { - *data = (char)tmp; - } - return ok; -} - -std::string ExtraTags::setData(std::string tag, std::string &data) -{ - bool ok = false; - std::string tmp = asString(tag, &ok); - return (ok) ? tmp : data; -} diff --git a/source/blender/collada/ExtraTags.h b/source/blender/collada/ExtraTags.h deleted file mode 100644 index 9191182c757..00000000000 --- a/source/blender/collada/ExtraTags.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __EXTRATAGS_H__ -#define __EXTRATAGS_H__ - -#include -#include -#include - -/** \brief Class for saving \ tags for a specific UniqueId. - */ -class ExtraTags { - public: - /** Constructor. */ - ExtraTags(const std::string profile); - - /** Destructor. */ - virtual ~ExtraTags(); - - /** Handle the beginning of an element. */ - bool addTag(std::string tag, std::string data); - - /** Set given short pointer to value of tag, if it exists. */ - bool setData(std::string tag, short *data); - - /** Set given int pointer to value of tag, if it exists. */ - bool setData(std::string tag, int *data); - - /** Set given float pointer to value of tag, if it exists. */ - bool setData(std::string tag, float *data); - - /** Set given char pointer to value of tag, if it exists. */ - bool setData(std::string tag, char *data); - std::string setData(std::string tag, std::string &data); - - /** Return true if the extra tags is for specified profile. */ - bool isProfile(std::string profile); - - private: - /** Disable default copy constructor. */ - ExtraTags(const ExtraTags &pre); - /** Disable default assignment operator. */ - const ExtraTags &operator=(const ExtraTags &pre); - - /** The profile for which the tags are. */ - std::string profile; - - /** Map of tag and text pairs. */ - std::map tags; - - /** Get text data for tag as an int. */ - int asInt(std::string tag, bool *ok); - /** Get text data for tag as a float. */ - float asFloat(std::string tag, bool *ok); - /** Get text data for tag as a string. */ - std::string asString(std::string tag, bool *ok); -}; - -#endif /* __EXTRATAGS_H__ */ diff --git a/source/blender/collada/GeometryExporter.cpp b/source/blender/collada/GeometryExporter.cpp deleted file mode 100644 index 640bf3c0633..00000000000 --- a/source/blender/collada/GeometryExporter.cpp +++ /dev/null @@ -1,718 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -#include "COLLADASWPrimitves.h" -#include "COLLADASWSource.h" -#include "COLLADASWVertices.h" -#include "COLLADABUUtils.h" - -#include "GeometryExporter.h" - -#include "DNA_meshdata_types.h" - -extern "C" { -#include "BLI_utildefines.h" - -#include "BKE_customdata.h" -#include "BKE_global.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -} - -#include "collada_internal.h" -#include "collada_utils.h" - -void GeometryExporter::exportGeom() -{ - Scene *sce = blender_context.get_scene(); - openLibrary(); - - GeometryFunctor gf; - gf.forEachMeshObjectInExportSet( - sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); -} - -void GeometryExporter::operator()(Object *ob) -{ - bool use_instantiation = this->export_settings.get_use_object_instantiation(); - Mesh *me = bc_get_mesh_copy(blender_context, - ob, - this->export_settings.get_export_mesh_type(), - this->export_settings.get_apply_modifiers(), - this->export_settings.get_triangulate()); - - std::string geom_id = get_geometry_id(ob, use_instantiation); - std::vector nor; - std::vector norind; - - /* Skip if linked geometry was already exported from another reference */ - if (use_instantiation && exportedGeometry.find(geom_id) != exportedGeometry.end()) { - return; - } - - std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob); - geom_name = encode_xml(geom_name); - - exportedGeometry.insert(geom_id); - - bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); - - create_normals(nor, norind, me); - - /* openMesh(geoId, geoName, meshId) */ - openMesh(geom_id, geom_name); - - /* writes for vertex coords */ - createVertsSource(geom_id, me); - - /* writes for normal coords */ - createNormalsSource(geom_id, me, nor); - - bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV); - - /* writes for uv coords if mesh has uv coords */ - if (has_uvs) { - createTexcoordsSource(geom_id, me); - } - - if (has_color) { - createVertexColorSource(geom_id, me); - } - /* */ - - COLLADASW::Vertices verts(mSW); - verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX)); - COLLADASW::InputList &input_list = verts.getInputList(); - COLLADASW::Input input(COLLADASW::InputSemantic::POSITION, - getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); - input_list.push_back(input); - verts.add(); - - createLooseEdgeList(ob, me, geom_id); - - /* Only create Polylists if number of faces > 0 */ - if (me->totface > 0) { - /* XXX slow */ - if (ob->totcol) { - for (int a = 0; a < ob->totcol; a++) { - create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind); - } - } - else { - create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind); - } - } - - closeMesh(); - - closeGeometry(); - - if (this->export_settings.get_include_shapekeys()) { - Key *key = BKE_key_from_object(ob); - if (key) { - KeyBlock *kb = (KeyBlock *)key->block.first; - /* skip the basis */ - kb = kb->next; - for (; kb; kb = kb->next) { - BKE_keyblock_convert_to_mesh(kb, me); - export_key_mesh(ob, me, kb); - } - } - } - - BKE_id_free(NULL, me); -} - -void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) -{ - std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name); - std::vector nor; - std::vector norind; - - if (exportedGeometry.find(geom_id) != exportedGeometry.end()) { - return; - } - - std::string geom_name = kb->name; - - exportedGeometry.insert(geom_id); - - bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); - - create_normals(nor, norind, me); - - // openMesh(geoId, geoName, meshId) - openMesh(geom_id, geom_name); - - /* writes for vertex coords */ - createVertsSource(geom_id, me); - - /* writes for normal coords */ - createNormalsSource(geom_id, me, nor); - - bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV); - - /* writes for uv coords if mesh has uv coords */ - if (has_uvs) { - createTexcoordsSource(geom_id, me); - } - - if (has_color) { - createVertexColorSource(geom_id, me); - } - - /* */ - - COLLADASW::Vertices verts(mSW); - verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX)); - COLLADASW::InputList &input_list = verts.getInputList(); - COLLADASW::Input input(COLLADASW::InputSemantic::POSITION, - getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); - input_list.push_back(input); - verts.add(); - - // createLooseEdgeList(ob, me, geom_id, norind); - - /* XXX slow */ - if (ob->totcol) { - for (int a = 0; a < ob->totcol; a++) { - create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind); - } - } - else { - create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind); - } - - closeMesh(); - - closeGeometry(); -} - -void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id) -{ - - MEdge *medges = me->medge; - int totedges = me->totedge; - int edges_in_linelist = 0; - std::vector edge_list; - int index; - - /* Find all loose edges in Mesh - * and save vertex indices in edge_list */ - for (index = 0; index < totedges; index++) { - MEdge *edge = &medges[index]; - - if (edge->flag & ME_LOOSEEDGE) { - edges_in_linelist += 1; - edge_list.push_back(edge->v1); - edge_list.push_back(edge->v2); - } - } - - if (edges_in_linelist > 0) { - /* Create the list of loose edges */ - COLLADASW::Lines lines(mSW); - - lines.setCount(edges_in_linelist); - - COLLADASW::InputList &til = lines.getInputList(); - - /* creates in for vertices */ - COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX, - getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), - 0); - til.push_back(input1); - - lines.prepareToAppendValues(); - - for (index = 0; index < edges_in_linelist; index++) { - lines.appendValues(edge_list[2 * index + 1]); - lines.appendValues(edge_list[2 * index]); - } - lines.finish(); - } -} - -static void prepareToAppendValues(bool is_triangulated, - COLLADASW::PrimitivesBase &primitive_list, - std::vector &vcount_list) -{ - /* performs the actual writing */ - if (is_triangulated) { - ((COLLADASW::Triangles &)primitive_list).prepareToAppendValues(); - } - else { - /* sets */ - primitive_list.setVCountList(vcount_list); - ((COLLADASW::Polylist &)primitive_list).prepareToAppendValues(); - } -} - -static void finish_and_delete_primitive_List(bool is_triangulated, - COLLADASW::PrimitivesBase *primitive_list) -{ - if (is_triangulated) { - ((COLLADASW::Triangles *)primitive_list)->finish(); - } - else { - ((COLLADASW::Polylist *)primitive_list)->finish(); - } - delete primitive_list; -} - -static COLLADASW::PrimitivesBase *create_primitive_list(bool is_triangulated, - COLLADASW::StreamWriter *mSW) -{ - COLLADASW::PrimitivesBase *primitive_list; - - if (is_triangulated) { - primitive_list = new COLLADASW::Triangles(mSW); - } - else { - primitive_list = new COLLADASW::Polylist(mSW); - } - return primitive_list; -} - -static bool collect_vertex_counts_per_poly(Mesh *me, - int material_index, - std::vector &vcount_list) -{ - MPoly *mpolys = me->mpoly; - int totpolys = me->totpoly; - bool is_triangulated = true; - - int i; - /* Expecting that p->mat_nr is always 0 if the mesh has no materials assigned */ - for (i = 0; i < totpolys; i++) { - MPoly *p = &mpolys[i]; - if (p->mat_nr == material_index) { - int vertex_count = p->totloop; - vcount_list.push_back(vertex_count); - if (vertex_count != 3) { - is_triangulated = false; - } - } - } - return is_triangulated; -} - -std::string GeometryExporter::makeVertexColorSourceId(std::string &geom_id, char *layer_name) -{ - std::string result = getIdBySemantics(geom_id, COLLADASW::InputSemantic::COLOR) + "-" + - layer_name; - return result; -} - -/* powerful because it handles both cases when there is material and when there's not */ -void GeometryExporter::create_mesh_primitive_list(short material_index, - bool has_uvs, - bool has_color, - Object *ob, - Mesh *me, - std::string &geom_id, - std::vector &norind) -{ - - MPoly *mpolys = me->mpoly; - MLoop *mloops = me->mloop; - int totpolys = me->totpoly; - - std::vector vcount_list; - - bool is_triangulated = collect_vertex_counts_per_poly(me, material_index, vcount_list); - int polygon_count = vcount_list.size(); - - /* no faces using this material */ - if (polygon_count == 0) { - fprintf( - stderr, "%s: material with index %d is not used.\n", id_name(ob).c_str(), material_index); - return; - } - - Material *ma = ob->totcol ? BKE_object_material_get(ob, material_index + 1) : NULL; - COLLADASW::PrimitivesBase *primitive_list = create_primitive_list(is_triangulated, mSW); - - /* sets count attribute in */ - primitive_list->setCount(polygon_count); - - /* sets material name */ - if (ma) { - std::string material_id = get_material_id(ma); - std::ostringstream ostr; - ostr << translate_id(material_id); - primitive_list->setMaterial(ostr.str()); - } - - COLLADASW::Input vertex_input(COLLADASW::InputSemantic::VERTEX, - getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), - 0); - COLLADASW::Input normals_input(COLLADASW::InputSemantic::NORMAL, - getUrlBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL), - 1); - - COLLADASW::InputList &til = primitive_list->getInputList(); - til.push_back(vertex_input); - til.push_back(normals_input); - - /* if mesh has uv coords writes for TEXCOORD */ - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); - for (int i = 0; i < num_layers; i++) { - int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, i); - if (!this->export_settings.get_active_uv_only() || layer_index == active_uv_index) { - - // char *name = CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, i); - COLLADASW::Input texcoord_input( - COLLADASW::InputSemantic::TEXCOORD, - makeUrl(makeTexcoordSourceId(geom_id, i, this->export_settings.get_active_uv_only())), - 2, // this is only until we have optimized UV sets - (this->export_settings.get_active_uv_only()) ? 0 : layer_index - 1 /* set (0,1,2,...) */ - ); - til.push_back(texcoord_input); - } - } - - int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); - if (totlayer_mcol > 0) { - int map_index = 0; - - for (int a = 0; a < totlayer_mcol; a++) { - char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); - COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR, - makeUrl(makeVertexColorSourceId(geom_id, layer_name)), - (has_uvs) ? 3 : 2, // all color layers have same index order - map_index // set number equals color map index - ); - til.push_back(input4); - map_index++; - } - } - - /* performs the actual writing */ - prepareToAppendValues(is_triangulated, *primitive_list, vcount_list); - - /*

*/ - int texindex = 0; - for (int i = 0; i < totpolys; i++) { - MPoly *p = &mpolys[i]; - int loop_count = p->totloop; - - if (p->mat_nr == material_index) { - MLoop *l = &mloops[p->loopstart]; - BCPolygonNormalsIndices normal_indices = norind[i]; - - for (int j = 0; j < loop_count; j++) { - primitive_list->appendValues(l[j].v); - primitive_list->appendValues(normal_indices[j]); - if (has_uvs) { - primitive_list->appendValues(texindex + j); - } - - if (has_color) { - primitive_list->appendValues(texindex + j); - } - } - } - - texindex += loop_count; - } - - finish_and_delete_primitive_List(is_triangulated, primitive_list); -} - -/* creates for positions */ -void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me) -{ -#if 0 - int totverts = dm->getNumVerts(dm); - MVert *verts = dm->getVertArray(dm); -#endif - int totverts = me->totvert; - MVert *verts = me->mvert; - - COLLADASW::FloatSourceF source(mSW); - source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); - source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) + - ARRAY_ID_SUFFIX); - source.setAccessorCount(totverts); - source.setAccessorStride(3); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - /* main function, it creates , */ - source.prepareToAppendValues(); - /* appends data to */ - int i = 0; - for (i = 0; i < totverts; i++) { - Vector co; - if (export_settings.get_apply_global_orientation()) { - bc_add_global_transform(co, verts[i].co, export_settings.get_global_transform()); - } - else { - copy_v3_v3(co, verts[i].co); - } - source.appendValues(co[0], co[1], co[2]); - } - - source.finish(); -} - -void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) -{ - /* Find number of vertex color layers */ - int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); - if (totlayer_mcol == 0) { - return; - } - - int map_index = 0; - for (int a = 0; a < totlayer_mcol; a++) { - - map_index++; - MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, a); - - COLLADASW::FloatSourceF source(mSW); - - char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); - std::string layer_id = makeVertexColorSourceId(geom_id, layer_name); - source.setId(layer_id); - - source.setNodeName(layer_name); - - source.setArrayId(layer_id + ARRAY_ID_SUFFIX); - source.setAccessorCount(me->totloop); - source.setAccessorStride(4); - - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("R"); - param.push_back("G"); - param.push_back("B"); - param.push_back("A"); - - source.prepareToAppendValues(); - - MPoly *mpoly; - int i; - for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) { - MLoopCol *mlc = mloopcol + mpoly->loopstart; - for (int j = 0; j < mpoly->totloop; j++, mlc++) { - source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f); - } - } - - source.finish(); - } -} - -std::string GeometryExporter::makeTexcoordSourceId(std::string &geom_id, - int layer_index, - bool is_single_layer) -{ - char suffix[20]; - if (is_single_layer) { - suffix[0] = '\0'; - } - else { - sprintf(suffix, "-%d", layer_index); - } - return getIdBySemantics(geom_id, COLLADASW::InputSemantic::TEXCOORD) + suffix; -} - -/* creates for texcoords */ -void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me) -{ - - int totpoly = me->totpoly; - int totuv = me->totloop; - MPoly *mpolys = me->mpoly; - - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - - /* write for each layer - * each will get id like meshName + "map-channel-1" */ - int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); - for (int a = 0; a < num_layers; a++) { - int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, a); - if (!this->export_settings.get_active_uv_only() || layer_index == active_uv_index) { - MLoopUV *mloops = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a); - - COLLADASW::FloatSourceF source(mSW); - std::string layer_id = makeTexcoordSourceId( - geom_id, a, this->export_settings.get_active_uv_only()); - source.setId(layer_id); - source.setArrayId(layer_id + ARRAY_ID_SUFFIX); - - source.setAccessorCount(totuv); - source.setAccessorStride(2); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("S"); - param.push_back("T"); - - source.prepareToAppendValues(); - - for (int index = 0; index < totpoly; index++) { - MPoly *mpoly = mpolys + index; - MLoopUV *mloop = mloops + mpoly->loopstart; - for (int j = 0; j < mpoly->totloop; j++) { - source.appendValues(mloop[j].uv[0], mloop[j].uv[1]); - } - } - - source.finish(); - } - } -} - -bool operator<(const Normal &a, const Normal &b) -{ - /* only needed to sort normal vectors and find() them later in a map.*/ - return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z))); -} - -/* creates for normals */ -void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::vector &nor) -{ -#if 0 - int totverts = dm->getNumVerts(dm); - MVert *verts = dm->getVertArray(dm); -#endif - - COLLADASW::FloatSourceF source(mSW); - source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL)); - source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL) + ARRAY_ID_SUFFIX); - source.setAccessorCount((unsigned long)nor.size()); - source.setAccessorStride(3); - COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); - param.push_back("X"); - param.push_back("Y"); - param.push_back("Z"); - - source.prepareToAppendValues(); - - std::vector::iterator it; - for (it = nor.begin(); it != nor.end(); it++) { - Normal &n = *it; - - Vector no{n.x, n.y, n.z}; - if (export_settings.get_apply_global_orientation()) { - bc_add_global_transform(no, export_settings.get_global_transform()); - } - source.appendValues(no[0], no[1], no[2]); - } - - source.finish(); -} - -void GeometryExporter::create_normals(std::vector &normals, - std::vector &polygons_normals, - Mesh *me) -{ - std::map shared_normal_indices; - int last_normal_index = -1; - - MVert *verts = me->mvert; - MLoop *mloops = me->mloop; - float(*lnors)[3] = NULL; - bool use_custom_normals = false; - - BKE_mesh_calc_normals_split(me); - if (CustomData_has_layer(&me->ldata, CD_NORMAL)) { - lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL); - use_custom_normals = true; - } - - for (int poly_index = 0; poly_index < me->totpoly; poly_index++) { - MPoly *mpoly = &me->mpoly[poly_index]; - bool use_vertex_normals = use_custom_normals || mpoly->flag & ME_SMOOTH; - - if (!use_vertex_normals) { - /* For flat faces use face normal as vertex normal: */ - - float vector[3]; - BKE_mesh_calc_poly_normal(mpoly, mloops + mpoly->loopstart, verts, vector); - - Normal n = {vector[0], vector[1], vector[2]}; - normals.push_back(n); - last_normal_index++; - } - - BCPolygonNormalsIndices poly_indices; - for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) { - unsigned int loop_idx = mpoly->loopstart + loop_index; - if (use_vertex_normals) { - float normalized[3]; - - if (use_custom_normals) { - normalize_v3_v3(normalized, lnors[loop_idx]); - } - else { - normal_short_to_float_v3(normalized, verts[mloops[loop_index].v].no); - normalize_v3(normalized); - } - Normal n = {normalized[0], normalized[1], normalized[2]}; - - if (shared_normal_indices.find(n) != shared_normal_indices.end()) { - poly_indices.add_index(shared_normal_indices[n]); - } - else { - last_normal_index++; - poly_indices.add_index(last_normal_index); - shared_normal_indices[n] = last_normal_index; - normals.push_back(n); - } - } - else { - poly_indices.add_index(last_normal_index); - } - } - - polygons_normals.push_back(poly_indices); - } -} - -std::string GeometryExporter::getIdBySemantics(std::string geom_id, - COLLADASW::InputSemantic::Semantics type, - std::string other_suffix) -{ - return geom_id + getSuffixBySemantic(type) + other_suffix; -} - -COLLADASW::URI GeometryExporter::getUrlBySemantics(std::string geom_id, - COLLADASW::InputSemantic::Semantics type, - std::string other_suffix) -{ - - std::string id(getIdBySemantics(geom_id, type, other_suffix)); - return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); -} - -COLLADASW::URI GeometryExporter::makeUrl(std::string id) -{ - return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); -} diff --git a/source/blender/collada/GeometryExporter.h b/source/blender/collada/GeometryExporter.h deleted file mode 100644 index 8c7a38fc407..00000000000 --- a/source/blender/collada/GeometryExporter.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __GEOMETRYEXPORTER_H__ -#define __GEOMETRYEXPORTER_H__ - -#include -#include -#include - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryGeometries.h" -#include "COLLADASWInputList.h" - -#include "DNA_mesh_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_key_types.h" - -#include "ExportSettings.h" -#include "collada_utils.h" -#include "BlenderContext.h" -#include "BKE_key.h" - -class Normal { - public: - float x; - float y; - float z; - - friend bool operator<(const Normal &, const Normal &); -}; - -bool operator<(const Normal &, const Normal &); - -/* TODO: optimize UV sets by making indexed list with duplicates removed */ -class GeometryExporter : COLLADASW::LibraryGeometries { - struct Face { - unsigned int v1, v2, v3, v4; - }; - - public: - /* TODO: optimize UV sets by making indexed list with duplicates removed */ - GeometryExporter(BlenderContext &blender_context, - COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings) - : COLLADASW::LibraryGeometries(sw), - blender_context(blender_context), - export_settings(export_settings) - { - } - - void exportGeom(); - - void operator()(Object *ob); - - void createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id); - - /* powerful because it handles both cases when there is material and when there's not */ - void create_mesh_primitive_list(short material_index, - bool has_uvs, - bool has_color, - Object *ob, - Mesh *me, - std::string &geom_id, - std::vector &norind); - - /* creates for positions */ - void createVertsSource(std::string geom_id, Mesh *me); - - void createVertexColorSource(std::string geom_id, Mesh *me); - - std::string makeTexcoordSourceId(std::string &geom_id, int layer_index, bool is_single_layer); - - /* creates for texcoords */ - void createTexcoordsSource(std::string geom_id, Mesh *me); - void createTesselatedTexcoordsSource(std::string geom_id, Mesh *me); - - /* creates for normals */ - void createNormalsSource(std::string geom_id, Mesh *me, std::vector &nor); - - void create_normals(std::vector &nor, - std::vector &ind, - Mesh *me); - - std::string getIdBySemantics(std::string geom_id, - COLLADASW::InputSemantic::Semantics type, - std::string other_suffix = ""); - std::string makeVertexColorSourceId(std::string &geom_id, char *layer_name); - - COLLADASW::URI getUrlBySemantics(std::string geom_id, - COLLADASW::InputSemantic::Semantics type, - std::string other_suffix = ""); - - COLLADASW::URI makeUrl(std::string id); - - void export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb); - - private: - std::set exportedGeometry; - BlenderContext &blender_context; - BCExportSettings &export_settings; - - Mesh *get_mesh(Scene *sce, Object *ob, int apply_modifiers); -}; - -struct GeometryFunctor { - /* f should have - * void operator()(Object *ob) */ - template - void forEachMeshObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) - { - LinkNode *node; - for (node = export_set; node; node = node->next) { - Object *ob = (Object *)node->link; - if (ob->type == OB_MESH) { - f(ob); - } - } - } -}; - -#endif diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp deleted file mode 100644 index 6e31e17fb26..00000000000 --- a/source/blender/collada/ImageExporter.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "COLLADABUURI.h" -#include "COLLADASWImage.h" - -extern "C" { -#include "DNA_texture_types.h" -#include "DNA_image_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_customdata.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_main.h" -#include "BKE_mesh.h" - -#include "BLI_fileops.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "IMB_imbuf_types.h" -} - -#include "ImageExporter.h" -#include "MaterialExporter.h" - -ImagesExporter::ImagesExporter(COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings, - KeyImageMap &key_image_map) - : COLLADASW::LibraryImages(sw), export_settings(export_settings), key_image_map(key_image_map) -{ - /* pass */ -} - -void ImagesExporter::export_UV_Image(Image *image, bool use_copies) -{ - std::string name(id_name(image)); - std::string translated_name(translate_id(name)); - - ImBuf *imbuf = BKE_image_acquire_ibuf(image, NULL, NULL); - if (!imbuf) { - fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->name); - return; - } - - bool is_dirty = BKE_image_is_dirty(image); - - ImageFormatData imageFormat; - BKE_imbuf_to_image_format(&imageFormat, imbuf); - - short image_source = image->source; - bool is_generated = image_source == IMA_SRC_GENERATED; - bool is_packed = BKE_image_has_packedfile(image); - - char export_path[FILE_MAX]; - char source_path[FILE_MAX]; - char export_dir[FILE_MAX]; - char export_file[FILE_MAX]; - - /* Destination folder for exported assets */ - BLI_split_dir_part(this->export_settings.get_filepath(), export_dir, sizeof(export_dir)); - - if (is_generated || is_dirty || use_copies || is_packed) { - - /* make absolute destination path */ - - BLI_strncpy(export_file, name.c_str(), sizeof(export_file)); - BKE_image_path_ensure_ext_from_imformat(export_file, &imageFormat); - - BLI_join_dirfile(export_path, sizeof(export_path), export_dir, export_file); - - /* make dest directory if it doesn't exist */ - BLI_make_existing_file(export_path); - } - - if (is_generated || is_dirty || is_packed) { - - /* This image in its current state only exists in Blender memory. - * So we have to export it. The export will keep the image state intact, - * so the exported file will not be associated with the image. */ - - if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { - fprintf(stderr, "Collada export: Cannot export image to:\n%s\n", export_path); - return; - } - BLI_strncpy(export_path, export_file, sizeof(export_path)); - } - else { - - /* make absolute source path */ - BLI_strncpy(source_path, image->name, sizeof(source_path)); - BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id)); - BLI_cleanup_path(NULL, source_path); - - if (use_copies) { - - /* This image is already located on the file system. - * But we want to create copies here. - * To move images into the same export directory. - * Note: If an image is already located in the export folder, - * then skip the copy (as it would result in a file copy error). */ - - if (BLI_path_cmp(source_path, export_path) != 0) { - if (BLI_copy(source_path, export_path) != 0) { - fprintf(stderr, - "Collada export: Cannot copy image:\n source:%s\ndest :%s\n", - source_path, - export_path); - return; - } - } - - BLI_strncpy(export_path, export_file, sizeof(export_path)); - } - else { - - /* Do not make any copies, but use the source path directly as reference - * to the original image */ - - BLI_strncpy(export_path, source_path, sizeof(export_path)); - } - } - - /* Set name also to mNameNC. - * This helps other viewers import files exported from Blender better. */ - COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(export_path)), - translated_name, - translated_name); - img.add(mSW); - fprintf(stdout, "Collada export: Added image: %s\n", export_file); - - BKE_image_release_ibuf(image, imbuf, NULL); -} - -void ImagesExporter::exportImages(Scene *sce) -{ - bool use_texture_copies = this->export_settings.get_use_texture_copies(); - openLibrary(); - - KeyImageMap::iterator iter; - for (iter = key_image_map.begin(); iter != key_image_map.end(); iter++) { - - Image *image = iter->second; - std::string uid(id_name(image)); - std::string key = translate_id(uid); - - export_UV_Image(image, use_texture_copies); - } - - closeLibrary(); -} diff --git a/source/blender/collada/ImageExporter.h b/source/blender/collada/ImageExporter.h deleted file mode 100644 index b72d2709382..00000000000 --- a/source/blender/collada/ImageExporter.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __IMAGEEXPORTER_H__ -#define __IMAGEEXPORTER_H__ - -#include -#include - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryImages.h" - -#include "DNA_material_types.h" -#include "DNA_image_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "ExportSettings.h" -#include "collada_utils.h" - -class ImagesExporter : COLLADASW::LibraryImages { - public: - ImagesExporter(COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings, - KeyImageMap &key_image_map); - void exportImages(Scene *sce); - - private: - BCExportSettings &export_settings; - KeyImageMap &key_image_map; - void export_UV_Image(Image *image, bool use_texture_copies); -}; - -#endif diff --git a/source/blender/collada/ImportSettings.cpp b/source/blender/collada/ImportSettings.cpp deleted file mode 100644 index 049ee1d0975..00000000000 --- a/source/blender/collada/ImportSettings.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "ImportSettings.h" diff --git a/source/blender/collada/ImportSettings.h b/source/blender/collada/ImportSettings.h deleted file mode 100644 index 608d8bff882..00000000000 --- a/source/blender/collada/ImportSettings.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __IMPORTSETTINGS_H__ -#define __IMPORTSETTINGS_H__ - -typedef struct ImportSettings { - bool import_units; - bool find_chains; - bool auto_connect; - bool fix_orientation; - int min_chain_length; - char *filepath; - bool keep_bind_info; -} ImportSettings; - -#endif diff --git a/source/blender/collada/InstanceWriter.cpp b/source/blender/collada/InstanceWriter.cpp deleted file mode 100644 index c9390d23fe7..00000000000 --- a/source/blender/collada/InstanceWriter.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include - -#include "COLLADASWInstanceMaterial.h" - -extern "C" { -#include "BKE_customdata.h" -#include "BKE_material.h" -#include "DNA_mesh_types.h" -} - -#include "InstanceWriter.h" -#include "collada_internal.h" -#include "collada_utils.h" - -void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial &bind_material, - Object *ob, - bool active_uv_only) -{ - for (int a = 0; a < ob->totcol; a++) { - Material *ma = BKE_object_material_get(ob, a + 1); - - COLLADASW::InstanceMaterialList &iml = bind_material.getInstanceMaterialList(); - - if (ma) { - std::string matid(get_material_id(ma)); - matid = translate_id(matid); - std::ostringstream ostr; - ostr << matid; - COLLADASW::InstanceMaterial im(ostr.str(), - COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid)); - - // create for each uv map - Mesh *me = (Mesh *)ob->data; - - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - - int map_index = 0; - int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); - for (int b = 0; b < num_layers; b++) { - if (!active_uv_only || b == active_uv_index) { - char *name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, b); - im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", map_index++)); - } - } - - iml.push_back(im); - } - } -} diff --git a/source/blender/collada/InstanceWriter.h b/source/blender/collada/InstanceWriter.h deleted file mode 100644 index cfec1cf7006..00000000000 --- a/source/blender/collada/InstanceWriter.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __INSTANCEWRITER_H__ -#define __INSTANCEWRITER_H__ - -#include "COLLADASWBindMaterial.h" - -#include "DNA_object_types.h" - -class InstanceWriter { - protected: - void add_material_bindings(COLLADASW::BindMaterial &bind_material, - Object *ob, - bool active_uv_only); -}; - -#endif diff --git a/source/blender/collada/LightExporter.cpp b/source/blender/collada/LightExporter.cpp deleted file mode 100644 index 463981ceefa..00000000000 --- a/source/blender/collada/LightExporter.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -#include "COLLADASWColor.h" -#include "COLLADASWLight.h" - -#include "BLI_math.h" - -#include "LightExporter.h" -#include "collada_internal.h" - -template -void forEachLightObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) -{ - LinkNode *node; - for (node = export_set; node; node = node->next) { - Object *ob = (Object *)node->link; - - if (ob->type == OB_LAMP && ob->data) { - f(ob); - } - } -} - -LightsExporter::LightsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) - : COLLADASW::LibraryLights(sw), export_settings(export_settings) -{ -} - -void LightsExporter::exportLights(Scene *sce) -{ - openLibrary(); - - forEachLightObjectInExportSet(sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); -} - -void LightsExporter::operator()(Object *ob) -{ - Light *la = (Light *)ob->data; - std::string la_id(get_light_id(ob)); - std::string la_name(id_name(la)); - COLLADASW::Color col(la->r * la->energy, la->g * la->energy, la->b * la->energy); - float d, constatt, linatt, quadatt; - - d = la->dist; - - constatt = 1.0f; - - if (la->falloff_type == LA_FALLOFF_INVLINEAR) { - linatt = 1.0f / d; - quadatt = 0.0f; - } - else { - linatt = 0.0f; - quadatt = 1.0f / (d * d); - } - - // sun - if (la->type == LA_SUN) { - COLLADASW::DirectionalLight cla(mSW, la_id, la_name); - cla.setColor(col, false, "color"); - cla.setConstantAttenuation(constatt); - exportBlenderProfile(cla, la); - addLight(cla); - } - - // spot - else if (la->type == LA_SPOT) { - COLLADASW::SpotLight cla(mSW, la_id, la_name); - cla.setColor(col, false, "color"); - cla.setFallOffAngle(RAD2DEGF(la->spotsize), false, "fall_off_angle"); - cla.setFallOffExponent(la->spotblend, false, "fall_off_exponent"); - cla.setConstantAttenuation(constatt); - cla.setLinearAttenuation(linatt); - cla.setQuadraticAttenuation(quadatt); - exportBlenderProfile(cla, la); - addLight(cla); - } - // lamp - else if (la->type == LA_LOCAL) { - COLLADASW::PointLight cla(mSW, la_id, la_name); - cla.setColor(col, false, "color"); - cla.setConstantAttenuation(constatt); - cla.setLinearAttenuation(linatt); - cla.setQuadraticAttenuation(quadatt); - exportBlenderProfile(cla, la); - addLight(cla); - } - // area light is not supported - // it will be exported as a local lamp - else { - COLLADASW::PointLight cla(mSW, la_id, la_name); - cla.setColor(col, false, "color"); - cla.setConstantAttenuation(constatt); - cla.setLinearAttenuation(linatt); - cla.setQuadraticAttenuation(quadatt); - exportBlenderProfile(cla, la); - addLight(cla); - } -} - -bool LightsExporter::exportBlenderProfile(COLLADASW::Light &cla, Light *la) -{ - cla.addExtraTechniqueParameter("blender", "type", la->type); - cla.addExtraTechniqueParameter("blender", "flag", la->flag); - cla.addExtraTechniqueParameter("blender", "mode", la->mode); - cla.addExtraTechniqueParameter("blender", "gamma", la->k, "blender_gamma"); - cla.addExtraTechniqueParameter("blender", "red", la->r); - cla.addExtraTechniqueParameter("blender", "green", la->g); - cla.addExtraTechniqueParameter("blender", "blue", la->b); - cla.addExtraTechniqueParameter("blender", "shadow_r", la->shdwr, "blender_shadow_r"); - cla.addExtraTechniqueParameter("blender", "shadow_g", la->shdwg, "blender_shadow_g"); - cla.addExtraTechniqueParameter("blender", "shadow_b", la->shdwb, "blender_shadow_b"); - cla.addExtraTechniqueParameter("blender", "energy", la->energy, "blender_energy"); - cla.addExtraTechniqueParameter("blender", "dist", la->dist, "blender_dist"); - cla.addExtraTechniqueParameter("blender", "spotsize", RAD2DEGF(la->spotsize)); - cla.addExtraTechniqueParameter("blender", "spotblend", la->spotblend); - cla.addExtraTechniqueParameter("blender", "att1", la->att1); - cla.addExtraTechniqueParameter("blender", "att2", la->att2); - // \todo figure out how we can have falloff curve supported here - cla.addExtraTechniqueParameter("blender", "falloff_type", la->falloff_type); - cla.addExtraTechniqueParameter("blender", "clipsta", la->clipsta); - cla.addExtraTechniqueParameter("blender", "clipend", la->clipend); - cla.addExtraTechniqueParameter("blender", "bias", la->bias); - cla.addExtraTechniqueParameter("blender", "soft", la->soft); - cla.addExtraTechniqueParameter("blender", "bufsize", la->bufsize); - cla.addExtraTechniqueParameter("blender", "samp", la->samp); - cla.addExtraTechniqueParameter("blender", "buffers", la->buffers); - cla.addExtraTechniqueParameter("blender", "area_shape", la->area_shape); - cla.addExtraTechniqueParameter("blender", "area_size", la->area_size); - cla.addExtraTechniqueParameter("blender", "area_sizey", la->area_sizey); - cla.addExtraTechniqueParameter("blender", "area_sizez", la->area_sizez); - - return true; -} diff --git a/source/blender/collada/LightExporter.h b/source/blender/collada/LightExporter.h deleted file mode 100644 index 045ccfe1ce8..00000000000 --- a/source/blender/collada/LightExporter.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __LIGHTEXPORTER_H__ -#define __LIGHTEXPORTER_H__ - -#include "COLLADASWStreamWriter.h" -#include "COLLADASWLibraryLights.h" - -#include "DNA_light_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" - -#include "ExportSettings.h" - -class LightsExporter : COLLADASW::LibraryLights { - public: - LightsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); - void exportLights(Scene *sce); - void operator()(Object *ob); - - private: - bool exportBlenderProfile(COLLADASW::Light &cla, Light *la); - BCExportSettings &export_settings; -}; - -#endif diff --git a/source/blender/collada/MaterialExporter.cpp b/source/blender/collada/MaterialExporter.cpp deleted file mode 100644 index 488d1833e48..00000000000 --- a/source/blender/collada/MaterialExporter.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "MaterialExporter.h" -#include "COLLADABUUtils.h" -#include "collada_internal.h" - -MaterialsExporter::MaterialsExporter(COLLADASW::StreamWriter *sw, - BCExportSettings &export_settings) - : COLLADASW::LibraryMaterials(sw), export_settings(export_settings) -{ - /* pass */ -} - -void MaterialsExporter::exportMaterials(Scene *sce) -{ - if (hasMaterials(sce)) { - openLibrary(); - - MaterialFunctor mf; - mf.forEachMaterialInExportSet( - sce, *this, this->export_settings.get_export_set()); - - closeLibrary(); - } -} - -bool MaterialsExporter::hasMaterials(Scene *sce) -{ - LinkNode *node; - for (node = this->export_settings.get_export_set(); node; node = node->next) { - Object *ob = (Object *)node->link; - int a; - for (a = 0; a < ob->totcol; a++) { - Material *ma = BKE_object_material_get(ob, a + 1); - - // no material, but check all of the slots - if (!ma) { - continue; - } - - return true; - } - } - return false; -} - -void MaterialsExporter::operator()(Material *ma, Object *ob) -{ - std::string mat_name = encode_xml(id_name(ma)); - std::string mat_id = get_material_id(ma); - std::string eff_id = get_effect_id(ma); - - openMaterial(mat_id, mat_name); - addInstanceEffect(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, eff_id)); - - closeMaterial(); -} diff --git a/source/blender/collada/MaterialExporter.h b/source/blender/collada/MaterialExporter.h deleted file mode 100644 index be0d939b68a..00000000000 --- a/source/blender/collada/MaterialExporter.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __MATERIALEXPORTER_H__ -#define __MATERIALEXPORTER_H__ - -#include -#include - -#include "COLLADASWLibraryMaterials.h" -#include "COLLADASWStreamWriter.h" - -extern "C" { -#include "BKE_material.h" -#include "DNA_material_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -} - -#include "GeometryExporter.h" -#include "collada_internal.h" -#include "ExportSettings.h" -#include "Materials.h" - -class MaterialsExporter : COLLADASW::LibraryMaterials { - public: - MaterialsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); - void exportMaterials(Scene *sce); - void operator()(Material *ma, Object *ob); - - private: - bool hasMaterials(Scene *sce); - BCExportSettings &export_settings; -}; - -// used in forEachMaterialInScene -template class ForEachMaterialFunctor { - std::vector - mMat; // contains list of material names, to avoid duplicate calling of f - Functor *f; - - public: - ForEachMaterialFunctor(Functor *f) : f(f) - { - } - - void operator()(Object *ob) - { - int a; - for (a = 0; a < ob->totcol; a++) { - - Material *ma = BKE_object_material_get(ob, a + 1); - - if (!ma) { - continue; - } - - std::string translated_id = translate_id(id_name(ma)); - if (find(mMat.begin(), mMat.end(), translated_id) == mMat.end()) { - (*this->f)(ma, ob); - - mMat.push_back(translated_id); - } - } - } -}; - -struct MaterialFunctor { - // calls f for each unique material linked to each object in sce - // f should have - // void operator()(Material *ma) - template - void forEachMaterialInExportSet(Scene *sce, Functor &f, LinkNode *export_set) - { - ForEachMaterialFunctor matfunc(&f); - GeometryFunctor gf; - gf.forEachMeshObjectInExportSet>(sce, matfunc, export_set); - } -}; - -#endif diff --git a/source/blender/collada/Materials.cpp b/source/blender/collada/Materials.cpp deleted file mode 100644 index 06f54884668..00000000000 --- a/source/blender/collada/Materials.cpp +++ /dev/null @@ -1,396 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include "Materials.h" - -MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map) - : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map) -{ - bNodeTree *new_ntree = prepare_material_nodetree(); - setShaderType(); - if (new_ntree) { - shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, ""); - output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, ""); - add_link(shader_node, 0, output_node, 0); - } -} - -MaterialNode::MaterialNode(bContext *C, - COLLADAFW::EffectCommon *ef, - Material *ma, - UidImageMap &uid_image_map) - : mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map) -{ - prepare_material_nodetree(); - setShaderType(); - - std::map nmap; -#if 0 - nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300); - nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission"); - nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400); - nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200); - nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency"); - nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300); - nmap["out"]->flag &= ~NODE_SELECT; - - add_link(ntree, nmap["emission"], 0, nmap["add"], 0); - add_link(ntree, nmap["main"], 0, nmap["add"], 1); - add_link(ntree, nmap["add"], 0, nmap["mix"], 1); - add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2); - - add_link(ntree, nmap["mix"], 0, nmap["out"], 0); - // experimental, probably not used. - make_group(C, ntree, nmap); -#else - shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, ""); - output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, ""); - add_link(shader_node, 0, output_node, 0); -#endif -} - -void MaterialNode::setShaderType() -{ -#if 0 - COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType(); - // Currently we only support PBR based shaders - // TODO: simulate the effects with PBR - - // blinn - if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) { - ma->spec_shader = MA_SPEC_BLINN; - ma->spec = ef->getShininess().getFloatValue(); - } - // phong - else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) { - ma->spec_shader = MA_SPEC_PHONG; - ma->har = ef->getShininess().getFloatValue(); - } - // lambert - else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) { - ma->diff_shader = MA_DIFF_LAMBERT; - } - // default - lambert - else { - ma->diff_shader = MA_DIFF_LAMBERT; - fprintf(stderr, "Current shader type is not supported, default to lambert.\n"); - } -#endif -} - -// returns null if material already has a node tree -bNodeTree *MaterialNode::prepare_material_nodetree() -{ - if (material->nodetree) { - ntree = material->nodetree; - return NULL; - } - - material->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree"); - material->use_nodes = true; - ntree = material->nodetree; - return ntree; -} - -bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label) -{ - bNode *node = nodeAddStaticNode(mContext, ntree, node_type); - if (node) { - if (label.length() > 0) { - strcpy(node->label, label.c_str()); - } - node->locx = locx; - node->locy = locy; - node->flag |= NODE_SELECT; - } - node_map[label] = node; - return node; -} - -void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index) -{ - bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); - bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); - - nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); -} - -void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val) -{ - float reflectivity = val.getFloatValue(); - if (reflectivity >= 0) { - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic"); - ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity; - material->metallic = reflectivity; - } -} - -#if 0 -// needs rework to be done for 2.81 -void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val) -{ - float roughness = val.getFloatValue(); - if (roughness >= 0) { - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness"); - ((bNodeSocketValueFloat *)socket->default_value)->value = roughness; - } -} -#endif - -void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val) -{ - float ior = val.getFloatValue(); - if (ior < 0) { - fprintf(stderr, - "IOR of negative value is not allowed for materials (using Blender default value " - "instead)"); - return; - } - - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR"); - ((bNodeSocketValueFloat *)socket->default_value)->value = ior; -} - -void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, - COLLADAFW::ColorOrTexture &cot, - COLLADAFW::FloatOrParam &val) -{ - /* Handling the alpha value according to the Collada 1.4 reference guide - * see page 7-5 Determining Transparency (Opacity) - */ - - if (effect == nullptr) { - return; - } - - if (cot.isColor() || !cot.isValid()) { - // transparent_cot is either a color or not defined - - float transparent_alpha; - if (cot.isValid()) { - COLLADAFW::Color col = cot.getColor(); - transparent_alpha = col.getAlpha(); - } - else { - // no transparent color defined - transparent_alpha = 1; - } - - float transparency_alpha = val.getFloatValue(); - if (transparency_alpha < 0) { - // transparency is not defined - transparency_alpha = 1; // set to opaque - } - - float alpha = transparent_alpha * transparency_alpha; - if (mode == COLLADAFW::EffectCommon::RGB_ZERO) { - alpha = 1 - alpha; - } - - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); - ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; - material->a = alpha; - } - else if (cot.isTexture()) { - int locy = -300 * (node_map.size() - 2); - add_texture_node(cot, -300, locy, "Alpha"); - } -} - -void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot) -{ - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color"); - float *fcol = (float *)socket->default_value; - - fcol[0] = material->r = col.getRed(); - fcol[1] = material->g = col.getGreen(); - fcol[2] = material->b = col.getBlue(); - fcol[3] = material->a = col.getAlpha(); - } - else if (cot.isTexture()) { - bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color"); - if (texture_node != NULL) { - add_link(texture_node, 0, shader_node, 0); - } - } -} - -Image *MaterialNode::get_diffuse_image() -{ - bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); - if (shader == nullptr) { - return nullptr; - } - - bNodeSocket *in_socket = nodeFindSocket(shader, SOCK_IN, "Base Color"); - if (in_socket == nullptr) { - return nullptr; - } - - bNodeLink *link = in_socket->link; - if (link == nullptr) { - return nullptr; - } - - bNode *texture = link->fromnode; - if (texture == nullptr) { - return nullptr; - } - - if (texture->type != SH_NODE_TEX_IMAGE) { - return nullptr; - } - - Image *image = (Image *)texture->id; - return image; -} - -static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col) -{ - bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0); - float *fcol = (float *)socket->default_value; - fcol[0] = col.getRed(); - fcol[1] = col.getGreen(); - fcol[2] = col.getBlue(); - - return socket; -} - -void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot) -{ - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient"); - set_color(node, col); - // TODO: Connect node - } - // texture - else if (cot.isTexture()) { - add_texture_node(cot, -300, locy, "Ambient"); - // TODO: Connect node - } -} - -void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot) -{ - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective"); - set_color(node, col); - // TODO: Connect node - } - // texture - else if (cot.isTexture()) { - add_texture_node(cot, -300, locy, "Reflective"); - // TODO: Connect node - } -} - -void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot) -{ - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission"); - float *fcol = (float *)socket->default_value; - - fcol[0] = col.getRed(); - fcol[1] = col.getGreen(); - fcol[2] = col.getBlue(); - fcol[3] = col.getAlpha(); - } - else if (cot.isTexture()) { - bNode *texture_node = add_texture_node(cot, -300, locy, "Emission"); - if (texture_node != NULL) { - add_link(texture_node, 0, shader_node, 0); - } - } -} - -void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot) -{ - if (effect == nullptr) { - return; - } - - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = effect->getTransparent().getColor(); - float alpha = effect->getTransparency().getFloatValue(); - - if (col.isValid()) { - alpha *= col.getAlpha(); // Assuming A_ONE opaque mode - } - - bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); - ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; - } - // texture - else if (cot.isTexture()) { - add_texture_node(cot, -300, locy, "Alpha"); - // TODO: Connect node - } -} - -void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot) -{ - int locy = -300 * (node_map.size() - 2); - if (cot.isColor()) { - COLLADAFW::Color col = cot.getColor(); - bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular"); - set_color(node, col); - // TODO: Connect node - } - // texture - else if (cot.isTexture()) { - add_texture_node(cot, -300, locy, "Specular"); - // TODO: Connect node - } -} - -bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot, - int locx, - int locy, - std::string label) -{ - if (effect == nullptr) { - return nullptr; - } - - UidImageMap &image_map = *uid_image_map; - - COLLADAFW::Texture ctex = cot.getTexture(); - - COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray(); - COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()]; - - const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage(); - - if (image_map.find(ima_uid) == image_map.end()) { - fprintf(stderr, "Couldn't find an image by UID.\n"); - return NULL; - } - - Image *ima = image_map[ima_uid]; - bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label); - texture_node->id = &ima->id; - return texture_node; -} diff --git a/source/blender/collada/Materials.h b/source/blender/collada/Materials.h deleted file mode 100644 index 0a4f2ee61a5..00000000000 --- a/source/blender/collada/Materials.h +++ /dev/null @@ -1,76 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __MATERIALS_H__ -#define __MATERIALS_H__ - -#include -#include - -extern "C" { -#include "BKE_context.h" -#include "BKE_node.h" -#include "BLI_listbase.h" -#include "DNA_material_types.h" -#include "DNA_node_types.h" -} - -#include "collada_utils.h" -#include "COLLADAFWEffectCommon.h" - -typedef std::map NodeMap; - -class MaterialNode { - - private: - bContext *mContext; - Material *material; - COLLADAFW::EffectCommon *effect; - UidImageMap *uid_image_map = nullptr; - KeyImageMap *key_image_map = nullptr; - - NodeMap node_map; - bNodeTree *ntree; - - bNode *shader_node; - bNode *output_node; - - bNodeTree *prepare_material_nodetree(); - bNode *add_node(int node_type, int locx, int locy, std::string label); - void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index); - bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label); - void setShaderType(); - - public: - MaterialNode(bContext *C, COLLADAFW::EffectCommon *ef, Material *ma, UidImageMap &uid_image_map); - MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map); - Image *get_diffuse_image(); - - void set_diffuse(COLLADAFW::ColorOrTexture &cot); - void set_specular(COLLADAFW::ColorOrTexture &cot); - void set_ambient(COLLADAFW::ColorOrTexture &cot); - void set_reflective(COLLADAFW::ColorOrTexture &cot); - void set_emission(COLLADAFW::ColorOrTexture &cot); - void set_opacity(COLLADAFW::ColorOrTexture &cot); - void set_reflectivity(COLLADAFW::FloatOrParam &val); - void set_shininess(COLLADAFW::FloatOrParam &val); - void set_ior(COLLADAFW::FloatOrParam &val); - void set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, - COLLADAFW::ColorOrTexture &cot, - COLLADAFW::FloatOrParam &val); -}; - -#endif /* __MATERIALS_H__ */ diff --git a/source/blender/collada/MeshImporter.cpp b/source/blender/collada/MeshImporter.cpp deleted file mode 100644 index bc6dd4202b1..00000000000 --- a/source/blender/collada/MeshImporter.cpp +++ /dev/null @@ -1,1208 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include -#include - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "COLLADAFWMeshPrimitive.h" -#include "COLLADAFWMeshVertexData.h" -#include "COLLADAFWPolygons.h" - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "BKE_customdata.h" -#include "BKE_displist.h" -#include "BKE_global.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_object.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_string.h" -#include "BLI_edgehash.h" -} - -#include "ArmatureImporter.h" -#include "MeshImporter.h" -#include "collada_utils.h" - -// get node name, or fall back to original id if not present (name is optional) -template static const std::string bc_get_dae_name(T *node) -{ - return node->getName().size() ? node->getName() : node->getOriginalId(); -} - -static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type) -{ - switch (type) { - case COLLADAFW::MeshPrimitive::LINES: - return "LINES"; - case COLLADAFW::MeshPrimitive::LINE_STRIPS: - return "LINESTRIPS"; - case COLLADAFW::MeshPrimitive::POLYGONS: - return "POLYGONS"; - case COLLADAFW::MeshPrimitive::POLYLIST: - return "POLYLIST"; - case COLLADAFW::MeshPrimitive::TRIANGLES: - return "TRIANGLES"; - case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: - return "TRIANGLE_FANS"; - case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS: - return "TRIANGLE_STRIPS"; - case COLLADAFW::MeshPrimitive::POINTS: - return "POINTS"; - case COLLADAFW::MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE: - return "UNDEFINED_PRIMITIVE_TYPE"; - } - return "UNKNOWN"; -} - -static const char *bc_geomTypeToStr(COLLADAFW::Geometry::GeometryType type) -{ - switch (type) { - case COLLADAFW::Geometry::GEO_TYPE_MESH: - return "MESH"; - case COLLADAFW::Geometry::GEO_TYPE_SPLINE: - return "SPLINE"; - case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH: - return "CONVEX_MESH"; - case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN: - default: - return "UNKNOWN"; - } -} - -UVDataWrapper::UVDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) -{ -} - -#ifdef COLLADA_DEBUG -void WVDataWrapper::print() -{ - fprintf(stderr, "UVs:\n"); - switch (mVData->getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); - if (values->getCount()) { - for (int i = 0; i < values->getCount(); i += 2) { - fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i + 1]); - } - } - } break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); - if (values->getCount()) { - for (int i = 0; i < values->getCount(); i += 2) { - fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i + 1]); - } - } - } break; - } - fprintf(stderr, "\n"); -} -#endif - -void UVDataWrapper::getUV(int uv_index, float *uv) -{ - int stride = mVData->getStride(0); - if (stride == 0) { - stride = 2; - } - - switch (mVData->getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); - if (values->empty()) { - return; - } - uv[0] = (*values)[uv_index * stride]; - uv[1] = (*values)[uv_index * stride + 1]; - - } break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); - if (values->empty()) { - return; - } - uv[0] = (float)(*values)[uv_index * stride]; - uv[1] = (float)(*values)[uv_index * stride + 1]; - - } break; - case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: - default: - fprintf(stderr, "MeshImporter.getUV(): unknown data type\n"); - } -} - -VCOLDataWrapper::VCOLDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) -{ -} - -void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol) -{ - int stride = mVData->getStride(0); - if (stride == 0) { - stride = 3; - } - - switch (mVData->getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); - if (values->empty() || values->getCount() <= (v_index * stride + 2)) { - return; // xxx need to create an error instead - } - - mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]); - mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]); - mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]); - } break; - - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { - COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); - if (values->empty() || values->getCount() <= (v_index * stride + 2)) { - return; // xxx need to create an error instead - } - - mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]); - mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]); - mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]); - } break; - default: - fprintf(stderr, "VCOLDataWrapper.getvcol(): unknown data type\n"); - } -} - -MeshImporter::MeshImporter( - UnitConverter *unitconv, ArmatureImporter *arm, Main *bmain, Scene *sce, ViewLayer *view_layer) - : unitconverter(unitconv), - m_bmain(bmain), - scene(sce), - view_layer(view_layer), - armature_importer(arm) -{ - /* pass */ -} - -bool MeshImporter::set_poly_indices( - MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) -{ - mpoly->loopstart = loop_index; - mpoly->totloop = loop_count; - bool broken_loop = false; - for (int index = 0; index < loop_count; index++) { - - /* Test if loop defines a hole */ - if (!broken_loop) { - for (int i = 0; i < index; i++) { - if (indices[i] == indices[index]) { - // duplicate index -> not good - broken_loop = true; - } - } - } - - mloop->v = indices[index]; - mloop++; - } - return broken_loop; -} - -void MeshImporter::set_vcol(MLoopCol *mlc, - VCOLDataWrapper &vob, - int loop_index, - COLLADAFW::IndexList &index_list, - int count) -{ - int index; - for (index = 0; index < count; index++, mlc++) { - int v_index = index_list.getIndex(index + loop_index); - vob.get_vcol(v_index, mlc); - } -} - -void MeshImporter::set_face_uv(MLoopUV *mloopuv, - UVDataWrapper &uvs, - int start_index, - COLLADAFW::IndexList &index_list, - int count) -{ - // per face vertex indices, this means for quad we have 4 indices, not 8 - COLLADAFW::UIntValuesArray &indices = index_list.getIndices(); - - for (int index = 0; index < count; index++) { - int uv_index = indices[index + start_index]; - uvs.getUV(uv_index, mloopuv[index].uv); - } -} - -#ifdef COLLADA_DEBUG -void MeshImporter::print_index_list(COLLADAFW::IndexList &index_list) -{ - fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str()); - for (int i = 0; i < index_list.getIndicesCount(); i += 2) { - fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1)); - } - fprintf(stderr, "\n"); -} -#endif - -/* checks if mesh has supported primitive types: lines, polylist, triangles, triangle_fans */ -bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) -{ - COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); - - const std::string &name = bc_get_dae_name(mesh); - - for (unsigned int i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType(); - - const char *type_str = bc_primTypeToStr(type); - - // OpenCollada passes POLYGONS type for - if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { - - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; - COLLADAFW::Polygons::VertexCountArray &vca = mpvc->getGroupedVerticesVertexCountArray(); - - int hole_count = 0; - int nonface_count = 0; - - for (unsigned int j = 0; j < vca.getCount(); j++) { - int count = vca[j]; - if (abs(count) < 3) { - nonface_count++; - } - - if (count < 0) { - hole_count++; - } - } - - if (hole_count > 0) { - fprintf(stderr, - "WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n", - type_str, - name.c_str(), - hole_count); - } - - if (nonface_count > 0) { - fprintf(stderr, - "WARNING: Primitive %s in %s: %d faces with vertex count < 3 (rejected)\n", - type_str, - name.c_str(), - nonface_count); - } - } - - else if (type == COLLADAFW::MeshPrimitive::LINES) { - // TODO: Add Checker for line syntax here - } - - else if (type != COLLADAFW::MeshPrimitive::TRIANGLES && - type != COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { - fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str); - return false; - } - } - - return true; -} - -void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) -{ - // vertices - COLLADAFW::MeshVertexData &pos = mesh->getPositions(); - if (pos.empty()) { - return; - } - - int stride = pos.getStride(0); - if (stride == 0) { - stride = 3; - } - - me->totvert = pos.getFloatValues()->getCount() / stride; - me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); - - MVert *mvert; - int i; - - for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { - get_vector(mvert->co, pos, i, stride); - } -} - -// ===================================================================== -// condition 1: The Primitive has normals -// condition 2: The number of normals equals the number of faces. -// return true if both conditions apply. -// return false otherwise. -// ===================================================================== -bool MeshImporter::primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp) -{ - - bool has_useable_normals = false; - - int normals_count = mp->getNormalIndices().getCount(); - if (normals_count > 0) { - int index_count = mp->getPositionIndices().getCount(); - if (index_count == normals_count) { - has_useable_normals = true; - } - else { - fprintf(stderr, - "Warning: Number of normals %d is different from the number of vertices %d, " - "skipping normals\n", - normals_count, - index_count); - } - } - - return has_useable_normals; -} - -// ===================================================================== -// Assume that only TRIANGLES, TRIANGLE_FANS, POLYLIST and POLYGONS -// have faces. (to be verified) -// ===================================================================== -bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp) -{ - - bool has_faces = false; - int type = mp->getPrimitiveType(); - switch (type) { - case COLLADAFW::MeshPrimitive::TRIANGLES: - case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: - case COLLADAFW::MeshPrimitive::POLYLIST: - case COLLADAFW::MeshPrimitive::POLYGONS: { - has_faces = true; - break; - } - default: { - has_faces = false; - break; - } - } - return has_faces; -} - -static std::string extract_vcolname(const COLLADAFW::String &collada_id) -{ - std::string colname = collada_id; - int spos = colname.find("-mesh-colors-"); - if (spos != std::string::npos) { - colname = colname.substr(spos + 13); - } - return colname; -} - -// ================================================================= -// Return the number of faces by summing up -// the facecounts of the parts. -// hint: This is done because mesh->getFacesCount() does -// count loose edges as extra faces, which is not what we want here. -// ================================================================= -void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) -{ - COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives(); - int total_poly_count = 0; - int total_loop_count = 0; - - // collect edge_count and face_count from all parts - for (int i = 0; i < prim_arr.getCount(); i++) { - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - int type = mp->getPrimitiveType(); - switch (type) { - case COLLADAFW::MeshPrimitive::TRIANGLES: - case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: - case COLLADAFW::MeshPrimitive::POLYLIST: - case COLLADAFW::MeshPrimitive::POLYGONS: { - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; - size_t prim_poly_count = mpvc->getFaceCount(); - - size_t prim_loop_count = 0; - for (int index = 0; index < prim_poly_count; index++) { - int vcount = get_vertex_count(mpvc, index); - if (vcount > 0) { - prim_loop_count += vcount; - total_poly_count++; - } - else { - // TODO: this is a hole and not another polygon! - } - } - - total_loop_count += prim_loop_count; - - break; - } - default: - break; - } - } - - // Add the data containers - if (total_poly_count > 0) { - me->totpoly = total_poly_count; - me->totloop = total_loop_count; - me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, me->totpoly); - me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, me->totloop); - - unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount(); - for (int i = 0; i < totuvset; i++) { - if (collada_mesh->getUVCoords().getLength(i) == 0) { - totuvset = 0; - break; - } - } - - if (totuvset > 0) { - for (int i = 0; i < totuvset; i++) { - COLLADAFW::MeshVertexData::InputInfos *info = - collada_mesh->getUVCoords().getInputInfosArray()[i]; - COLLADAFW::String &uvname = info->mName; - // Allocate space for UV_data - CustomData_add_layer_named( - &me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, uvname.c_str()); - } - // activate the first uv map - me->mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0); - } - - int totcolset = collada_mesh->getColors().getInputInfosArray().getCount(); - if (totcolset > 0) { - for (int i = 0; i < totcolset; i++) { - COLLADAFW::MeshVertexData::InputInfos *info = - collada_mesh->getColors().getInputInfosArray()[i]; - COLLADAFW::String colname = extract_vcolname(info->mName); - CustomData_add_layer_named( - &me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, colname.c_str()); - } - me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, 0); - } - } -} - -unsigned int MeshImporter::get_vertex_count(COLLADAFW::Polygons *mp, int index) -{ - int type = mp->getPrimitiveType(); - int result; - switch (type) { - case COLLADAFW::MeshPrimitive::TRIANGLES: - case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: { - result = 3; - break; - } - case COLLADAFW::MeshPrimitive::POLYLIST: - case COLLADAFW::MeshPrimitive::POLYGONS: { - result = mp->getGroupedVerticesVertexCountArray()[index]; - break; - } - default: { - result = -1; - break; - } - } - return result; -} - -unsigned int MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh) -{ - COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); - int loose_edge_count = 0; - - // collect edge_count and face_count from all parts - for (int i = 0; i < prim_arr.getCount(); i++) { - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - int type = mp->getPrimitiveType(); - switch (type) { - case COLLADAFW::MeshPrimitive::LINES: { - size_t prim_totface = mp->getFaceCount(); - loose_edge_count += prim_totface; - break; - } - default: - break; - } - } - return loose_edge_count; -} - -// ================================================================= -// This function is copied from source/blender/editors/mesh/mesh_data.c -// -// TODO: (As discussed with sergey-) : -// Maybe move this function to blenderkernel/intern/mesh.c -// and add definition to BKE_mesh.c -// ================================================================= -void MeshImporter::mesh_add_edges(Mesh *mesh, int len) -{ - CustomData edata; - MEdge *medge; - int totedge; - - if (len == 0) { - return; - } - - totedge = mesh->totedge + len; - - /* update customdata */ - CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge); - CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); - - if (!CustomData_has_layer(&edata, CD_MEDGE)) { - CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); - } - - CustomData_free(&mesh->edata, mesh->totedge); - mesh->edata = edata; - BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */ - - /* set default flags */ - medge = &mesh->medge[mesh->totedge]; - for (int i = 0; i < len; i++, medge++) { - medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT; - } - - mesh->totedge = totedge; -} - -// ================================================================= -// Read all loose edges. -// Important: This function assumes that all edges from existing -// faces have already been generated and added to me->medge -// So this function MUST be called after read_faces() (see below) -// ================================================================= -void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) -{ - unsigned int loose_edge_count = get_loose_edge_count(mesh); - if (loose_edge_count > 0) { - - unsigned int face_edge_count = me->totedge; - /* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */ - - mesh_add_edges(me, loose_edge_count); - MEdge *med = me->medge + face_edge_count; - - COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); - - for (int index = 0; index < prim_arr.getCount(); index++) { - COLLADAFW::MeshPrimitive *mp = prim_arr[index]; - - int type = mp->getPrimitiveType(); - if (type == COLLADAFW::MeshPrimitive::LINES) { - unsigned int edge_count = mp->getFaceCount(); - unsigned int *indices = mp->getPositionIndices().getData(); - - for (int j = 0; j < edge_count; j++, med++) { - med->bweight = 0; - med->crease = 0; - med->flag |= ME_LOOSEEDGE; - med->v1 = indices[2 * j]; - med->v2 = indices[2 * j + 1]; - } - } - } - } -} - -// ======================================================================= -// Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON -// Important: This function MUST be called before read_lines() -// Otherwise we will loose all edges from faces (see read_lines() above) -// -// TODO: import uv set names -// ======================================================================== -void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) -{ - unsigned int i; - - allocate_poly_data(collada_mesh, me); - - UVDataWrapper uvs(collada_mesh->getUVCoords()); - VCOLDataWrapper vcol(collada_mesh->getColors()); - - MPoly *mpoly = me->mpoly; - MLoop *mloop = me->mloop; - int loop_index = 0; - - MaterialIdPrimitiveArrayMap mat_prim_map; - - COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives(); - COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals(); - - for (i = 0; i < prim_arr.getCount(); i++) { - - COLLADAFW::MeshPrimitive *mp = prim_arr[i]; - - // faces - size_t prim_totpoly = mp->getFaceCount(); - unsigned int *position_indices = mp->getPositionIndices().getData(); - unsigned int *normal_indices = mp->getNormalIndices().getData(); - - bool mp_has_normals = primitive_has_useable_normals(mp); - bool mp_has_faces = primitive_has_faces(mp); - - int collada_meshtype = mp->getPrimitiveType(); - - // since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive - Primitive prim = {mpoly, 0}; - - // If MeshPrimitive is TRIANGLE_FANS we split it into triangles - // The first trifan vertex will be the first vertex in every triangle - // XXX The proper function of TRIANGLE_FANS is not tested!!! - // XXX In particular the handling of the normal_indices looks very wrong to me - if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { - unsigned int grouped_vertex_count = mp->getGroupedVertexElementsCount(); - for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) { - unsigned int first_vertex = position_indices[0]; // Store first trifan vertex - unsigned int first_normal = normal_indices[0]; // Store first trifan vertex normal - unsigned int vertex_count = mp->getGroupedVerticesVertexCount(group_index); - - for (unsigned int vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) { - // For each triangle store indices of its 3 vertices - unsigned int triangle_vertex_indices[3] = { - first_vertex, position_indices[1], position_indices[2]}; - set_poly_indices(mpoly, mloop, loop_index, triangle_vertex_indices, 3); - - if (mp_has_normals) { // vertex normals, same implementation as for the triangles - // the same for vertces normals - unsigned int vertex_normal_indices[3] = { - first_normal, normal_indices[1], normal_indices[2]}; - if (!is_flat_face(vertex_normal_indices, nor, 3)) { - mpoly->flag |= ME_SMOOTH; - } - normal_indices++; - } - - mpoly++; - mloop += 3; - loop_index += 3; - prim.totpoly++; - } - - // Moving cursor to the next triangle fan. - if (mp_has_normals) { - normal_indices += 2; - } - - position_indices += 2; - } - } - - if (collada_meshtype == COLLADAFW::MeshPrimitive::POLYLIST || - collada_meshtype == COLLADAFW::MeshPrimitive::POLYGONS || - collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLES) { - COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; - unsigned int start_index = 0; - - COLLADAFW::IndexListArray &index_list_array_uvcoord = mp->getUVCoordIndicesArray(); - COLLADAFW::IndexListArray &index_list_array_vcolor = mp->getColorIndicesArray(); - - int invalid_loop_holes = 0; - for (unsigned int j = 0; j < prim_totpoly; j++) { - - // Vertices in polygon: - int vcount = get_vertex_count(mpvc, j); - if (vcount < 0) { - continue; // TODO: add support for holes - } - - bool broken_loop = set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); - if (broken_loop) { - invalid_loop_holes += 1; - } - - for (unsigned int uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount(); - uvset_index++) { - // get mtface by face index and uv set index - COLLADAFW::IndexList &index_list = *index_list_array_uvcoord[uvset_index]; - MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_named( - &me->ldata, CD_MLOOPUV, index_list.getName().c_str()); - if (mloopuv == NULL) { - fprintf(stderr, - "Collada import: Mesh [%s] : Unknown reference to TEXCOORD [#%s].\n", - me->id.name, - index_list.getName().c_str()); - } - else { - set_face_uv(mloopuv + loop_index, - uvs, - start_index, - *index_list_array_uvcoord[uvset_index], - vcount); - } - } - - if (mp_has_normals) { - if (!is_flat_face(normal_indices, nor, vcount)) { - mpoly->flag |= ME_SMOOTH; - } - } - - if (mp->hasColorIndices()) { - int vcolor_count = index_list_array_vcolor.getCount(); - - for (unsigned int vcolor_index = 0; vcolor_index < vcolor_count; vcolor_index++) { - - COLLADAFW::IndexList &color_index_list = *mp->getColorIndices(vcolor_index); - COLLADAFW::String colname = extract_vcolname(color_index_list.getName()); - MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named( - &me->ldata, CD_MLOOPCOL, colname.c_str()); - if (mloopcol == NULL) { - fprintf(stderr, - "Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n", - me->id.name, - color_index_list.getName().c_str()); - } - else { - set_vcol(mloopcol + loop_index, vcol, start_index, color_index_list, vcount); - } - } - } - - mpoly++; - mloop += vcount; - loop_index += vcount; - start_index += vcount; - prim.totpoly++; - - if (mp_has_normals) { - normal_indices += vcount; - } - - position_indices += vcount; - } - - if (invalid_loop_holes > 0) { - fprintf(stderr, - "Collada import: Mesh [%s] : contains %d unsupported loops (holes).\n", - me->id.name, - invalid_loop_holes); - } - } - - else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) { - continue; // read the lines later after all the rest is done - } - - if (mp_has_faces) { - mat_prim_map[mp->getMaterialId()].push_back(prim); - } - } - - geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map; -} - -void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData &arr, int i, int stride) -{ - i *= stride; - - switch (arr.getType()) { - case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { - COLLADAFW::ArrayPrimitiveType *values = arr.getFloatValues(); - if (values->empty()) { - return; - } - - v[0] = (*values)[i++]; - v[1] = (*values)[i++]; - if (stride >= 3) { - v[2] = (*values)[i]; - } - else { - v[2] = 0.0f; - } - - } break; - case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { - COLLADAFW::ArrayPrimitiveType *values = arr.getDoubleValues(); - if (values->empty()) { - return; - } - - v[0] = (float)(*values)[i++]; - v[1] = (float)(*values)[i++]; - if (stride >= 3) { - v[2] = (float)(*values)[i]; - } - else { - v[2] = 0.0f; - } - } break; - default: - break; - } -} - -bool MeshImporter::is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData &nor, int count) -{ - float a[3], b[3]; - - get_vector(a, nor, *nind, 3); - normalize_v3(a); - - nind++; - - for (int i = 1; i < count; i++, nind++) { - get_vector(b, nor, *nind, 3); - normalize_v3(b); - - float dp = dot_v3v3(a, b); - - if (dp < 0.99999f || dp > 1.00001f) { - return false; - } - } - - return true; -} - -Object *MeshImporter::get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid) -{ - if (uid_object_map.find(geom_uid) != uid_object_map.end()) { - return uid_object_map[geom_uid]; - } - return NULL; -} - -Mesh *MeshImporter::get_mesh_by_geom_uid(const COLLADAFW::UniqueId &mesh_uid) -{ - if (uid_mesh_map.find(mesh_uid) != uid_mesh_map.end()) { - return uid_mesh_map[mesh_uid]; - } - return NULL; -} - -std::string *MeshImporter::get_geometry_name(const std::string &mesh_name) -{ - if (this->mesh_geom_map.find(mesh_name) != this->mesh_geom_map.end()) { - return &this->mesh_geom_map[mesh_name]; - } - return NULL; -} - -/** - * this function checks if both objects have the same - * materials assigned to Object (in the same order) - * returns true if condition matches, otherwise false; - */ -static bool bc_has_same_material_configuration(Object *ob1, Object *ob2) -{ - if (ob1->totcol != ob2->totcol) { - return false; // not same number of materials - } - if (ob1->totcol == 0) { - return false; // no material at all - } - - for (int index = 0; index < ob1->totcol; index++) { - if (ob1->matbits[index] != ob2->matbits[index]) { - return false; // shouldn't happen - } - if (ob1->matbits[index] == 0) { - return false; // shouldn't happen - } - if (ob1->mat[index] != ob2->mat[index]) { - return false; // different material assignment - } - } - return true; -} - -/** - * - * Caution here: This code assumes that all materials are assigned to Object - * and no material is assigned to Data. - * That is true right after the objects have been imported. - * - */ -static void bc_copy_materials_to_data(Object *ob, Mesh *me) -{ - for (int index = 0; index < ob->totcol; index++) { - ob->matbits[index] = 0; - me->mat[index] = ob->mat[index]; - } -} - -/** - * - * Remove all references to materials from the object - * - */ -static void bc_remove_materials_from_object(Object *ob, Mesh *me) -{ - for (int index = 0; index < ob->totcol; index++) { - ob->matbits[index] = 0; - ob->mat[index] = NULL; - } -} - -/** - * Returns the list of Users of the given Mesh object. - * Note: This function uses the object user flag to control - * which objects have already been processed. - */ -std::vector MeshImporter::get_all_users_of(Mesh *reference_mesh) -{ - std::vector mesh_users; - for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); - ++it) { - Object *ob = (*it); - if (bc_is_marked(ob)) { - bc_remove_mark(ob); - Mesh *me = (Mesh *)ob->data; - if (me == reference_mesh) { - mesh_users.push_back(ob); - } - } - } - return mesh_users; -} - -/** - * - * During import all materials have been assigned to Object. - * Now we iterate over the imported objects and optimize - * the assignments as follows: - * - * for each imported geometry: - * if number of users is 1: - * get the user (object) - * move the materials from Object to Data - * else: - * determine which materials are assigned to the first user - * check if all other users have the same materials in the same order - * if the check is positive: - * Add the materials of the first user to the geometry - * adjust all other users accordingly. - * - */ -void MeshImporter::optimize_material_assignements() -{ - for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); - ++it) { - Object *ob = (*it); - Mesh *me = (Mesh *)ob->data; - if (ID_REAL_USERS(&me->id) == 1) { - bc_copy_materials_to_data(ob, me); - bc_remove_materials_from_object(ob, me); - bc_remove_mark(ob); - } - else if (ID_REAL_USERS(&me->id) > 1) { - bool can_move = true; - std::vector mesh_users = get_all_users_of(me); - if (mesh_users.size() > 1) { - Object *ref_ob = mesh_users[0]; - for (int index = 1; index < mesh_users.size(); index++) { - if (!bc_has_same_material_configuration(ref_ob, mesh_users[index])) { - can_move = false; - break; - } - } - if (can_move) { - bc_copy_materials_to_data(ref_ob, me); - for (int index = 0; index < mesh_users.size(); index++) { - Object *object = mesh_users[index]; - bc_remove_materials_from_object(object, me); - bc_remove_mark(object); - } - } - } - } - } -} - -/** - * We do not know in advance which objects will share geometries. - * And we do not know either if the objects which share geometries - * come along with different materials. So we first create the objects - * and assign the materials to Object, then in a later cleanup we decide - * which materials shall be moved to the created geometries. Also see - * optimize_material_assignements() above. - */ -void MeshImporter::assign_material_to_geom( - COLLADAFW::MaterialBinding cmaterial, - std::map &uid_material_map, - Object *ob, - const COLLADAFW::UniqueId *geom_uid, - short mat_index) -{ - const COLLADAFW::UniqueId &ma_uid = cmaterial.getReferencedMaterial(); - - // do we know this material? - if (uid_material_map.find(ma_uid) == uid_material_map.end()) { - - fprintf(stderr, "Cannot find material by UID.\n"); - return; - } - - // first time we get geom_uid, ma_uid pair. Save for later check. - materials_mapped_to_geom.insert( - std::pair(*geom_uid, ma_uid)); - - Material *ma = uid_material_map[ma_uid]; - - // Attention! This temporarily assigns material to object on purpose! - // See note above. - ob->actcol = 0; - BKE_object_material_assign(m_bmain, ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT); - - MaterialIdPrimitiveArrayMap &mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; - COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId(); - - // assign material indices to mesh faces - if (mat_prim_map.find(mat_id) != mat_prim_map.end()) { - - std::vector &prims = mat_prim_map[mat_id]; - - std::vector::iterator it; - - for (it = prims.begin(); it != prims.end(); it++) { - Primitive &prim = *it; - MPoly *mpoly = prim.mpoly; - - for (int i = 0; i < prim.totpoly; i++, mpoly++) { - mpoly->mat_nr = mat_index; - } - } - } -} - -Object *MeshImporter::create_mesh_object( - COLLADAFW::Node *node, - COLLADAFW::InstanceGeometry *geom, - bool isController, - std::map &uid_material_map) -{ - const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId(); - - // check if node instantiates controller or geometry - if (isController) { - - geom_uid = armature_importer->get_geometry_uid(*geom_uid); - - if (!geom_uid) { - fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n"); - return NULL; - } - } - else { - - if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) { - // this could happen if a mesh was not created - // (e.g. if it contains unsupported geometry) - fprintf(stderr, "Couldn't find a mesh by UID.\n"); - return NULL; - } - } - if (!uid_mesh_map[*geom_uid]) { - return NULL; - } - - // name Object - const std::string &id = node->getName().size() ? node->getName() : node->getOriginalId(); - const char *name = (id.length()) ? id.c_str() : NULL; - - // add object - Object *ob = bc_add_object(m_bmain, scene, view_layer, OB_MESH, name); - bc_set_mark(ob); // used later for material assignment optimization - - // store object pointer for ArmatureImporter - uid_object_map[*geom_uid] = ob; - imported_objects.push_back(ob); - - // replace ob->data freeing the old one - Mesh *old_mesh = (Mesh *)ob->data; - Mesh *new_mesh = uid_mesh_map[*geom_uid]; - - BKE_mesh_assign_object(m_bmain, ob, new_mesh); - BKE_mesh_calc_normals(new_mesh); - - /* Because BKE_mesh_assign_object would have already decreased it... */ - id_us_plus(&old_mesh->id); - - BKE_id_free_us(m_bmain, old_mesh); - - COLLADAFW::MaterialBindingArray &mat_array = geom->getMaterialBindings(); - - // loop through geom's materials - for (unsigned int i = 0; i < mat_array.getCount(); i++) { - - if (mat_array[i].getReferencedMaterial().isValid()) { - assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, i); - } - else { - fprintf(stderr, "invalid referenced material for %s\n", mat_array[i].getName().c_str()); - } - } - - // clean up the mesh - BKE_mesh_validate((Mesh *)ob->data, false, false); - - return ob; -} - -// create a mesh storing a pointer in a map so it can be retrieved later by geometry UID -bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) -{ - - if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { - // TODO: report warning - fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType())); - return true; - } - - COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh *)geom; - - if (!is_nice_mesh(mesh)) { - fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh).c_str()); - return true; - } - - const std::string &str_geom_id = mesh->getName().size() ? mesh->getName() : - mesh->getOriginalId(); - Mesh *me = BKE_mesh_add(m_bmain, (char *)str_geom_id.c_str()); - id_us_min(&me->id); // is already 1 here, but will be set later in BKE_mesh_assign_object - - // store the Mesh pointer to link it later with an Object - // mesh_geom_map needed to map mesh to its geometry name (for shape key naming) - this->uid_mesh_map[mesh->getUniqueId()] = me; - this->mesh_geom_map[std::string(me->id.name)] = str_geom_id; - - read_vertices(mesh, me); - read_polys(mesh, me); - BKE_mesh_calc_edges(me, false, false); - // read_lines() must be called after the face edges have been generated. - // Otherwise the loose edges will be silently deleted again. - read_lines(mesh, me); - - return true; -} diff --git a/source/blender/collada/MeshImporter.h b/source/blender/collada/MeshImporter.h deleted file mode 100644 index 9517587013d..00000000000 --- a/source/blender/collada/MeshImporter.h +++ /dev/null @@ -1,182 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __MESHIMPORTER_H__ -#define __MESHIMPORTER_H__ - -#include -#include - -#include "COLLADAFWIndexList.h" -#include "COLLADAFWPolygons.h" -#include "COLLADAFWInstanceGeometry.h" -#include "COLLADAFWMaterialBinding.h" -#include "COLLADAFWMesh.h" -#include "COLLADAFWMeshVertexData.h" -#include "COLLADAFWNode.h" -#include "COLLADAFWTextureCoordinateBinding.h" -#include "COLLADAFWTypes.h" -#include "COLLADAFWUniqueId.h" - -#include "ArmatureImporter.h" -#include "collada_utils.h" - -extern "C" { -#include "BLI_edgehash.h" -#include "DNA_material_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -} - -/* only for ArmatureImporter to "see" MeshImporter::get_object_by_geom_uid */ -class MeshImporterBase { - public: - virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid) = 0; - virtual Mesh *get_mesh_by_geom_uid(const COLLADAFW::UniqueId &mesh_uid) = 0; - virtual std::string *get_geometry_name(const std::string &mesh_name) = 0; -}; - -class UVDataWrapper { - COLLADAFW::MeshVertexData *mVData; - - public: - UVDataWrapper(COLLADAFW::MeshVertexData &vdata); - -#ifdef COLLADA_DEBUG - void print(); -#endif - - void getUV(int uv_index, float *uv); -}; - -class VCOLDataWrapper { - COLLADAFW::MeshVertexData *mVData; - - public: - VCOLDataWrapper(COLLADAFW::MeshVertexData &vdata); - void get_vcol(int v_index, MLoopCol *mloopcol); -}; - -class MeshImporter : public MeshImporterBase { - private: - UnitConverter *unitconverter; - - Main *m_bmain; - Scene *scene; - ViewLayer *view_layer; - - ArmatureImporter *armature_importer; - - std::map mesh_geom_map; /* needed for correct shape key naming */ - std::map uid_mesh_map; /* geometry unique id-to-mesh map */ - std::map uid_object_map; /* geom uid-to-object */ - std::vector imported_objects; /* list of imported objects */ - - /* this structure is used to assign material indices to polygons - * it holds a portion of Mesh faces and corresponds to a DAE primitive list - * (, , etc.) */ - struct Primitive { - MPoly *mpoly; - unsigned int totpoly; - }; - typedef std::map> MaterialIdPrimitiveArrayMap; - /* crazy name! */ - std::map geom_uid_mat_mapping_map; - /* < materials that have already been mapped to a geometry. - * A pair/of geom uid and mat uid, one geometry can have several materials */ - std::multimap materials_mapped_to_geom; - - bool set_poly_indices( - MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count); - - void set_face_uv(MLoopUV *mloopuv, - UVDataWrapper &uvs, - int loop_index, - COLLADAFW::IndexList &index_list, - int count); - - void set_vcol(MLoopCol *mloopcol, - VCOLDataWrapper &vob, - int loop_index, - COLLADAFW::IndexList &index_list, - int count); - -#ifdef COLLADA_DEBUG - void print_index_list(COLLADAFW::IndexList &index_list); -#endif - - bool is_nice_mesh(COLLADAFW::Mesh *mesh); - - void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me); - - bool primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp); - bool primitive_has_faces(COLLADAFW::MeshPrimitive *mp); - - static void mesh_add_edges(Mesh *mesh, int len); - - unsigned int get_loose_edge_count(COLLADAFW::Mesh *mesh); - - CustomData create_edge_custom_data(EdgeHash *eh); - - void allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me); - - /* TODO: import uv set names */ - void read_polys(COLLADAFW::Mesh *mesh, Mesh *me); - void read_lines(COLLADAFW::Mesh *mesh, Mesh *me); - unsigned int get_vertex_count(COLLADAFW::Polygons *mp, int index); - - void get_vector(float v[3], COLLADAFW::MeshVertexData &arr, int i, int stride); - - bool is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData &nor, int count); - - std::vector get_all_users_of(Mesh *reference_mesh); - - public: - MeshImporter(UnitConverter *unitconv, - ArmatureImporter *arm, - Main *bmain, - Scene *sce, - ViewLayer *view_layer); - - virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid); - - virtual Mesh *get_mesh_by_geom_uid(const COLLADAFW::UniqueId &geom_uid); - - void optimize_material_assignements(); - - void assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, - std::map &uid_material_map, - Object *ob, - const COLLADAFW::UniqueId *geom_uid, - short mat_index); - - Object *create_mesh_object(COLLADAFW::Node *node, - COLLADAFW::InstanceGeometry *geom, - bool isController, - std::map &uid_material_map); - - /* create a mesh storing a pointer in a map so it can be retrieved later by geometry UID */ - bool write_geometry(const COLLADAFW::Geometry *geom); - std::string *get_geometry_name(const std::string &mesh_name); -}; - -#endif diff --git a/source/blender/collada/SceneExporter.cpp b/source/blender/collada/SceneExporter.cpp deleted file mode 100644 index 42901bd2a4a..00000000000 --- a/source/blender/collada/SceneExporter.cpp +++ /dev/null @@ -1,242 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -extern "C" { -#include "BLI_utildefines.h" -#include "BKE_collection.h" -#include "BKE_object.h" -#include "BLI_listbase.h" -#include "BKE_lib_id.h" -} - -#include "SceneExporter.h" -#include "collada_utils.h" -#include "BCSampleData.h" - -void SceneExporter::exportScene() -{ - Scene *scene = blender_context.get_scene(); - - /* */ - std::string name = id_name(scene); - openVisualScene(translate_id(name), encode_xml(name)); - exportHierarchy(); - closeVisualScene(); - closeLibrary(); -} - -void SceneExporter::exportHierarchy() -{ - LinkNode *node; - ColladaBaseNodes base_objects; - - /* Ensure all objects in the export_set are marked */ - for (node = this->export_settings.get_export_set(); node; node = node->next) { - Object *ob = (Object *)node->link; - ob->id.tag |= LIB_TAG_DOIT; - } - - /* Now find all exportable base objects (highest in export hierarchy) */ - for (node = this->export_settings.get_export_set(); node; node = node->next) { - Object *ob = (Object *)node->link; - if (this->export_settings.is_export_root(ob)) { - switch (ob->type) { - case OB_MESH: - case OB_CAMERA: - case OB_LAMP: - case OB_EMPTY: - case OB_GPENCIL: - case OB_ARMATURE: - base_objects.add(ob); - break; - } - } - } - - /* And now export the base objects: */ - for (int index = 0; index < base_objects.size(); index++) { - Object *ob = base_objects.get(index); - writeNode(ob); - if (bc_is_marked(ob)) { - bc_remove_mark(ob); - } - } -} - -void SceneExporter::writeNodeList(std::vector &child_objects, Object *parent) -{ - /* TODO: Handle the case where a parent is not exported - * Actually i am not even sure if this can be done at all - * in a good way. - * I really prefer to enforce the export of hidden - * elements in an object hierarchy. When the children of - * the hidden elements are exported as well. */ - for (int i = 0; i < child_objects.size(); i++) { - Object *child = child_objects[i]; - writeNode(child); - if (bc_is_marked(child)) { - bc_remove_mark(child); - } - } -} - -void SceneExporter::writeNode(Object *ob) -{ - ViewLayer *view_layer = blender_context.get_view_layer(); - - std::vector child_objects; - bc_get_children(child_objects, ob, view_layer); - bool can_export = bc_is_in_Export_set(this->export_settings.get_export_set(), ob, view_layer); - - /* Add associated armature first if available */ - bool armature_exported = false; - Object *ob_arm = bc_get_assigned_armature(ob); - - if (ob_arm != NULL) { - armature_exported = bc_is_in_Export_set( - this->export_settings.get_export_set(), ob_arm, view_layer); - if (armature_exported && bc_is_marked(ob_arm)) { - writeNode(ob_arm); - bc_remove_mark(ob_arm); - armature_exported = true; - } - } - - if (can_export) { - COLLADASW::Node colladaNode(mSW); - colladaNode.setNodeId(translate_id(id_name(ob))); - colladaNode.setNodeName(encode_xml(id_name(ob))); - colladaNode.setType(COLLADASW::Node::NODE); - - colladaNode.start(); - if (ob->type == OB_MESH && armature_exported) { - /* for skinned mesh we write obmat in */ - TransformWriter::add_node_transform_identity(colladaNode, this->export_settings); - } - else { - TransformWriter::add_node_transform_ob(colladaNode, ob, this->export_settings); - } - - /* */ - if (ob->type == OB_MESH) { - bool instance_controller_created = false; - if (armature_exported) { - instance_controller_created = arm_exporter->add_instance_controller(ob); - } - if (!instance_controller_created) { - COLLADASW::InstanceGeometry instGeom(mSW); - instGeom.setUrl(COLLADASW::URI( - COLLADABU::Utils::EMPTY_STRING, - get_geometry_id(ob, this->export_settings.get_use_object_instantiation()))); - instGeom.setName(encode_xml(id_name(ob))); - InstanceWriter::add_material_bindings( - instGeom.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); - instGeom.add(); - } - } - - /* */ - else if (ob->type == OB_ARMATURE) { - arm_exporter->add_armature_bones(ob, view_layer, this, child_objects); - } - - /* */ - else if (ob->type == OB_CAMERA) { - COLLADASW::InstanceCamera instCam( - mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob))); - instCam.add(); - } - - /* */ - else if (ob->type == OB_LAMP) { - COLLADASW::InstanceLight instLa( - mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob))); - instLa.add(); - } - - /* empty object */ - else if (ob->type == OB_EMPTY) { /* TODO: handle groups (OB_DUPLICOLLECTION */ - if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->instance_collection) { - Collection *collection = ob->instance_collection; - /* printf("group detected '%s'\n", group->id.name + 2); */ - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) { - printf("\t%s\n", object->id.name); - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - } - - if (BLI_listbase_is_empty(&ob->constraints) == false) { - bConstraint *con = (bConstraint *)ob->constraints.first; - while (con) { - std::string con_name(encode_xml(con->name)); - std::string con_tag = con_name + "_constraint"; - printf("%s\n", con_name.c_str()); - printf("%s\n\n", con_tag.c_str()); - colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "type", con->type); - colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "enforce", con->enforce); - colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "flag", con->flag); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "headtail", con->headtail); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "lin_error", con->lin_error); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "own_space", con->ownspace); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "rot_error", con->rot_error); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "tar_space", con->tarspace); - colladaNode.addExtraTechniqueChildParameter( - "blender", con_tag, "lin_error", con->lin_error); - - /* not ideal: add the target object name as another parameter. - * No real mapping in the .dae - * Need support for multiple target objects also. */ - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - ListBase targets = {NULL, NULL}; - if (cti && cti->get_constraint_targets) { - - bConstraintTarget *ct; - Object *obtar; - - cti->get_constraint_targets(con, &targets); - - for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) { - obtar = ct->tar; - std::string tar_id((obtar) ? id_name(obtar) : ""); - colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "target_id", tar_id); - } - - if (cti->flush_constraint_targets) { - cti->flush_constraint_targets(con, &targets, 1); - } - } - - con = con->next; - } - } - } - bc_remove_mark(ob); - writeNodeList(child_objects, ob); - colladaNode.end(); - } - else { - writeNodeList(child_objects, ob); - } -} diff --git a/source/blender/collada/SceneExporter.h b/source/blender/collada/SceneExporter.h deleted file mode 100644 index a61d045ad5d..00000000000 --- a/source/blender/collada/SceneExporter.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __SCENEEXPORTER_H__ -#define __SCENEEXPORTER_H__ - -#include -#include -#include - -extern "C" { -#include "DNA_scene_types.h" -#include "DNA_object_types.h" -#include "DNA_collection_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_mesh_types.h" -#include "DNA_image_types.h" -#include "DNA_material_types.h" -#include "DNA_texture_types.h" -#include "DNA_anim_types.h" -#include "DNA_action_types.h" -#include "DNA_curve_types.h" -#include "DNA_constraint_types.h" -#include "DNA_armature_types.h" -#include "DNA_modifier_types.h" -#include "DNA_userdef_types.h" - -#include "BKE_fcurve.h" -#include "BKE_animsys.h" -#include "BLI_path_util.h" -#include "BKE_constraint.h" -#include "BLI_fileops.h" -#include "ED_keyframing.h" -} - -#include "COLLADASWAsset.h" -#include "COLLADASWLibraryVisualScenes.h" -#include "COLLADASWNode.h" -#include "COLLADASWSource.h" -#include "COLLADASWInstanceGeometry.h" -#include "COLLADASWInputList.h" -#include "COLLADASWPrimitves.h" -#include "COLLADASWVertices.h" -#include "COLLADASWLibraryAnimations.h" -#include "COLLADASWLibraryImages.h" -#include "COLLADASWLibraryEffects.h" -#include "COLLADASWImage.h" -#include "COLLADASWEffectProfile.h" -#include "COLLADASWColorOrTexture.h" -#include "COLLADASWParamTemplate.h" -#include "COLLADASWParamBase.h" -#include "COLLADASWSurfaceInitOption.h" -#include "COLLADASWSampler.h" -#include "COLLADASWScene.h" -#include "COLLADASWTechnique.h" -#include "COLLADASWTexture.h" -#include "COLLADASWLibraryMaterials.h" -#include "COLLADASWBindMaterial.h" -#include "COLLADASWInstanceCamera.h" -#include "COLLADASWInstanceLight.h" -#include "COLLADASWConstants.h" -#include "COLLADASWLibraryControllers.h" -#include "COLLADASWInstanceController.h" -#include "COLLADASWInstanceNode.h" -#include "COLLADASWBaseInputElement.h" - -#include "ArmatureExporter.h" -#include "ExportSettings.h" - -extern void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer); - -class SceneExporter : COLLADASW::LibraryVisualScenes, - protected TransformWriter, - protected InstanceWriter { - public: - SceneExporter(BlenderContext &blender_context, - COLLADASW::StreamWriter *sw, - ArmatureExporter *arm, - BCExportSettings &export_settings) - : COLLADASW::LibraryVisualScenes(sw), - blender_context(blender_context), - arm_exporter(arm), - export_settings(export_settings) - { - } - - void exportScene(); - - private: - BlenderContext &blender_context; - friend class ArmatureExporter; - ArmatureExporter *arm_exporter; - BCExportSettings &export_settings; - - void exportHierarchy(); - void writeNodeList(std::vector &child_objects, Object *parent); - void writeNode(Object *ob); -}; - -#endif diff --git a/source/blender/collada/SkinInfo.cpp b/source/blender/collada/SkinInfo.cpp deleted file mode 100644 index d8804a1e831..00000000000 --- a/source/blender/collada/SkinInfo.cpp +++ /dev/null @@ -1,357 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include - -#if !defined(WIN32) -# include -#endif - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_compiler_attrs.h" - -#include "DNA_armature_types.h" -#include "DNA_modifier_types.h" -#include "DNA_scene_types.h" - -#include "BKE_action.h" -#include "BKE_object.h" -#include "BKE_object_deform.h" - -#include "ED_mesh.h" -#include "ED_object.h" - -#include "SkinInfo.h" -#include "collada_utils.h" - -/* use name, or fall back to original id if name not present (name is optional) */ -template static const char *bc_get_joint_name(T *node) -{ - const std::string &id = node->getName(); - return id.size() ? id.c_str() : node->getOriginalId().c_str(); -} - -/* This is used to store data passed in write_controller_data. - * Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members - * so that arrays don't get freed until we free them explicitly. */ -SkinInfo::SkinInfo() -{ - /* pass */ -} - -SkinInfo::SkinInfo(const SkinInfo &skin) - : weights(skin.weights), - joint_data(skin.joint_data), - unit_converter(skin.unit_converter), - ob_arm(skin.ob_arm), - controller_uid(skin.controller_uid), - parent(skin.parent) -{ - copy_m4_m4(bind_shape_matrix, (float(*)[4])skin.bind_shape_matrix); - - transfer_uint_array_data_const(skin.joints_per_vertex, joints_per_vertex); - transfer_uint_array_data_const(skin.weight_indices, weight_indices); - transfer_int_array_data_const(skin.joint_indices, joint_indices); -} - -SkinInfo::SkinInfo(UnitConverter *conv) : unit_converter(conv), ob_arm(NULL), parent(NULL) -{ -} - -/* nobody owns the data after this, so it should be freed manually with releaseMemory */ -template void SkinInfo::transfer_array_data(T &src, T &dest) -{ - dest.setData(src.getData(), src.getCount()); - src.yieldOwnerShip(); - dest.yieldOwnerShip(); -} - -/* when src is const we cannot src.yieldOwnerShip, this is used by copy constructor */ -void SkinInfo::transfer_int_array_data_const(const COLLADAFW::IntValuesArray &src, - COLLADAFW::IntValuesArray &dest) -{ - dest.setData((int *)src.getData(), src.getCount()); - dest.yieldOwnerShip(); -} - -void SkinInfo::transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray &src, - COLLADAFW::UIntValuesArray &dest) -{ - dest.setData((unsigned int *)src.getData(), src.getCount()); - dest.yieldOwnerShip(); -} - -void SkinInfo::borrow_skin_controller_data(const COLLADAFW::SkinControllerData *skin) -{ - transfer_array_data((COLLADAFW::UIntValuesArray &)skin->getJointsPerVertex(), joints_per_vertex); - transfer_array_data((COLLADAFW::UIntValuesArray &)skin->getWeightIndices(), weight_indices); - transfer_array_data((COLLADAFW::IntValuesArray &)skin->getJointIndices(), joint_indices); - // transfer_array_data(skin->getWeights(), weights); - - /* cannot transfer data for FloatOrDoubleArray, copy values manually */ - const COLLADAFW::FloatOrDoubleArray &weight = skin->getWeights(); - for (unsigned int i = 0; i < weight.getValuesCount(); i++) { - weights.push_back(bc_get_float_value(weight, i)); - } - - unit_converter->dae_matrix_to_mat4_(bind_shape_matrix, skin->getBindShapeMatrix()); -} - -void SkinInfo::free() -{ - joints_per_vertex.releaseMemory(); - weight_indices.releaseMemory(); - joint_indices.releaseMemory(); - // weights.releaseMemory(); -} - -/* using inverse bind matrices to construct armature - * it is safe to invert them to get the original matrices - * because if they are inverse matrices, they can be inverted */ -void SkinInfo::add_joint(const COLLADABU::Math::Matrix4 &matrix) -{ - JointData jd; - unit_converter->dae_matrix_to_mat4_(jd.inv_bind_mat, matrix); - joint_data.push_back(jd); -} - -void SkinInfo::set_controller(const COLLADAFW::SkinController *co) -{ - controller_uid = co->getUniqueId(); - - /* fill in joint UIDs */ - const COLLADAFW::UniqueIdArray &joint_uids = co->getJoints(); - for (unsigned int i = 0; i < joint_uids.getCount(); i++) { - joint_data[i].joint_uid = joint_uids[i]; - - /* store armature pointer */ - // JointData& jd = joint_index_to_joint_info_map[i]; - // jd.ob_arm = ob_arm; - - /* now we'll be able to get inv bind matrix from joint id */ - // joint_id_to_joint_index_map[joint_ids[i]] = i; - } -} - -/* called from write_controller */ -Object *SkinInfo::create_armature(Main *bmain, Scene *scene, ViewLayer *view_layer) -{ - ob_arm = bc_add_object(bmain, scene, view_layer, OB_ARMATURE, NULL); - return ob_arm; -} - -Object *SkinInfo::set_armature(Object *ob_arm) -{ - if (this->ob_arm) { - return this->ob_arm; - } - - this->ob_arm = ob_arm; - return ob_arm; -} - -bool SkinInfo::get_joint_inv_bind_matrix(float inv_bind_mat[4][4], COLLADAFW::Node *node) -{ - const COLLADAFW::UniqueId &uid = node->getUniqueId(); - std::vector::iterator it; - for (it = joint_data.begin(); it != joint_data.end(); it++) { - if ((*it).joint_uid == uid) { - copy_m4_m4(inv_bind_mat, (*it).inv_bind_mat); - return true; - } - } - - return false; -} - -Object *SkinInfo::BKE_armature_from_object() -{ - return ob_arm; -} - -const COLLADAFW::UniqueId &SkinInfo::get_controller_uid() -{ - return controller_uid; -} - -/* check if this skin controller references a joint or any descendant of it - * - * some nodes may not be referenced by SkinController, - * in this case to determine if the node belongs to this armature, - * we need to search down the tree */ -bool SkinInfo::uses_joint_or_descendant(COLLADAFW::Node *node) -{ - const COLLADAFW::UniqueId &uid = node->getUniqueId(); - std::vector::iterator it; - for (it = joint_data.begin(); it != joint_data.end(); it++) { - if ((*it).joint_uid == uid) { - return true; - } - } - - COLLADAFW::NodePointerArray &children = node->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (uses_joint_or_descendant(children[i])) { - return true; - } - } - - return false; -} - -void SkinInfo::link_armature(bContext *C, - Object *ob, - std::map &joint_by_uid, - TransformReader *tm) -{ - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - - ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature); - ArmatureModifierData *amd = (ArmatureModifierData *)md; - amd->object = ob_arm; - -#if 1 - /* XXX Why do we enforce objects to be children of Armatures if they weren't so before ?*/ - if (!BKE_object_is_child_recursive(ob_arm, ob)) { - bc_set_parent(ob, ob_arm, C); - } -#else - Object workob; - ob->parent = ob_arm; - ob->partype = PAROBJECT; - - BKE_object_workob_calc_parent(scene, ob, &workob); - invert_m4_m4(ob->parentinv, workob.obmat); - - DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); -#endif - copy_m4_m4(ob->obmat, bind_shape_matrix); - BKE_object_apply_mat4(ob, ob->obmat, 0, 0); - - amd->deformflag = ARM_DEF_VGROUP; - - /* create all vertex groups */ - std::vector::iterator it; - int joint_index; - for (it = joint_data.begin(), joint_index = 0; it != joint_data.end(); it++, joint_index++) { - const char *name = "Group"; - - /* skip joints that have invalid UID */ - if ((*it).joint_uid == COLLADAFW::UniqueId::INVALID) { - continue; - } - - /* name group by joint node name */ - - if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) { - name = bc_get_joint_name(joint_by_uid[(*it).joint_uid]); - } - - BKE_object_defgroup_add_name(ob, name); - } - - /* - number of joints per vertex - joints_per_vertex - * - [[bone index, weight index] * joints per vertex] * vertices - weight indices - * ^ bone index can be -1 meaning weight toward bind shape, how to express this in Blender? - * - * for each vertex in weight indices - * for each bone index in vertex - * add vertex to group at group index - * treat group index -1 specially - * - * get def group by index with BLI_findlink */ - - for (unsigned int vertex = 0, weight = 0; vertex < joints_per_vertex.getCount(); vertex++) { - - unsigned int limit = weight + joints_per_vertex[vertex]; - for (; weight < limit; weight++) { - int joint = joint_indices[weight], joint_weight = weight_indices[weight]; - - /* -1 means "weight towards the bind shape", we just don't assign it to any group */ - if (joint != -1) { - bDeformGroup *def = (bDeformGroup *)BLI_findlink(&ob->defbase, joint); - - ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE); - } - } - } -} - -bPoseChannel *SkinInfo::get_pose_channel_from_node(COLLADAFW::Node *node) -{ - return BKE_pose_channel_find_name(ob_arm->pose, bc_get_joint_name(node)); -} - -void SkinInfo::set_parent(Object *_parent) -{ - parent = _parent; -} - -Object *SkinInfo::get_parent() -{ - return parent; -} - -void SkinInfo::find_root_joints(const std::vector &root_joints, - std::map &joint_by_uid, - std::vector &result) -{ - std::vector::const_iterator it; - /* for each root_joint */ - for (it = root_joints.begin(); it != root_joints.end(); it++) { - COLLADAFW::Node *root = *it; - std::vector::iterator ji; - /* for each joint_data in this skin */ - for (ji = joint_data.begin(); ji != joint_data.end(); ji++) { - if (joint_by_uid.find((*ji).joint_uid) != joint_by_uid.end()) { - /* get joint node from joint map */ - COLLADAFW::Node *joint = joint_by_uid[(*ji).joint_uid]; - - /* find if joint node is in the tree belonging to the root_joint */ - if (find_node_in_tree(joint, root)) { - if (std::find(result.begin(), result.end(), root) == result.end()) { - result.push_back(root); - } - } - } - } - } -} - -bool SkinInfo::find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root) -{ - if (node == tree_root) { - return true; - } - - COLLADAFW::NodePointerArray &children = tree_root->getChildNodes(); - for (unsigned int i = 0; i < children.getCount(); i++) { - if (find_node_in_tree(node, children[i])) { - return true; - } - } - - return false; -} diff --git a/source/blender/collada/SkinInfo.h b/source/blender/collada/SkinInfo.h deleted file mode 100644 index 255d6d9b1f3..00000000000 --- a/source/blender/collada/SkinInfo.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __SKININFO_H__ -#define __SKININFO_H__ - -#include -#include - -#include "COLLADAFWUniqueId.h" -#include "COLLADAFWTypes.h" -#include "COLLADAFWNode.h" -#include "COLLADAFWSkinController.h" -#include "COLLADAFWSkinControllerData.h" - -#include "DNA_object_types.h" -#include "BKE_context.h" - -#include "TransformReader.h" -#include "collada_internal.h" - -// This is used to store data passed in write_controller_data. -// Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members -// so that arrays don't get freed until we free them explicitly. -class SkinInfo { - private: - // to build armature bones from inverse bind matrices - struct JointData { - float inv_bind_mat[4][4]; // joint inverse bind matrix - COLLADAFW::UniqueId joint_uid; // joint node UID - // Object *ob_arm; // armature object - }; - - float bind_shape_matrix[4][4]; - - // data from COLLADAFW::SkinControllerData, each array should be freed - COLLADAFW::UIntValuesArray joints_per_vertex; - COLLADAFW::UIntValuesArray weight_indices; - COLLADAFW::IntValuesArray joint_indices; - // COLLADAFW::FloatOrDoubleArray weights; - std::vector weights; - - std::vector joint_data; // index to this vector is joint index - - UnitConverter *unit_converter; - - Object *ob_arm; - COLLADAFW::UniqueId controller_uid; - Object *parent; - - public: - SkinInfo(); - SkinInfo(const SkinInfo &skin); - SkinInfo(UnitConverter *conv); - - // nobody owns the data after this, so it should be freed manually with releaseMemory - template void transfer_array_data(T &src, T &dest); - - // when src is const we cannot src.yieldOwnerShip, this is used by copy constructor - void transfer_int_array_data_const(const COLLADAFW::IntValuesArray &src, - COLLADAFW::IntValuesArray &dest); - - void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray &src, - COLLADAFW::UIntValuesArray &dest); - - void borrow_skin_controller_data(const COLLADAFW::SkinControllerData *skin); - - void free(); - - // using inverse bind matrices to construct armature - // it is safe to invert them to get the original matrices - // because if they are inverse matrices, they can be inverted - void add_joint(const COLLADABU::Math::Matrix4 &matrix); - - void set_controller(const COLLADAFW::SkinController *co); - - // called from write_controller - Object *create_armature(Main *bmain, Scene *scene, ViewLayer *view_layer); - - Object *set_armature(Object *ob_arm); - - bool get_joint_inv_bind_matrix(float inv_bind_mat[4][4], COLLADAFW::Node *node); - - Object *BKE_armature_from_object(); - - const COLLADAFW::UniqueId &get_controller_uid(); - - // check if this skin controller references a joint or any descendant of it - // - // some nodes may not be referenced by SkinController, - // in this case to determine if the node belongs to this armature, - // we need to search down the tree - bool uses_joint_or_descendant(COLLADAFW::Node *node); - - void link_armature(bContext *C, - Object *ob, - std::map &joint_by_uid, - TransformReader *tm); - - bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node); - - void set_parent(Object *_parent); - - Object *get_parent(); - - void find_root_joints(const std::vector &root_joints, - std::map &joint_by_uid, - std::vector &result); - - bool find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root); -}; - -#endif diff --git a/source/blender/collada/TransformReader.cpp b/source/blender/collada/TransformReader.cpp deleted file mode 100644 index 8ee31f80405..00000000000 --- a/source/blender/collada/TransformReader.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "TransformReader.h" - -TransformReader::TransformReader(UnitConverter *conv) : unit_converter(conv) -{ - /* pass */ -} - -void TransformReader::get_node_mat(float mat[4][4], - COLLADAFW::Node *node, - std::map *animation_map, - Object *ob) -{ - get_node_mat(mat, node, animation_map, ob, NULL); -} - -void TransformReader::get_node_mat(float mat[4][4], - COLLADAFW::Node *node, - std::map *animation_map, - Object *ob, - float parent_mat[4][4]) -{ - float cur[4][4]; - float copy[4][4]; - - unit_m4(mat); - - for (unsigned int i = 0; i < node->getTransformations().getCount(); i++) { - - COLLADAFW::Transformation *tm = node->getTransformations()[i]; - COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); - - switch (type) { - case COLLADAFW::Transformation::MATRIX: - // When matrix AND Trans/Rot/Scale are defined for a node, - // then this is considered as redundant information. - // So if we find a Matrix we use that and return. - dae_matrix_to_mat4(tm, mat); - if (parent_mat) { - mul_m4_m4m4(mat, parent_mat, mat); - } - return; - case COLLADAFW::Transformation::TRANSLATE: - dae_translate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::ROTATE: - dae_rotate_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::SCALE: - dae_scale_to_mat4(tm, cur); - break; - case COLLADAFW::Transformation::LOOKAT: - fprintf(stderr, "|! LOOKAT transformations are not supported yet.\n"); - break; - case COLLADAFW::Transformation::SKEW: - fprintf(stderr, "|! SKEW transformations are not supported yet.\n"); - break; - } - - copy_m4_m4(copy, mat); - mul_m4_m4m4(mat, copy, cur); - - if (animation_map) { - // AnimationList that drives this Transformation - const COLLADAFW::UniqueId &anim_list_id = tm->getAnimationList(); - - // store this so later we can link animation data with ob - Animation anim = {ob, node, tm}; - (*animation_map)[anim_list_id] = anim; - } - } - - if (parent_mat) { - mul_m4_m4m4(mat, parent_mat, mat); - } -} - -void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) -{ - COLLADAFW::Rotate *ro = (COLLADAFW::Rotate *)tm; - COLLADABU::Math::Vector3 &axis = ro->getRotationAxis(); - const float angle = (float)DEG2RAD(ro->getRotationAngle()); - const float ax[] = {(float)axis[0], (float)axis[1], (float)axis[2]}; - // float quat[4]; - // axis_angle_to_quat(quat, axis, angle); - // quat_to_mat4(m, quat); - axis_angle_to_mat4(m, ax, angle); -} - -void TransformReader::dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) -{ - COLLADAFW::Translate *tra = (COLLADAFW::Translate *)tm; - COLLADABU::Math::Vector3 &t = tra->getTranslation(); - - unit_m4(m); - - m[3][0] = (float)t[0]; - m[3][1] = (float)t[1]; - m[3][2] = (float)t[2]; -} - -void TransformReader::dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) -{ - COLLADABU::Math::Vector3 &s = ((COLLADAFW::Scale *)tm)->getScale(); - float size[3] = {(float)s[0], (float)s[1], (float)s[2]}; - size_to_mat4(m, size); -} - -void TransformReader::dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) -{ - unit_converter->dae_matrix_to_mat4_(m, ((COLLADAFW::Matrix *)tm)->getMatrix()); -} - -void TransformReader::dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]) -{ - dae_vector3_to_v3(((COLLADAFW::Translate *)tm)->getTranslation(), v); -} - -void TransformReader::dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]) -{ - dae_vector3_to_v3(((COLLADAFW::Scale *)tm)->getScale(), v); -} - -void TransformReader::dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]) -{ - v[0] = v3.x; - v[1] = v3.y; - v[2] = v3.z; -} diff --git a/source/blender/collada/TransformReader.h b/source/blender/collada/TransformReader.h deleted file mode 100644 index 2cf3ee795ae..00000000000 --- a/source/blender/collada/TransformReader.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __TRANSFORMREADER_H__ -#define __TRANSFORMREADER_H__ - -#include "COLLADAFWNode.h" -#include "COLLADAFWTransformation.h" -#include "COLLADAFWTranslate.h" -#include "COLLADAFWRotate.h" -#include "COLLADAFWScale.h" -#include "COLLADAFWMatrix.h" -#include "COLLADAFWUniqueId.h" -#include "Math/COLLADABUMathVector3.h" - -#include "DNA_object_types.h" -#include "BLI_math.h" - -#include "collada_internal.h" - -// struct Object; - -class TransformReader { - protected: - UnitConverter *unit_converter; - - public: - struct Animation { - Object *ob; - COLLADAFW::Node *node; - COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id - }; - - TransformReader(UnitConverter *conv); - - void get_node_mat(float mat[4][4], - COLLADAFW::Node *node, - std::map *animation_map, - Object *ob); - void get_node_mat(float mat[4][4], - COLLADAFW::Node *node, - std::map *animation_map, - Object *ob, - float parent_mat[4][4]); - - void dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); - void dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); - void dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); - void dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); - void dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]); - void dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]); - void dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]); -}; - -#endif diff --git a/source/blender/collada/TransformWriter.cpp b/source/blender/collada/TransformWriter.cpp deleted file mode 100644 index 0a66db72cb9..00000000000 --- a/source/blender/collada/TransformWriter.cpp +++ /dev/null @@ -1,141 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#include "BLI_math.h" -#include "BLI_sys_types.h" - -#include "BKE_object.h" - -#include "TransformWriter.h" - -void TransformWriter::add_joint_transform(COLLADASW::Node &node, - float mat[4][4], - float parent_mat[4][4], - BCExportSettings &export_settings, - bool has_restmat) -{ - float local[4][4]; - - if (parent_mat) { - float invpar[4][4]; - invert_m4_m4(invpar, parent_mat); - mul_m4_m4m4(local, invpar, mat); - } - else { - copy_m4_m4(local, mat); - } - - if (!has_restmat && export_settings.get_apply_global_orientation()) { - bc_apply_global_transform(local, export_settings.get_global_transform()); - } - - double dmat[4][4]; - UnitConverter *converter = new UnitConverter(); - converter->mat4_to_dae_double(dmat, local); - delete converter; - - if (export_settings.get_object_transformation_type() == BC_TRANSFORMATION_TYPE_MATRIX) { - node.addMatrix("transform", dmat); - } - else { - float loc[3], rot[3], scale[3]; - bc_decompose(local, loc, rot, NULL, scale); - add_transform(node, loc, rot, scale); - } -} - -void TransformWriter::add_node_transform_ob(COLLADASW::Node &node, - Object *ob, - BCExportSettings &export_settings) -{ - bool limit_precision = export_settings.get_limit_precision(); - - /* Export the local Matrix (relative to the object parent, - * be it an object, bone or vertex(-tices)). */ - Matrix f_obmat; - BKE_object_matrix_local_get(ob, f_obmat); - - if (export_settings.get_apply_global_orientation()) { - bc_apply_global_transform(f_obmat, export_settings.get_global_transform()); - } - else { - bc_add_global_transform(f_obmat, export_settings.get_global_transform()); - } - - switch (export_settings.get_object_transformation_type()) { - case BC_TRANSFORMATION_TYPE_MATRIX: { - UnitConverter converter; - double d_obmat[4][4]; - converter.mat4_to_dae_double(d_obmat, f_obmat); - - if (limit_precision) { - BCMatrix::sanitize(d_obmat, LIMITTED_PRECISION); - } - node.addMatrix("transform", d_obmat); - break; - } - case BC_TRANSFORMATION_TYPE_DECOMPOSED: { - float loc[3], rot[3], scale[3]; - bc_decompose(f_obmat, loc, rot, NULL, scale); - if (limit_precision) { - bc_sanitize_v3(loc, LIMITTED_PRECISION); - bc_sanitize_v3(rot, LIMITTED_PRECISION); - bc_sanitize_v3(scale, LIMITTED_PRECISION); - } - add_transform(node, loc, rot, scale); - break; - } - } -} - -void TransformWriter::add_node_transform_identity(COLLADASW::Node &node, - BCExportSettings &export_settings) -{ - BC_export_transformation_type transformation_type = - export_settings.get_object_transformation_type(); - switch (transformation_type) { - case BC_TRANSFORMATION_TYPE_MATRIX: { - BCMatrix mat; - DMatrix d_obmat; - mat.get_matrix(d_obmat); - node.addMatrix("transform", d_obmat); - break; - } - default: { - float loc[3] = {0.0f, 0.0f, 0.0f}; - float scale[3] = {1.0f, 1.0f, 1.0f}; - float rot[3] = {0.0f, 0.0f, 0.0f}; - add_transform(node, loc, rot, scale); - break; - } - } -} - -void TransformWriter::add_transform(COLLADASW::Node &node, - float loc[3], - float rot[3], - float scale[3]) -{ - node.addScale("scale", scale[0], scale[1], scale[2]); - node.addRotateZ("rotationZ", RAD2DEGF(rot[2])); - node.addRotateY("rotationY", RAD2DEGF(rot[1])); - node.addRotateX("rotationX", RAD2DEGF(rot[0])); - node.addTranslate("location", loc[0], loc[1], loc[2]); -} diff --git a/source/blender/collada/TransformWriter.h b/source/blender/collada/TransformWriter.h deleted file mode 100644 index d2e4b369cdc..00000000000 --- a/source/blender/collada/TransformWriter.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __TRANSFORMWRITER_H__ -#define __TRANSFORMWRITER_H__ - -#include "COLLADASWNode.h" - -#include "DNA_object_types.h" - -#include "collada_internal.h" -#include "collada_utils.h" -#include "collada.h" - -class TransformWriter { - protected: - void add_joint_transform(COLLADASW::Node &node, - float mat[4][4], - float parent_mat[4][4], - BCExportSettings &export_settings, - bool has_restmat); - - void add_node_transform_ob(COLLADASW::Node &node, Object *ob, BCExportSettings &export_settings); - - void add_node_transform_identity(COLLADASW::Node &node, BCExportSettings &export_settings); - - private: - void add_transform(COLLADASW::Node &node, float loc[3], float rot[3], float scale[3]); -}; - -#endif diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp deleted file mode 100644 index ea5600aa850..00000000000 --- a/source/blender/collada/collada.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "DocumentExporter.h" -#include "DocumentImporter.h" -#include "ExportSettings.h" -#include "ImportSettings.h" -#include "collada.h" - -extern "C" { -#include "BKE_scene.h" -#include "BKE_context.h" -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" - -/* make dummy file */ -#include "BLI_fileops.h" -#include "BLI_linklist.h" - -static void print_import_header(ImportSettings &import_settings) -{ - fprintf(stderr, "+-- Collada Import parameters------\n"); - fprintf(stderr, "| input file : %s\n", import_settings.filepath); - fprintf(stderr, "| use units : %s\n", (import_settings.import_units) ? "yes" : "no"); - fprintf(stderr, "| autoconnect : %s\n", (import_settings.auto_connect) ? "yes" : "no"); - fprintf(stderr, "+-- Armature Import parameters ----\n"); - fprintf(stderr, "| find bone chains: %s\n", (import_settings.find_chains) ? "yes" : "no"); - fprintf(stderr, "| min chain len : %d\n", import_settings.min_chain_length); - fprintf(stderr, "| fix orientation : %s\n", (import_settings.fix_orientation) ? "yes" : "no"); - fprintf(stderr, "| keep bind info : %s\n", (import_settings.keep_bind_info) ? "yes" : "no"); -} - -static void print_import_footer(int status) -{ - fprintf(stderr, "+----------------------------------\n"); - fprintf(stderr, "| Collada Import : %s\n", (status) ? "OK" : "FAIL"); - fprintf(stderr, "+----------------------------------\n"); -} - -int collada_import(bContext *C, ImportSettings *import_settings) -{ - print_import_header(*import_settings); - DocumentImporter imp(C, import_settings); - int status = imp.import() ? 1 : 0; - print_import_footer(status); - - return status; -} - -int collada_export(bContext *C, ExportSettings *export_settings) -{ - BlenderContext blender_context(C); - ViewLayer *view_layer = blender_context.get_view_layer(); - - int includeFilter = OB_REL_NONE; - if (export_settings->include_armatures) { - includeFilter |= OB_REL_MOD_ARMATURE; - } - if (export_settings->include_children) { - includeFilter |= OB_REL_CHILDREN_RECURSIVE; - } - - /* Fetch the complete set of exported objects - * ATTENTION: Invisible objects will not be exported - */ - eObjectSet objectSet = (export_settings->selected) ? OB_SET_SELECTED : OB_SET_ALL; - export_settings->export_set = BKE_object_relational_superset( - view_layer, objectSet, (eObRelationTypes)includeFilter); - - int export_count = BLI_linklist_count(export_settings->export_set); - - if (export_count == 0) { - if (export_settings->selected) { - fprintf(stderr, - "Collada: Found no objects to export.\nPlease ensure that all objects which shall " - "be exported are also visible in the 3D Viewport.\n"); - } - else { - fprintf(stderr, "Collada: Your scene seems to be empty. No Objects will be exported.\n"); - } - } - else { - if (export_settings->sort_by_name) { - bc_bubble_sort_by_Object_name(export_settings->export_set); - } - } - - DocumentExporter exporter(blender_context, export_settings); - int status = exporter.exportCurrentScene(); - - BLI_linklist_free(export_settings->export_set, NULL); - - return (status) ? -1 : export_count; -} - -/* end extern C */ -} diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h deleted file mode 100644 index 72753e170a3..00000000000 --- a/source/blender/collada/collada.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __COLLADA_H__ -#define __COLLADA_H__ - -#include - -#include "ImportSettings.h" -#include "ExportSettings.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#include "BLI_linklist.h" -#include "BLI_path_util.h" -#include "RNA_types.h" - -struct bContext; - -/* - * both return 1 on success, 0 on error - */ -int collada_import(struct bContext *C, ImportSettings *import_settings); - -int collada_export(struct bContext *C, ExportSettings *export_settings); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/source/blender/collada/collada_internal.cpp b/source/blender/collada/collada_internal.cpp deleted file mode 100644 index 7e834045795..00000000000 --- a/source/blender/collada/collada_internal.cpp +++ /dev/null @@ -1,340 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" -#include "collada_utils.h" - -#include "BLI_linklist.h" -#include "ED_armature.h" - -UnitConverter::UnitConverter() : unit(), up_axis(COLLADAFW::FileInfo::Z_UP) -{ - axis_angle_to_mat4_single(x_up_mat4, 'Y', -0.5 * M_PI); - axis_angle_to_mat4_single(y_up_mat4, 'X', 0.5 * M_PI); - - unit_m4(z_up_mat4); - unit_m4(scale_mat4); -} - -void UnitConverter::read_asset(const COLLADAFW::FileInfo *asset) -{ - unit = asset->getUnit(); - up_axis = asset->getUpAxisType(); -} - -UnitConverter::UnitSystem UnitConverter::isMetricSystem() -{ - switch (unit.getLinearUnitUnit()) { - case COLLADAFW::FileInfo::Unit::MILLIMETER: - case COLLADAFW::FileInfo::Unit::CENTIMETER: - case COLLADAFW::FileInfo::Unit::DECIMETER: - case COLLADAFW::FileInfo::Unit::METER: - case COLLADAFW::FileInfo::Unit::KILOMETER: - return UnitConverter::Metric; - case COLLADAFW::FileInfo::Unit::INCH: - case COLLADAFW::FileInfo::Unit::FOOT: - case COLLADAFW::FileInfo::Unit::YARD: - return UnitConverter::Imperial; - default: - return UnitConverter::None; - } -} - -float UnitConverter::getLinearMeter() -{ - return (float)unit.getLinearUnitMeter(); -} - -void UnitConverter::convertVector3(COLLADABU::Math::Vector3 &vec, float *v) -{ - v[0] = vec.x; - v[1] = vec.y; - v[2] = vec.z; -} - -// TODO need also for angle conversion, time conversion... - -void UnitConverter::dae_matrix_to_mat4_(float out[4][4], const COLLADABU::Math::Matrix4 &in) -{ - // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h) - // so here, to make a blender matrix, we swap columns and rows - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - out[i][j] = in[j][i]; - } - } -} - -void UnitConverter::mat4_to_dae(float out[4][4], float in[4][4]) -{ - transpose_m4_m4(out, in); -} - -void UnitConverter::mat4_to_dae_double(double out[4][4], float in[4][4]) -{ - float mat[4][4]; - - mat4_to_dae(mat, in); - - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - out[i][j] = mat[i][j]; - } - } -} - -float (&UnitConverter::get_rotation())[4][4] -{ - switch (up_axis) { - case COLLADAFW::FileInfo::X_UP: - return x_up_mat4; - break; - case COLLADAFW::FileInfo::Y_UP: - return y_up_mat4; - break; - default: - return z_up_mat4; - break; - } -} - -float (&UnitConverter::get_scale())[4][4] -{ - return scale_mat4; -} - -void UnitConverter::calculate_scale(Scene &sce) -{ - PointerRNA scene_ptr, unit_settings; - PropertyRNA *system_ptr, *scale_ptr; - RNA_id_pointer_create(&sce.id, &scene_ptr); - - unit_settings = RNA_pointer_get(&scene_ptr, "unit_settings"); - system_ptr = RNA_struct_find_property(&unit_settings, "system"); - scale_ptr = RNA_struct_find_property(&unit_settings, "scale_length"); - - int type = RNA_property_enum_get(&unit_settings, system_ptr); - - float bl_scale; - - switch (type) { - case USER_UNIT_NONE: - bl_scale = 1.0; // map 1 Blender unit to 1 Meter - break; - - case USER_UNIT_METRIC: - bl_scale = RNA_property_float_get(&unit_settings, scale_ptr); - break; - - default: - bl_scale = RNA_property_float_get(&unit_settings, scale_ptr); - // it looks like the conversion to Imperial is done implicitly. - // So nothing to do here. - break; - } - - float rescale[3]; - rescale[0] = rescale[1] = rescale[2] = getLinearMeter() / bl_scale; - - size_to_mat4(scale_mat4, rescale); -} - -/** - * Translation map. - * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be - * included. Look at the IDREF XSD declaration for more. - * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, - * like special chars (e.g. micro sign), umlauts and so on. - * The COLLADA spec also allows additional chars for member access ('.'), these - * must obviously be removed too, otherwise they would be heavily misinterpreted. - */ -const unsigned char translate_start_name_map[256] = { - - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 95, 95, 95, - 95, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 122, 95, 95, 95, 95, 95, - - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, - 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, - 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, - 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, -}; - -const unsigned char translate_name_map[256] = { - - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, - 95, 95, 95, 95, 95, 95, 95, 45, 95, 95, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 57, 95, 95, 95, 95, 95, 95, 95, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, - 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 95, 95, 95, - 95, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, - 114, 115, 116, 117, 118, 119, 120, 121, 122, 95, 95, 95, 95, 95, - - 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, - 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, - 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, - 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, - 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, - 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, - 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, -}; - -typedef std::map> map_string_list; -map_string_list global_id_map; - -void clear_global_id_map() -{ - global_id_map.clear(); -} - -/** Look at documentation of translate_map */ -std::string translate_id(const char *idString) -{ - std::string id = std::string(idString); - return translate_id(id); -} - -std::string translate_id(const std::string &id) -{ - if (id.size() == 0) { - return id; - } - - std::string id_translated = id; - id_translated[0] = translate_start_name_map[(unsigned int)id_translated[0]]; - for (unsigned int i = 1; i < id_translated.size(); i++) { - id_translated[i] = translate_name_map[(unsigned int)id_translated[i]]; - } - // It's so much workload now, the if () should speed up things. - if (id_translated != id) { - // Search duplicates - map_string_list::iterator iter = global_id_map.find(id_translated); - if (iter != global_id_map.end()) { - unsigned int i = 0; - bool found = false; - for (i = 0; i < iter->second.size(); i++) { - if (id == iter->second[i]) { - found = true; - break; - } - } - bool convert = false; - if (found) { - if (i > 0) { - convert = true; - } - } - else { - convert = true; - global_id_map[id_translated].push_back(id); - } - if (convert) { - std::stringstream out; - out << ++i; - id_translated += out.str(); - } - } - else { - global_id_map[id_translated].push_back(id); - } - } - return id_translated; -} - -std::string id_name(void *id) -{ - return ((ID *)id)->name + 2; -} - -std::string encode_xml(std::string xml) -{ - const std::map escape{ - {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {'&', "&"}}; - - std::map::const_iterator it; - std::string encoded_xml = ""; - - for (unsigned int i = 0; i < xml.size(); i++) { - char c = xml.at(i); - it = escape.find(c); - - if (it == escape.end()) { - encoded_xml += c; - } - else { - encoded_xml += it->second; - } - } - return encoded_xml; -} - -std::string get_geometry_id(Object *ob) -{ - return translate_id(id_name(ob->data)) + "-mesh"; -} - -std::string get_geometry_id(Object *ob, bool use_instantiation) -{ - std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob); - - return translate_id(geom_name) + "-mesh"; -} - -std::string get_light_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-light"; -} - -std::string get_joint_sid(Bone *bone) -{ - return translate_id(bone->name); -} -static std::string get_joint_sid(EditBone *bone) -{ - return translate_id(bone->name); -} - -std::string get_camera_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-camera"; -} - -std::string get_effect_id(Material *mat) -{ - return translate_id(id_name(mat)) + "-effect"; -} - -std::string get_material_id(Material *mat) -{ - return translate_id(id_name(mat)) + "-material"; -} - -std::string get_morph_id(Object *ob) -{ - return translate_id(id_name(ob)) + "-morph"; -} diff --git a/source/blender/collada/collada_internal.h b/source/blender/collada/collada_internal.h deleted file mode 100644 index 297ea9c0bbb..00000000000 --- a/source/blender/collada/collada_internal.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __COLLADA_INTERNAL_H__ -#define __COLLADA_INTERNAL_H__ - -#include -#include -#include - -#include "COLLADAFWFileInfo.h" -#include "Math/COLLADABUMathMatrix4.h" - -#include "DNA_armature_types.h" -#include "DNA_material_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "BLI_math.h" -#include "BLI_linklist.h" - -class UnitConverter { - private: - COLLADAFW::FileInfo::Unit unit; - COLLADAFW::FileInfo::UpAxisType up_axis; - - float x_up_mat4[4][4]; - float y_up_mat4[4][4]; - float z_up_mat4[4][4]; - float scale_mat4[4][4]; - - public: - enum UnitSystem { - None, - Metric, - Imperial, - }; - - // Initialize with Z_UP, since Blender uses right-handed, z-up - UnitConverter(); - - void read_asset(const COLLADAFW::FileInfo *asset); - - void convertVector3(COLLADABU::Math::Vector3 &vec, float *v); - - UnitConverter::UnitSystem isMetricSystem(void); - - float getLinearMeter(void); - - // TODO need also for angle conversion, time conversion... - - static void dae_matrix_to_mat4_(float out[4][4], const COLLADABU::Math::Matrix4 &in); - static void mat4_to_dae(float out[4][4], float in[4][4]); - static void mat4_to_dae_double(double out[4][4], float in[4][4]); - - float (&get_rotation())[4][4]; - float (&get_scale())[4][4]; - void calculate_scale(Scene &sce); -}; - -extern void clear_global_id_map(); -/** Look at documentation of translate_map */ -extern std::string translate_id(const std::string &id); -extern std::string translate_id(const char *idString); - -extern std::string id_name(void *id); -extern std::string encode_xml(std::string xml); - -extern std::string get_geometry_id(Object *ob); -extern std::string get_geometry_id(Object *ob, bool use_instantiation); - -extern std::string get_light_id(Object *ob); - -extern std::string get_joint_sid(Bone *bone); - -extern std::string get_camera_id(Object *ob); -extern std::string get_morph_id(Object *ob); - -extern std::string get_effect_id(Material *mat); -extern std::string get_material_id(Material *mat); - -#endif /* __COLLADA_INTERNAL_H__ */ diff --git a/source/blender/collada/collada_utils.cpp b/source/blender/collada/collada_utils.cpp deleted file mode 100644 index 26b392af0a1..00000000000 --- a/source/blender/collada/collada_utils.cpp +++ /dev/null @@ -1,1458 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -/* COLLADABU_ASSERT, may be able to remove later */ -#include "COLLADABUPlatform.h" - -#include "COLLADAFWGeometry.h" -#include "COLLADAFWMeshPrimitive.h" -#include "COLLADAFWMeshVertexData.h" - -#include -#include - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DNA_modifier_types.h" -#include "DNA_customdata_types.h" -#include "DNA_key_types.h" -#include "DNA_object_types.h" -#include "DNA_constraint_types.h" -#include "DNA_mesh_types.h" -#include "DNA_scene_types.h" -#include "DNA_armature_types.h" - -#include "BLI_math.h" -#include "BLI_linklist.h" -#include "BLI_listbase.h" - -#include "BKE_action.h" -#include "BKE_context.h" -#include "BKE_customdata.h" -#include "BKE_constraint.h" -#include "BKE_key.h" -#include "BKE_material.h" -#include "BKE_node.h" -#include "BKE_object.h" -#include "BKE_global.h" -#include "BKE_layer.h" -#include "BKE_lib_id.h" -#include "BKE_mesh.h" -#include "BKE_mesh_runtime.h" -#include "BKE_object.h" -#include "BKE_scene.h" - -#include "ED_armature.h" -#include "ED_screen.h" -#include "ED_node.h" -#include "ED_object.h" - -#include "WM_api.h" /* XXX hrm, see if we can do without this */ -#include "WM_types.h" - -#include "bmesh.h" -#include "bmesh_tools.h" - -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_query.h" -#if 0 -# include "NOD_common.h" -#endif -} - -#include "collada_utils.h" -#include "ExportSettings.h" -#include "BlenderContext.h" - -float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray &array, unsigned int index) -{ - if (index >= array.getValuesCount()) { - return 0.0f; - } - - if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) { - return array.getFloatValues()->getData()[index]; - } - else { - return array.getDoubleValues()->getData()[index]; - } -} - -/* copied from /editors/object/object_relations.c */ -int bc_test_parent_loop(Object *par, Object *ob) -{ - /* test if 'ob' is a parent somewhere in par's parents */ - - if (par == NULL) { - return 0; - } - if (ob == par) { - return 1; - } - - return bc_test_parent_loop(par->parent, ob); -} - -bool bc_validateConstraints(bConstraint *con) -{ - const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); - - /* these we can skip completely (invalid constraints...) */ - if (cti == NULL) { - return false; - } - if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) { - return false; - } - - /* these constraints can't be evaluated anyway */ - if (cti->evaluate_constraint == NULL) { - return false; - } - - /* influence == 0 should be ignored */ - if (con->enforce == 0.0f) { - return false; - } - - /* validation passed */ - return true; -} - -bool bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space) -{ - Scene *scene = CTX_data_scene(C); - int partype = PAR_OBJECT; - const bool xmirror = false; - const bool keep_transform = false; - - if (par && is_parent_space) { - mul_m4_m4m4(ob->obmat, par->obmat, ob->obmat); - } - - bool ok = ED_object_parent_set(NULL, C, scene, ob, par, partype, xmirror, keep_transform, NULL); - return ok; -} - -std::vector bc_getSceneActions(const bContext *C, Object *ob, bool all_actions) -{ - std::vector actions; - if (all_actions) { - Main *bmain = CTX_data_main(C); - ID *id; - - for (id = (ID *)bmain->actions.first; id; id = (ID *)(id->next)) { - bAction *act = (bAction *)id; - /* XXX This currently creates too many actions. - * TODO Need to check if the action is compatible to the given object. */ - actions.push_back(act); - } - } - else { - bAction *action = bc_getSceneObjectAction(ob); - actions.push_back(action); - } - - return actions; -} - -std::string bc_get_action_id(std::string action_name, - std::string ob_name, - std::string channel_type, - std::string axis_name, - std::string axis_separator) -{ - std::string result = action_name + "_" + channel_type; - if (ob_name.length() > 0) { - result = ob_name + "_" + result; - } - if (axis_name.length() > 0) { - result += axis_separator + axis_name; - } - return translate_id(result); -} - -void bc_update_scene(BlenderContext &blender_context, float ctime) -{ - Main *bmain = blender_context.get_main(); - Scene *scene = blender_context.get_scene(); - Depsgraph *depsgraph = blender_context.get_depsgraph(); - - /* See remark in physics_fluid.c lines 395...) */ - // BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay); - BKE_scene_frame_set(scene, ctime); - ED_update_for_newframe(bmain, depsgraph); -} - -Object *bc_add_object(Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name) -{ - Object *ob = BKE_object_add_only_object(bmain, type, name); - - ob->data = BKE_object_obdata_add_from_type(bmain, type, name); - DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - - Base *base = BKE_view_layer_base_find(view_layer, ob); - /* TODO: is setting active needed? */ - BKE_view_layer_base_select_and_set_active(view_layer, base); - - return ob; -} - -Mesh *bc_get_mesh_copy(BlenderContext &blender_context, - Object *ob, - BC_export_mesh_type export_mesh_type, - bool apply_modifiers, - bool triangulate) -{ - CustomData_MeshMasks mask = CD_MASK_MESH; - Mesh *tmpmesh = NULL; - if (apply_modifiers) { -#if 0 /* Not supported by new system currently... */ - switch (export_mesh_type) { - case BC_MESH_TYPE_VIEW: { - dm = mesh_create_derived_view(depsgraph, scene, ob, &mask); - break; - } - case BC_MESH_TYPE_RENDER: { - dm = mesh_create_derived_render(depsgraph, scene, ob, &mask); - break; - } - } -#else - Depsgraph *depsgraph = blender_context.get_depsgraph(); - Scene *scene_eval = blender_context.get_evaluated_scene(); - Object *ob_eval = blender_context.get_evaluated_object(ob); - tmpmesh = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &mask); -#endif - } - else { - tmpmesh = (Mesh *)ob->data; - } - - BKE_id_copy_ex(NULL, &tmpmesh->id, (ID **)&tmpmesh, LIB_ID_COPY_LOCALIZE); - - if (triangulate) { - bc_triangulate_mesh(tmpmesh); - } - BKE_mesh_tessface_ensure(tmpmesh); - return tmpmesh; -} - -Object *bc_get_assigned_armature(Object *ob) -{ - Object *ob_arm = NULL; - - if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { - ob_arm = ob->parent; - } - else { - ModifierData *mod; - for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) { - if (mod->type == eModifierType_Armature) { - ob_arm = ((ArmatureModifierData *)mod)->object; - } - } - } - - return ob_arm; -} - -bool bc_has_object_type(LinkNode *export_set, short obtype) -{ - LinkNode *node; - - for (node = export_set; node; node = node->next) { - Object *ob = (Object *)node->link; - /* XXX - why is this checking for ob->data? - we could be looking for empties */ - if (ob->type == obtype && ob->data) { - return true; - } - } - return false; -} - -/* Use bubble sort algorithm for sorting the export set */ -void bc_bubble_sort_by_Object_name(LinkNode *export_set) -{ - bool sorted = false; - LinkNode *node; - for (node = export_set; node->next && !sorted; node = node->next) { - - sorted = true; - - LinkNode *current; - for (current = export_set; current->next; current = current->next) { - Object *a = (Object *)current->link; - Object *b = (Object *)current->next->link; - - if (strcmp(a->id.name, b->id.name) > 0) { - current->link = b; - current->next->link = a; - sorted = false; - } - } - } -} - -/* Check if a bone is the top most exportable bone in the bone hierarchy. - * When deform_bones_only == false, then only bones with NO parent - * can be root bones. Otherwise the top most deform bones in the hierarchy - * are root bones. - */ -bool bc_is_root_bone(Bone *aBone, bool deform_bones_only) -{ - if (deform_bones_only) { - Bone *root = NULL; - Bone *bone = aBone; - while (bone) { - if (!(bone->flag & BONE_NO_DEFORM)) { - root = bone; - } - bone = bone->parent; - } - return (aBone == root); - } - else { - return !(aBone->parent); - } -} - -int bc_get_active_UVLayer(Object *ob) -{ - Mesh *me = (Mesh *)ob->data; - return CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); -} - -std::string bc_url_encode(std::string data) -{ - /* XXX We probably do not need to do a full encoding. - * But in case that is necessary,then it can be added here. - */ - return bc_replace_string(data, "#", "%23"); -} - -std::string bc_replace_string(std::string data, - const std::string &pattern, - const std::string &replacement) -{ - size_t pos = 0; - while ((pos = data.find(pattern, pos)) != std::string::npos) { - data.replace(pos, pattern.length(), replacement); - pos += replacement.length(); - } - return data; -} - -/** - * Calculate a rescale factor such that the imported scene's scale - * is preserved. I.e. 1 meter in the import will also be - * 1 meter in the current scene. - */ - -void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene) -{ - if (scale_to_scene) { - mul_m4_m4m4(ob->obmat, bc_unit.get_scale(), ob->obmat); - } - mul_m4_m4m4(ob->obmat, bc_unit.get_rotation(), ob->obmat); - BKE_object_apply_mat4(ob, ob->obmat, 0, 0); -} - -void bc_match_scale(std::vector *objects_done, - UnitConverter &bc_unit, - bool scale_to_scene) -{ - for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); - ++it) { - Object *ob = *it; - if (ob->parent == NULL) { - bc_match_scale(*it, bc_unit, scale_to_scene); - } - } -} - -/* - * Convenience function to get only the needed components of a matrix - */ -void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size) -{ - if (size) { - mat4_to_size(size, mat); - } - - if (eul) { - mat4_to_eul(eul, mat); - } - - if (quat) { - mat4_to_quat(quat, mat); - } - - if (loc) { - copy_v3_v3(loc, mat[3]); - } -} - -/* - * Create rotation_quaternion from a delta rotation and a reference quat - * - * Input: - * mat_from: The rotation matrix before rotation - * mat_to : The rotation matrix after rotation - * qref : the quat corresponding to mat_from - * - * Output: - * rot : the calculated result (quaternion) - */ -void bc_rotate_from_reference_quat(float quat_to[4], float quat_from[4], float mat_to[4][4]) -{ - float qd[4]; - float matd[4][4]; - float mati[4][4]; - float mat_from[4][4]; - quat_to_mat4(mat_from, quat_from); - - /* Calculate the difference matrix matd between mat_from and mat_to */ - invert_m4_m4(mati, mat_from); - mul_m4_m4m4(matd, mati, mat_to); - - mat4_to_quat(qd, matd); - - mul_qt_qtqt(quat_to, qd, quat_from); /* rot is the final rotation corresponding to mat_to */ -} - -void bc_triangulate_mesh(Mesh *me) -{ - bool use_beauty = false; - bool tag_only = false; - - /* XXX: The triangulation method selection could be offered in the UI. */ - int quad_method = MOD_TRIANGULATE_QUAD_SHORTEDGE; - - const struct BMeshCreateParams bm_create_params = {0}; - BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_create_params); - BMeshFromMeshParams bm_from_me_params = {0}; - bm_from_me_params.calc_face_normal = true; - BM_mesh_bm_from_me(bm, me, &bm_from_me_params); - BM_mesh_triangulate(bm, quad_method, use_beauty, 4, tag_only, NULL, NULL, NULL); - - BMeshToMeshParams bm_to_me_params = {0}; - bm_to_me_params.calc_object_remap = false; - BM_mesh_bm_to_me(NULL, bm, me, &bm_to_me_params); - BM_mesh_free(bm); -} - -/* - * A bone is a leaf when it has no children or all children are not connected. - */ -bool bc_is_leaf_bone(Bone *bone) -{ - for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { - if (child->flag & BONE_CONNECTED) { - return false; - } - } - return true; -} - -EditBone *bc_get_edit_bone(bArmature *armature, char *name) -{ - EditBone *eBone; - - for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { - if (STREQ(name, eBone->name)) { - return eBone; - } - } - - return NULL; -} -int bc_set_layer(int bitfield, int layer) -{ - return bc_set_layer(bitfield, layer, true); /* enable */ -} - -int bc_set_layer(int bitfield, int layer, bool enable) -{ - int bit = 1u << layer; - - if (enable) { - bitfield |= bit; - } - else { - bitfield &= ~bit; - } - - return bitfield; -} - -/** - * This method creates a new extension map when needed. - * \note The ~BoneExtensionManager destructor takes care - * to delete the created maps when the manager is removed. - */ -BoneExtensionMap &BoneExtensionManager::getExtensionMap(bArmature *armature) -{ - std::string key = armature->id.name; - BoneExtensionMap *result = extended_bone_maps[key]; - if (result == NULL) { - result = new BoneExtensionMap(); - extended_bone_maps[key] = result; - } - return *result; -} - -BoneExtensionManager::~BoneExtensionManager() -{ - std::map::iterator map_it; - for (map_it = extended_bone_maps.begin(); map_it != extended_bone_maps.end(); ++map_it) { - BoneExtensionMap *extended_bones = map_it->second; - for (BoneExtensionMap::iterator ext_it = extended_bones->begin(); - ext_it != extended_bones->end(); - ++ext_it) { - if (ext_it->second != NULL) { - delete ext_it->second; - } - } - extended_bones->clear(); - delete extended_bones; - } -} - -/** - * BoneExtended is a helper class needed for the Bone chain finder - * See ArmatureImporter::fix_leaf_bones() - * and ArmatureImporter::connect_bone_chains() - */ - -BoneExtended::BoneExtended(EditBone *aBone) -{ - this->set_name(aBone->name); - this->chain_length = 0; - this->is_leaf = false; - this->tail[0] = 0.0f; - this->tail[1] = 0.5f; - this->tail[2] = 0.0f; - this->use_connect = -1; - this->roll = 0; - this->bone_layers = 0; - - this->has_custom_tail = false; - this->has_custom_roll = false; -} - -char *BoneExtended::get_name() -{ - return name; -} - -void BoneExtended::set_name(char *aName) -{ - BLI_strncpy(name, aName, MAXBONENAME); -} - -int BoneExtended::get_chain_length() -{ - return chain_length; -} - -void BoneExtended::set_chain_length(const int aLength) -{ - chain_length = aLength; -} - -void BoneExtended::set_leaf_bone(bool state) -{ - is_leaf = state; -} - -bool BoneExtended::is_leaf_bone() -{ - return is_leaf; -} - -void BoneExtended::set_roll(float roll) -{ - this->roll = roll; - this->has_custom_roll = true; -} - -bool BoneExtended::has_roll() -{ - return this->has_custom_roll; -} - -float BoneExtended::get_roll() -{ - return this->roll; -} - -void BoneExtended::set_tail(float vec[]) -{ - this->tail[0] = vec[0]; - this->tail[1] = vec[1]; - this->tail[2] = vec[2]; - this->has_custom_tail = true; -} - -bool BoneExtended::has_tail() -{ - return this->has_custom_tail; -} - -float *BoneExtended::get_tail() -{ - return this->tail; -} - -inline bool isInteger(const std::string &s) -{ - if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) { - return false; - } - - char *p; - strtol(s.c_str(), &p, 10); - - return (*p == 0); -} - -void BoneExtended::set_bone_layers(std::string layerString, std::vector &layer_labels) -{ - std::stringstream ss(layerString); - std::string layer; - int pos; - - while (ss >> layer) { - - /* Blender uses numbers to specify layers*/ - if (isInteger(layer)) { - pos = atoi(layer.c_str()); - if (pos >= 0 && pos < 32) { - this->bone_layers = bc_set_layer(this->bone_layers, pos); - continue; - } - } - - /* layer uses labels (not supported by blender). Map to layer numbers:*/ - pos = find(layer_labels.begin(), layer_labels.end(), layer) - layer_labels.begin(); - if (pos >= layer_labels.size()) { - layer_labels.push_back(layer); /* remember layer number for future usage*/ - } - - if (pos > 31) { - fprintf(stderr, - "Too many layers in Import. Layer %s mapped to Blender layer 31\n", - layer.c_str()); - pos = 31; - } - - /* If numeric layers and labeled layers are used in parallel (unlikely), - * we get a potential mixup. Just leave as is for now. - */ - this->bone_layers = bc_set_layer(this->bone_layers, pos); - } -} - -std::string BoneExtended::get_bone_layers(int bitfield) -{ - std::string result = ""; - std::string sep = ""; - int bit = 1u; - - std::ostringstream ss; - for (int i = 0; i < 32; i++) { - if (bit & bitfield) { - ss << sep << i; - sep = " "; - } - bit = bit << 1; - } - return ss.str(); -} - -int BoneExtended::get_bone_layers() -{ - /* ensure that the bone is in at least one bone layer! */ - return (bone_layers == 0) ? 1 : bone_layers; -} - -void BoneExtended::set_use_connect(int use_connect) -{ - this->use_connect = use_connect; -} - -int BoneExtended::get_use_connect() -{ - return this->use_connect; -} - -/** - * Stores a 4*4 matrix as a custom bone property array of size 16 - */ -void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]) -{ - IDProperty *idgroup = (IDProperty *)ebone->prop; - if (idgroup == NULL) { - IDPropertyTemplate val = {0}; - idgroup = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); - ebone->prop = idgroup; - } - - IDPropertyTemplate val = {0}; - val.array.len = 16; - val.array.type = IDP_FLOAT; - - IDProperty *data = IDP_New(IDP_ARRAY, &val, key); - float *array = (float *)IDP_Array(data); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - array[4 * i + j] = mat[i][j]; - } - } - - IDP_AddToGroup(idgroup, data); -} - -#if 0 -/** - * Stores a Float value as a custom bone property - * - * Note: This function is currently not needed. Keep for future usage - */ -static void bc_set_IDProperty(EditBone *ebone, const char *key, float value) -{ - if (ebone->prop == NULL) { - IDPropertyTemplate val = {0}; - ebone->prop = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); - } - - IDProperty *pgroup = (IDProperty *)ebone->prop; - IDPropertyTemplate val = {0}; - IDProperty *prop = IDP_New(IDP_FLOAT, &val, key); - IDP_Float(prop) = value; - IDP_AddToGroup(pgroup, prop); -} -#endif - -/** - * Get a custom property when it exists. - * This function is also used to check if a property exists. - */ -IDProperty *bc_get_IDProperty(Bone *bone, std::string key) -{ - return (bone->prop == NULL) ? NULL : IDP_GetPropertyFromGroup(bone->prop, key.c_str()); -} - -/** - * Read a custom bone property and convert to float - * Return def if the property does not exist. - */ -float bc_get_property(Bone *bone, std::string key, float def) -{ - float result = def; - IDProperty *property = bc_get_IDProperty(bone, key); - if (property) { - switch (property->type) { - case IDP_INT: - result = (float)(IDP_Int(property)); - break; - case IDP_FLOAT: - result = (float)(IDP_Float(property)); - break; - case IDP_DOUBLE: - result = (float)(IDP_Double(property)); - break; - default: - result = def; - } - } - return result; -} - -/** - * Read a custom bone property and convert to matrix - * Return true if conversion was successful - * - * Return false if: - * - the property does not exist - * - is not an array of size 16 - */ -bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]) -{ - IDProperty *property = bc_get_IDProperty(bone, key); - if (property && property->type == IDP_ARRAY && property->len == 16) { - float *array = (float *)IDP_Array(property); - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - mat[i][j] = array[4 * i + j]; - } - } - return true; - } - return false; -} - -/** - * get a vector that is stored in 3 custom properties (used in Blender <= 2.78) - */ -void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]) -{ - val[0] = bc_get_property(bone, key + "_x", def[0]); - val[1] = bc_get_property(bone, key + "_y", def[1]); - val[2] = bc_get_property(bone, key + "_z", def[2]); -} - -/** - * Check if vector exist stored in 3 custom properties (used in Blender <= 2.78) - */ -static bool has_custom_props(Bone *bone, bool enabled, std::string key) -{ - if (!enabled) { - return false; - } - - return (bc_get_IDProperty(bone, key + "_x") || bc_get_IDProperty(bone, key + "_y") || - bc_get_IDProperty(bone, key + "_z")); -} - -void bc_enable_fcurves(bAction *act, char *bone_name) -{ - FCurve *fcu; - char prefix[200]; - - if (bone_name) { - BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); - } - - for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { - if (bone_name) { - if (STREQLEN(fcu->rna_path, prefix, strlen(prefix))) { - fcu->flag &= ~FCURVE_DISABLED; - } - else { - fcu->flag |= FCURVE_DISABLED; - } - } - else { - fcu->flag &= ~FCURVE_DISABLED; - } - } -} - -bool bc_bone_matrix_local_get(Object *ob, Bone *bone, Matrix &mat, bool for_opensim) -{ - - /* Ok, lets be super cautious and check if the bone exists */ - bPose *pose = ob->pose; - bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name); - if (!pchan) { - return false; - } - - bAction *action = bc_getSceneObjectAction(ob); - bPoseChannel *parchan = pchan->parent; - - bc_enable_fcurves(action, bone->name); - float ipar[4][4]; - - if (bone->parent) { - invert_m4_m4(ipar, parchan->pose_mat); - mul_m4_m4m4(mat, ipar, pchan->pose_mat); - } - else { - copy_m4_m4(mat, pchan->pose_mat); - } - - /* OPEN_SIM_COMPATIBILITY - * AFAIK animation to second life is via BVH, but no - * reason to not have the collada-animation be correct */ - if (for_opensim) { - float temp[4][4]; - copy_m4_m4(temp, bone->arm_mat); - temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; - invert_m4(temp); - - mul_m4_m4m4(mat, mat, temp); - - if (bone->parent) { - copy_m4_m4(temp, bone->parent->arm_mat); - temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; - - mul_m4_m4m4(mat, temp, mat); - } - } - bc_enable_fcurves(action, NULL); - return true; -} - -bool bc_is_animated(BCMatrixSampleMap &values) -{ - static float MIN_DISTANCE = 0.00001; - - if (values.size() < 2) { - return false; /* need at least 2 entries to be not flat */ - } - - BCMatrixSampleMap::iterator it; - const BCMatrix *refmat = NULL; - for (it = values.begin(); it != values.end(); ++it) { - const BCMatrix *matrix = it->second; - - if (refmat == NULL) { - refmat = matrix; - continue; - } - - if (!matrix->in_range(*refmat, MIN_DISTANCE)) { - return true; - } - } - return false; -} - -bool bc_has_animations(Object *ob) -{ - /* Check for object, light and camera transform animations */ - if ((bc_getSceneObjectAction(ob) && bc_getSceneObjectAction(ob)->curves.first) || - (bc_getSceneLightAction(ob) && bc_getSceneLightAction(ob)->curves.first) || - (bc_getSceneCameraAction(ob) && bc_getSceneCameraAction(ob)->curves.first)) { - return true; - } - - /* Check Material Effect parameter animations. */ - for (int a = 0; a < ob->totcol; a++) { - Material *ma = BKE_object_material_get(ob, a + 1); - if (!ma) { - continue; - } - if (ma->adt && ma->adt->action && ma->adt->action->curves.first) { - return true; - } - } - - Key *key = BKE_key_from_object(ob); - if ((key && key->adt && key->adt->action) && key->adt->action->curves.first) { - return true; - } - - return false; -} - -bool bc_has_animations(Scene *sce, LinkNode *export_set) -{ - LinkNode *node; - if (export_set) { - for (node = export_set; node; node = node->next) { - Object *ob = (Object *)node->link; - - if (bc_has_animations(ob)) { - return true; - } - } - } - return false; -} - -void bc_add_global_transform(Matrix &to_mat, - const Matrix &from_mat, - const BCMatrix &global_transform, - const bool invert) -{ - copy_m4_m4(to_mat, from_mat); - bc_add_global_transform(to_mat, global_transform, invert); -} - -void bc_add_global_transform(Vector &to_vec, - const Vector &from_vec, - const BCMatrix &global_transform, - const bool invert) -{ - copy_v3_v3(to_vec, from_vec); - bc_add_global_transform(to_vec, global_transform, invert); -} - -void bc_add_global_transform(Matrix &to_mat, const BCMatrix &global_transform, const bool invert) -{ - BCMatrix mat(to_mat); - mat.add_transform(global_transform, invert); - mat.get_matrix(to_mat); -} - -void bc_add_global_transform(Vector &to_vec, const BCMatrix &global_transform, const bool invert) -{ - Matrix mat; - Vector from_vec; - copy_v3_v3(from_vec, to_vec); - global_transform.get_matrix(mat, false, 6, invert); - mul_v3_m4v3(to_vec, mat, from_vec); -} - -void bc_apply_global_transform(Matrix &to_mat, const BCMatrix &global_transform, const bool invert) -{ - BCMatrix mat(to_mat); - mat.apply_transform(global_transform, invert); - mat.get_matrix(to_mat); -} - -void bc_apply_global_transform(Vector &to_vec, const BCMatrix &global_transform, const bool invert) -{ - Matrix transform; - global_transform.get_matrix(transform); - mul_v3_m4v3(to_vec, transform, to_vec); -} - -/** - * Check if custom information about bind matrix exists and modify the from_mat - * accordingly. - * - * Note: This is old style for Blender <= 2.78 only kept for compatibility - */ -void bc_create_restpose_mat(BCExportSettings &export_settings, - Bone *bone, - float to_mat[4][4], - float from_mat[4][4], - bool use_local_space) -{ - float loc[3]; - float rot[3]; - float scale[3]; - static const float V0[3] = {0, 0, 0}; - - if (!has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_loc") && - !has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_rot") && - !has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_scale")) { - /* No need */ - copy_m4_m4(to_mat, from_mat); - return; - } - - bc_decompose(from_mat, loc, rot, NULL, scale); - loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); - - if (export_settings.get_keep_bind_info()) { - bc_get_property_vector(bone, "restpose_loc", loc, loc); - - if (use_local_space && bone->parent) { - Bone *b = bone; - while (b->parent) { - b = b->parent; - float ploc[3]; - bc_get_property_vector(b, "restpose_loc", ploc, V0); - loc[0] += ploc[0]; - loc[1] += ploc[1]; - loc[2] += ploc[2]; - } - } - } - - if (export_settings.get_keep_bind_info()) { - if (bc_get_IDProperty(bone, "restpose_rot_x")) { - rot[0] = DEG2RADF(bc_get_property(bone, "restpose_rot_x", 0)); - } - if (bc_get_IDProperty(bone, "restpose_rot_y")) { - rot[1] = DEG2RADF(bc_get_property(bone, "restpose_rot_y", 0)); - } - if (bc_get_IDProperty(bone, "restpose_rot_z")) { - rot[2] = DEG2RADF(bc_get_property(bone, "restpose_rot_z", 0)); - } - } - - if (export_settings.get_keep_bind_info()) { - bc_get_property_vector(bone, "restpose_scale", scale, scale); - } - - loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); -} - -void bc_sanitize_v3(float v[3], int precision) -{ - for (int i = 0; i < 3; i++) { - double val = (double)v[i]; - val = double_round(val, precision); - v[i] = (float)val; - } -} - -void bc_sanitize_v3(double v[3], int precision) -{ - for (int i = 0; i < 3; i++) { - v[i] = double_round(v[i], precision); - } -} - -void bc_copy_m4_farray(float r[4][4], float *a) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - r[i][j] = *a++; - } - } -} - -void bc_copy_farray_m4(float *r, float a[4][4]) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - *r++ = a[i][j]; - } - } -} - -void bc_copy_darray_m4d(double *r, double a[4][4]) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - *r++ = a[i][j]; - } - } -} - -void bc_copy_v44_m4d(std::vector> &r, double (&a)[4][4]) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - r[i][j] = a[i][j]; - } - } -} - -void bc_copy_m4d_v44(double (&r)[4][4], std::vector> &a) -{ - for (int i = 0; i < 4; i++) { - for (int j = 0; j < 4; j++) { - r[i][j] = a[i][j]; - } - } -} - -/** - * Returns name of Active UV Layer or empty String if no active UV Layer defined - */ -static std::string bc_get_active_uvlayer_name(Mesh *me) -{ - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - if (num_layers) { - char *layer_name = bc_CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV); - if (layer_name) { - return std::string(layer_name); - } - } - return ""; -} - -/** - * Returns name of Active UV Layer or empty String if no active UV Layer defined. - * Assuming the Object is of type MESH - */ -static std::string bc_get_active_uvlayer_name(Object *ob) -{ - Mesh *me = (Mesh *)ob->data; - return bc_get_active_uvlayer_name(me); -} - -/** - * Returns UV Layer name or empty string if layer index is out of range - */ -static std::string bc_get_uvlayer_name(Mesh *me, int layer) -{ - int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); - if (num_layers && layer < num_layers) { - char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, layer); - if (layer_name) { - return std::string(layer_name); - } - } - return ""; -} - -std::string bc_find_bonename_in_path(std::string path, std::string probe) -{ - std::string result; - char *boneName = BLI_str_quoted_substrN(path.c_str(), probe.c_str()); - if (boneName) { - result = std::string(boneName); - MEM_freeN(boneName); - } - return result; -} - -static bNodeTree *prepare_material_nodetree(Material *ma) -{ - if (ma->nodetree == NULL) { - ma->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree"); - ma->use_nodes = true; - } - return ma->nodetree; -} - -static bNode *bc_add_node( - bContext *C, bNodeTree *ntree, int node_type, int locx, int locy, std::string label) -{ - bNode *node = nodeAddStaticNode(C, ntree, node_type); - if (node) { - if (label.length() > 0) { - strcpy(node->label, label.c_str()); - } - node->locx = locx; - node->locy = locy; - node->flag |= NODE_SELECT; - } - return node; -} - -static bNode *bc_add_node(bContext *C, bNodeTree *ntree, int node_type, int locx, int locy) -{ - return bc_add_node(C, ntree, node_type, locx, locy, ""); -} - -#if 0 -/* experimental, probably not used */ -static bNodeSocket *bc_group_add_input_socket(bNodeTree *ntree, - bNode *to_node, - int to_index, - std::string label) -{ - bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); - - //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); - //return socket; - - bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); - bNode *inputGroup = ntreeFindType(ntree, NODE_GROUP_INPUT); - node_group_input_verify(ntree, inputGroup, (ID *)ntree); - bNodeSocket *newsock = node_group_input_find_socket(inputGroup, gsock->identifier); - nodeAddLink(ntree, inputGroup, newsock, to_node, to_socket); - strcpy(newsock->name, label.c_str()); - return newsock; -} - -static bNodeSocket *bc_group_add_output_socket(bNodeTree *ntree, - bNode *from_node, - int from_index, - std::string label) -{ - bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); - - //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); - //return socket; - - bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, from_node, from_socket); - bNode *outputGroup = ntreeFindType(ntree, NODE_GROUP_OUTPUT); - node_group_output_verify(ntree, outputGroup, (ID *)ntree); - bNodeSocket *newsock = node_group_output_find_socket(outputGroup, gsock->identifier); - nodeAddLink(ntree, from_node, from_socket, outputGroup, newsock); - strcpy(newsock->name, label.c_str()); - return newsock; -} - -void bc_make_group(bContext *C, bNodeTree *ntree, std::map nmap) -{ - bNode *gnode = node_group_make_from_selected(C, ntree, "ShaderNodeGroup", "ShaderNodeTree"); - bNodeTree *gtree = (bNodeTree *)gnode->id; - - bc_group_add_input_socket(gtree, nmap["main"], 0, "Diffuse"); - bc_group_add_input_socket(gtree, nmap["emission"], 0, "Emission"); - bc_group_add_input_socket(gtree, nmap["mix"], 0, "Transparency"); - bc_group_add_input_socket(gtree, nmap["emission"], 1, "Emission"); - bc_group_add_input_socket(gtree, nmap["main"], 4, "Metallic"); - bc_group_add_input_socket(gtree, nmap["main"], 5, "Specular"); - - bc_group_add_output_socket(gtree, nmap["mix"], 0, "Shader"); -} -#endif - -static void bc_node_add_link( - bNodeTree *ntree, bNode *from_node, int from_index, bNode *to_node, int to_index) -{ - bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); - bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); - - nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); -} - -void bc_add_default_shader(bContext *C, Material *ma) -{ - bNodeTree *ntree = prepare_material_nodetree(ma); - std::map nmap; -#if 0 - nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300); - nmap["emission"] = bc_add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission"); - nmap["add"] = bc_add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400); - nmap["transparent"] = bc_add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200); - nmap["mix"] = bc_add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency"); - nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300); - nmap["out"]->flag &= ~NODE_SELECT; - - bc_node_add_link(ntree, nmap["emission"], 0, nmap["add"], 0); - bc_node_add_link(ntree, nmap["main"], 0, nmap["add"], 1); - bc_node_add_link(ntree, nmap["add"], 0, nmap["mix"], 1); - bc_node_add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2); - - bc_node_add_link(ntree, nmap["mix"], 0, nmap["out"], 0); - /* experimental, probably not used. */ - bc_make_group(C, ntree, nmap); -#else - nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, 0, 300); - nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 300, 300); - bc_node_add_link(ntree, nmap["main"], 0, nmap["out"], 0); -#endif -} - -COLLADASW::ColorOrTexture bc_get_base_color(Material *ma) -{ - /* for alpha see bc_get_alpha() */ - Color default_color = {ma->r, ma->g, ma->b, 1.0}; - bNode *shader = bc_get_master_shader(ma); - if (ma->use_nodes && shader) { - return bc_get_cot_from_shader(shader, "Base Color", default_color, false); - } - else { - return bc_get_cot(default_color); - } -} - -COLLADASW::ColorOrTexture bc_get_emission(Material *ma) -{ - Color default_color = {0, 0, 0, 1}; - bNode *shader = bc_get_master_shader(ma); - if (ma->use_nodes && shader) { - return bc_get_cot_from_shader(shader, "Emission", default_color); - } - else { - return bc_get_cot(default_color); /* default black */ - } -} - -COLLADASW::ColorOrTexture bc_get_ambient(Material *ma) -{ - Color default_color = {0, 0, 0, 1.0}; - return bc_get_cot(default_color); -} - -COLLADASW::ColorOrTexture bc_get_specular(Material *ma) -{ - Color default_color = {0, 0, 0, 1.0}; - return bc_get_cot(default_color); -} - -COLLADASW::ColorOrTexture bc_get_reflective(Material *ma) -{ - Color default_color = {0, 0, 0, 1.0}; - return bc_get_cot(default_color); -} - -double bc_get_alpha(Material *ma) -{ - double alpha = ma->a; /* fallback if no socket found */ - bNode *master_shader = bc_get_master_shader(ma); - if (ma->use_nodes && master_shader) { - bc_get_float_from_shader(master_shader, alpha, "Alpha"); - } - return alpha; -} - -double bc_get_ior(Material *ma) -{ - double ior = -1; /* fallback if no socket found */ - bNode *master_shader = bc_get_master_shader(ma); - if (ma->use_nodes && master_shader) { - bc_get_float_from_shader(master_shader, ior, "IOR"); - } - return ior; -} - -double bc_get_shininess(Material *ma) -{ - double ior = -1; /* fallback if no socket found */ - bNode *master_shader = bc_get_master_shader(ma); - if (ma->use_nodes && master_shader) { - bc_get_float_from_shader(master_shader, ior, "Roughness"); - } - return ior; -} - -double bc_get_reflectivity(Material *ma) -{ - double reflectivity = ma->spec; /* fallback if no socket found */ - bNode *master_shader = bc_get_master_shader(ma); - if (ma->use_nodes && master_shader) { - bc_get_float_from_shader(master_shader, reflectivity, "Metallic"); - } - return reflectivity; -} - -double bc_get_float_from_shader(bNode *shader, double &val, std::string nodeid) -{ - bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); - if (socket) { - bNodeSocketValueFloat *ref = (bNodeSocketValueFloat *)socket->default_value; - val = (double)ref->value; - return true; - } - return false; -} - -COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, - std::string nodeid, - Color &default_color, - bool with_alpha) -{ - bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); - if (socket) { - bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value; - float *col = dcol->value; - return bc_get_cot(col, with_alpha); - } - else { - return bc_get_cot(default_color, with_alpha); - } -} - -bNode *bc_get_master_shader(Material *ma) -{ - bNodeTree *nodetree = ma->nodetree; - if (nodetree) { - for (bNode *node = (bNode *)nodetree->nodes.first; node; node = node->next) { - if (node->typeinfo->type == SH_NODE_BSDF_PRINCIPLED) { - return node; - } - } - } - return NULL; -} - -COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a) -{ - COLLADASW::Color color(r, g, b, a); - COLLADASW::ColorOrTexture cot(color); - return cot; -} - -COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha) -{ - COLLADASW::Color color(col[0], col[1], col[2], (with_alpha) ? col[3] : 1.0); - COLLADASW::ColorOrTexture cot(color); - return cot; -} diff --git a/source/blender/collada/collada_utils.h b/source/blender/collada/collada_utils.h deleted file mode 100644 index 5c5e1415422..00000000000 --- a/source/blender/collada/collada_utils.h +++ /dev/null @@ -1,399 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -/** \file - * \ingroup collada - */ - -#ifndef __COLLADA_UTILS_H__ -#define __COLLADA_UTILS_H__ - -#include "COLLADAFWMeshPrimitive.h" -#include "COLLADAFWGeometry.h" -#include "COLLADAFWFloatOrDoubleArray.h" -#include "COLLADAFWTypes.h" -#include "COLLADASWEffectProfile.h" -#include "COLLADAFWColorOrTexture.h" - -#include -#include -#include -#include - -extern "C" { -#include "DNA_object_types.h" -#include "DNA_anim_types.h" -#include "DNA_constraint_types.h" -#include "DNA_mesh_types.h" -#include "DNA_light_types.h" -#include "DNA_camera_types.h" - -#include "DNA_customdata_types.h" -#include "DNA_texture_types.h" -#include "DNA_scene_types.h" - -#include "RNA_access.h" - -#include "BLI_linklist.h" -#include "BLI_utildefines.h" -#include "BLI_string.h" - -#include "BKE_main.h" -#include "BKE_context.h" -#include "BKE_object.h" -#include "BKE_scene.h" -#include "BKE_idprop.h" -#include "BKE_node.h" -} - -#include "DEG_depsgraph_query.h" - -#include "ImportSettings.h" -#include "ExportSettings.h" -#include "collada_internal.h" -#include "BCSampleData.h" -#include "BlenderContext.h" - -constexpr int LIMITTED_PRECISION = 6; - -typedef std::map UidImageMap; -typedef std::map KeyImageMap; -typedef std::map> TexIndexTextureArrayMap; -typedef std::set BCObjectSet; - -extern void bc_update_scene(BlenderContext &blender_context, float ctime); - -/* Action helpers */ - -std::vector bc_getSceneActions(const bContext *C, Object *ob, bool all_actions); - -/* Action helpers */ - -inline bAction *bc_getSceneObjectAction(Object *ob) -{ - return (ob->adt && ob->adt->action) ? ob->adt->action : NULL; -} - -/* Returns Light Action or NULL */ -inline bAction *bc_getSceneLightAction(Object *ob) -{ - if (ob->type != OB_LAMP) { - return NULL; - } - - Light *lamp = (Light *)ob->data; - return (lamp->adt && lamp->adt->action) ? lamp->adt->action : NULL; -} - -/* Return Camera Action or NULL */ -inline bAction *bc_getSceneCameraAction(Object *ob) -{ - if (ob->type != OB_CAMERA) { - return NULL; - } - - Camera *camera = (Camera *)ob->data; - return (camera->adt && camera->adt->action) ? camera->adt->action : NULL; -} - -/* returns material action or NULL */ -inline bAction *bc_getSceneMaterialAction(Material *ma) -{ - if (ma == NULL) { - return NULL; - } - - return (ma->adt && ma->adt->action) ? ma->adt->action : NULL; -} - -std::string bc_get_action_id(std::string action_name, - std::string ob_name, - std::string channel_type, - std::string axis_name, - std::string axis_separator = "_"); - -extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray &array, unsigned int index); -extern int bc_test_parent_loop(Object *par, Object *ob); - -extern bool bc_validateConstraints(bConstraint *con); - -bool bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true); -extern Object *bc_add_object( - Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name); -extern Mesh *bc_get_mesh_copy(BlenderContext &blender_context, - Object *ob, - BC_export_mesh_type export_mesh_type, - bool apply_modifiers, - bool triangulate); - -extern Object *bc_get_assigned_armature(Object *ob); -extern bool bc_has_object_type(LinkNode *export_set, short obtype); - -extern char *bc_CustomData_get_layer_name(const CustomData *data, int type, int n); -extern char *bc_CustomData_get_active_layer_name(const CustomData *data, int type); - -extern void bc_bubble_sort_by_Object_name(LinkNode *export_set); -extern bool bc_is_root_bone(Bone *aBone, bool deform_bones_only); -extern int bc_get_active_UVLayer(Object *ob); - -std::string bc_find_bonename_in_path(std::string path, std::string probe); - -inline std::string bc_string_after(const std::string &s, const std::string probe) -{ - size_t i = s.rfind(probe); - if (i != std::string::npos) { - return (s.substr(i + probe.length(), s.length() - i)); - } - return (s); -} - -inline std::string bc_string_before(const std::string &s, const std::string probe) -{ - size_t i = s.find(probe); - if (i != std::string::npos) { - return s.substr(0, i); - } - return (s); -} - -inline bool bc_startswith(std::string const &value, std::string const &starting) -{ - if (starting.size() > value.size()) { - return false; - } - return (value.substr(0, starting.size()) == starting); -} - -inline bool bc_endswith(const std::string &value, const std::string &ending) -{ - if (ending.size() > value.size()) { - return false; - } - - return value.compare(value.size() - ending.size(), ending.size(), ending) == 0; -} - -#if 0 /* UNUSED */ -inline bool bc_endswith(std::string const &value, std::string const &ending) -{ - if (ending.size() > value.size()) { - return false; - } - return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); -} -#endif - -extern std::string bc_replace_string(std::string data, - const std::string &pattern, - const std::string &replacement); -extern std::string bc_url_encode(std::string data); -extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene); -extern void bc_match_scale(std::vector *objects_done, - UnitConverter &unit_converter, - bool scale_to_scene); - -extern void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size); -extern void bc_rotate_from_reference_quat(float quat_to[4], - float quat_from[4], - float mat_to[4][4]); - -extern void bc_triangulate_mesh(Mesh *me); -extern bool bc_is_leaf_bone(Bone *bone); -extern EditBone *bc_get_edit_bone(bArmature *armature, char *name); -extern int bc_set_layer(int bitfield, int layer, bool enable); -extern int bc_set_layer(int bitfield, int layer); - -inline bool bc_in_range(float a, float b, float range) -{ - return fabsf(a - b) < range; -} -void bc_copy_m4_farray(float r[4][4], float *a); -void bc_copy_farray_m4(float *r, float a[4][4]); -void bc_copy_darray_m4d(double *r, double a[4][4]); -void bc_copy_m4d_v44(double (&r)[4][4], std::vector> &a); -void bc_copy_v44_m4d(std::vector> &a, double (&r)[4][4]); - -void bc_sanitize_v3(double v[3], int precision); -void bc_sanitize_v3(float v[3], int precision); - -extern IDProperty *bc_get_IDProperty(Bone *bone, std::string key); -extern void bc_set_IDProperty(EditBone *ebone, const char *key, float value); -extern void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]); - -extern float bc_get_property(Bone *bone, std::string key, float def); -extern void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]); -extern bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]); - -extern void bc_enable_fcurves(bAction *act, char *bone_name); -extern bool bc_bone_matrix_local_get(Object *ob, Bone *bone, Matrix &mat, bool for_opensim); -extern bool bc_is_animated(BCMatrixSampleMap &values); -extern bool bc_has_animations(Scene *sce, LinkNode *node); -extern bool bc_has_animations(Object *ob); - -extern void bc_add_global_transform(Matrix &to_mat, - const Matrix &from_mat, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_add_global_transform(Vector &to_vec, - const Vector &from_vec, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_add_global_transform(Vector &to_vec, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_add_global_transform(Matrix &to_mat, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_apply_global_transform(Matrix &to_mat, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_apply_global_transform(Vector &to_vec, - const BCMatrix &global_transform, - const bool invert = false); -extern void bc_create_restpose_mat(BCExportSettings &export_settings, - Bone *bone, - float to_mat[4][4], - float from_mat[4][4], - bool use_local_space); - -class ColladaBaseNodes { - private: - std::vector base_objects; - - public: - void add(Object *ob) - { - base_objects.push_back(ob); - } - - bool contains(Object *ob) - { - std::vector::iterator it = std::find(base_objects.begin(), base_objects.end(), ob); - return (it != base_objects.end()); - } - - int size() - { - return base_objects.size(); - } - - Object *get(int index) - { - return base_objects[index]; - } -}; - -class BCPolygonNormalsIndices { - std::vector normal_indices; - - public: - void add_index(unsigned int index) - { - normal_indices.push_back(index); - } - - unsigned int operator[](unsigned int i) - { - return normal_indices[i]; - } -}; - -class BoneExtended { - - private: - char name[MAXBONENAME]; - int chain_length; - bool is_leaf; - float tail[3]; - float roll; - - int bone_layers; - int use_connect; - bool has_custom_tail; - bool has_custom_roll; - - public: - BoneExtended(EditBone *aBone); - - void set_name(char *aName); - char *get_name(); - - void set_chain_length(const int aLength); - int get_chain_length(); - - void set_leaf_bone(bool state); - bool is_leaf_bone(); - - void set_bone_layers(std::string layers, std::vector &layer_labels); - int get_bone_layers(); - static std::string get_bone_layers(int bitfield); - - void set_roll(float roll); - bool has_roll(); - float get_roll(); - - void set_tail(float vec[]); - float *get_tail(); - bool has_tail(); - - void set_use_connect(int use_connect); - int get_use_connect(); -}; - -/* a map to store bone extension maps - * std:string : an armature name - * BoneExtended * : a map that contains extra data for bones - */ -typedef std::map BoneExtensionMap; - -/* - * A class to organize bone extension data for multiple Armatures. - * this is needed for the case where a Collada file contains 2 or more - * separate armatures. - */ -class BoneExtensionManager { - private: - std::map extended_bone_maps; - - public: - BoneExtensionMap &getExtensionMap(bArmature *armature); - ~BoneExtensionManager(); -}; - -void bc_add_default_shader(bContext *C, Material *ma); -bNode *bc_get_master_shader(Material *ma); - -COLLADASW::ColorOrTexture bc_get_base_color(Material *ma); -COLLADASW::ColorOrTexture bc_get_emission(Material *ma); -COLLADASW::ColorOrTexture bc_get_ambient(Material *ma); -COLLADASW::ColorOrTexture bc_get_specular(Material *ma); -COLLADASW::ColorOrTexture bc_get_reflective(Material *ma); - -double bc_get_reflectivity(Material *ma); -double bc_get_alpha(Material *ma); -double bc_get_ior(Material *ma); -double bc_get_shininess(Material *ma); - -double bc_get_float_from_shader(bNode *shader, double &ior, std::string nodeid); -COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, - std::string nodeid, - Color &default_color, - bool with_alpha = true); - -COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a); -COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha = true); - -#endif diff --git a/source/blender/collada/version.conf b/source/blender/collada/version.conf deleted file mode 100644 index d39af7a53df..00000000000 --- a/source/blender/collada/version.conf +++ /dev/null @@ -1 +0,0 @@ -463ba8a2ef5a021ce21df614dde29e0ee800e10b diff --git a/source/blender/editors/io/CMakeLists.txt b/source/blender/editors/io/CMakeLists.txt index 5afe348158f..39548449a86 100644 --- a/source/blender/editors/io/CMakeLists.txt +++ b/source/blender/editors/io/CMakeLists.txt @@ -17,16 +17,16 @@ set(INC ../include - ../../alembic ../../blenkernel ../../blenlib ../../blentranslation ../../bmesh - ../../collada ../../depsgraph + ../../io/alembic + ../../io/collada + ../../io/usd ../../makesdna ../../makesrna - ../../usd ../../windowmanager ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c index 3f51504d6ac..15fee4db73f 100644 --- a/source/blender/editors/io/io_collada.c +++ b/source/blender/editors/io/io_collada.c @@ -48,7 +48,7 @@ # include "WM_api.h" # include "WM_types.h" -# include "../../collada/collada.h" +# include "collada.h" # include "io_collada.h" diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index 7aab644fc12..cad0be659ec 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -143,7 +143,7 @@ endif() if(WITH_CODEC_AVI) list(APPEND INC - ../avi + ../io/avi ) list(APPEND LIB bf_avi diff --git a/source/blender/io/CMakeLists.txt b/source/blender/io/CMakeLists.txt new file mode 100644 index 00000000000..bc2f8d628e2 --- /dev/null +++ b/source/blender/io/CMakeLists.txt @@ -0,0 +1,35 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2020, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +if(WITH_ALEMBIC) + add_subdirectory(alembic) +endif() + +if(WITH_CODEC_AVI) + add_subdirectory(avi) +endif() + +if(WITH_OPENCOLLADA) + add_subdirectory(collada) +endif() + +if(WITH_USD) + add_subdirectory(usd) +endif() diff --git a/source/blender/io/alembic/ABC_alembic.h b/source/blender/io/alembic/ABC_alembic.h new file mode 100644 index 00000000000..878dbfc2a53 --- /dev/null +++ b/source/blender/io/alembic/ABC_alembic.h @@ -0,0 +1,141 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_ALEMBIC_H__ +#define __ABC_ALEMBIC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct CacheReader; +struct ListBase; +struct Main; +struct Mesh; +struct Object; +struct Scene; +struct bContext; + +typedef struct AbcArchiveHandle AbcArchiveHandle; + +enum { + ABC_ARCHIVE_OGAWA = 0, + ABC_ARCHIVE_HDF5 = 1, +}; + +int ABC_get_version(void); + +struct AlembicExportParams { + double frame_start; + double frame_end; + + unsigned int frame_samples_xform; + unsigned int frame_samples_shape; + + double shutter_open; + double shutter_close; + + bool selected_only; + bool uvs; + bool normals; + bool vcolors; + bool apply_subdiv; + bool curves_as_mesh; + bool flatten_hierarchy; + bool visible_objects_only; + bool renderable_only; + bool face_sets; + bool use_subdiv_schema; + bool packuv; + bool triangulate; + bool export_hair; + bool export_particles; + + unsigned int compression_type : 1; + + /* See MOD_TRIANGULATE_NGON_xxx and MOD_TRIANGULATE_QUAD_xxx + * in DNA_modifier_types.h */ + int quad_method; + int ngon_method; + + float global_scale; +}; + +/* The ABC_export and ABC_import functions both take a as_background_job + * parameter, and return a boolean. + * + * When as_background_job=true, returns false immediately after scheduling + * a background job. + * + * When as_background_job=false, performs the export synchronously, and returns + * true when the export was ok, and false if there were any errors. + */ + +bool ABC_export(struct Scene *scene, + struct bContext *C, + const char *filepath, + const struct AlembicExportParams *params, + bool as_background_job); + +bool ABC_import(struct bContext *C, + const char *filepath, + float scale, + bool is_sequence, + bool set_frame_range, + int sequence_len, + int offset, + bool validate_meshes, + bool as_background_job); + +AbcArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + struct ListBase *object_paths); + +void ABC_free_handle(AbcArchiveHandle *handle); + +void ABC_get_transform(struct CacheReader *reader, float r_mat[4][4], float time, float scale); + +/* Either modifies current_mesh in-place or constructs a new mesh. */ +struct Mesh *ABC_read_mesh(struct CacheReader *reader, + struct Object *ob, + struct Mesh *current_mesh, + const float time, + const char **err_str, + int flags); + +bool ABC_mesh_topology_changed(struct CacheReader *reader, + struct Object *ob, + struct Mesh *existing_mesh, + const float time, + const char **err_str); + +void CacheReader_incref(struct CacheReader *reader); +void CacheReader_free(struct CacheReader *reader); + +struct CacheReader *CacheReader_open_alembic_object(struct AbcArchiveHandle *handle, + struct CacheReader *reader, + struct Object *object, + const char *object_path); + +#ifdef __cplusplus +} +#endif + +#endif /* __ABC_ALEMBIC_H__ */ diff --git a/source/blender/io/alembic/CMakeLists.txt b/source/blender/io/alembic/CMakeLists.txt new file mode 100644 index 00000000000..cbcdfaf4b77 --- /dev/null +++ b/source/blender/io/alembic/CMakeLists.txt @@ -0,0 +1,110 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../../blenkernel + ../../blenlib + ../../blenloader + ../../bmesh + ../../depsgraph + ../../editors/include + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/utfconv +) + +set(INC_SYS + ${ALEMBIC_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIR} + ${HDF5_INCLUDE_DIRS} + ${OPENEXR_INCLUDE_DIRS} +) + +set(SRC + intern/abc_customdata.cc + intern/abc_exporter.cc + intern/abc_reader_archive.cc + intern/abc_reader_camera.cc + intern/abc_reader_curves.cc + intern/abc_reader_mesh.cc + intern/abc_reader_nurbs.cc + intern/abc_reader_object.cc + intern/abc_reader_points.cc + intern/abc_reader_transform.cc + intern/abc_util.cc + intern/abc_writer_archive.cc + intern/abc_writer_camera.cc + intern/abc_writer_curves.cc + intern/abc_writer_hair.cc + intern/abc_writer_mball.cc + intern/abc_writer_mesh.cc + intern/abc_writer_nurbs.cc + intern/abc_writer_object.cc + intern/abc_writer_points.cc + intern/abc_writer_transform.cc + intern/alembic_capi.cc + + ABC_alembic.h + intern/abc_customdata.h + intern/abc_exporter.h + intern/abc_reader_archive.h + intern/abc_reader_camera.h + intern/abc_reader_curves.h + intern/abc_reader_mesh.h + intern/abc_reader_nurbs.h + intern/abc_reader_object.h + intern/abc_reader_points.h + intern/abc_reader_transform.h + intern/abc_util.h + intern/abc_writer_archive.h + intern/abc_writer_camera.h + intern/abc_writer_curves.h + intern/abc_writer_hair.h + intern/abc_writer_mball.h + intern/abc_writer_mesh.h + intern/abc_writer_nurbs.h + intern/abc_writer_object.h + intern/abc_writer_points.h + intern/abc_writer_transform.h +) + +set(LIB + bf_blenkernel + bf_blenlib + + ${ALEMBIC_LIBRARIES} + ${OPENEXR_LIBRARIES} +) + +if(WITH_ALEMBIC_HDF5) + add_definitions(-DWITH_ALEMBIC_HDF5) + list(APPEND LIB + ${HDF5_LIBRARIES} + ) +endif() + +list(APPEND LIB + ${BOOST_LIBRARIES} +) + +blender_add_lib(bf_alembic "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc new file mode 100644 index 00000000000..c5f60ac3e29 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -0,0 +1,484 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_customdata.h" + +#include +#include +#include + +extern "C" { +#include "DNA_customdata_types.h" +#include "DNA_meshdata_types.h" + +#include "BLI_math_base.h" +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +} + +/* NOTE: for now only UVs and Vertex Colors are supported for streaming. + * Although Alembic only allows for a single UV layer per {I|O}Schema, and does + * not have a vertex color concept, there is a convention between DCCs to write + * such data in a way that lets other DCC know what they are for. See comments + * in the write code for the conventions. */ + +using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::kVertexScope; + +using Alembic::Abc::C4fArraySample; +using Alembic::Abc::UInt32ArraySample; +using Alembic::Abc::V2fArraySample; + +using Alembic::AbcGeom::OC4fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +static void get_uvs(const CDStreamConfig &config, + std::vector &uvs, + std::vector &uvidx, + void *cd_data) +{ + MLoopUV *mloopuv_array = static_cast(cd_data); + + if (!mloopuv_array) { + return; + } + + const int num_poly = config.totpoly; + MPoly *polygons = config.mpoly; + MLoop *mloop = config.mloop; + + if (!config.pack_uvs) { + int cnt = 0; + uvidx.resize(config.totloop); + uvs.resize(config.totloop); + + /* Iterate in reverse order to match exported polygons. */ + for (int i = 0; i < num_poly; i++) { + MPoly ¤t_poly = polygons[i]; + MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; j++, cnt++) { + loopuv--; + + uvidx[cnt] = cnt; + uvs[cnt][0] = loopuv->uv[0]; + uvs[cnt][1] = loopuv->uv[1]; + } + } + } + else { + /* Mapping for indexed UVs, deduplicating UV coordinates at vertices. */ + std::vector> idx_map(config.totvert); + int idx_count = 0; + + for (int i = 0; i < num_poly; i++) { + MPoly ¤t_poly = polygons[i]; + MLoop *looppoly = mloop + current_poly.loopstart + current_poly.totloop; + MLoopUV *loopuv = mloopuv_array + current_poly.loopstart + current_poly.totloop; + + for (int j = 0; j < current_poly.totloop; j++) { + looppoly--; + loopuv--; + + Imath::V2f uv(loopuv->uv[0], loopuv->uv[1]); + bool found_same = false; + + /* Find UV already in uvs array. */ + for (uint32_t uv_idx : idx_map[looppoly->v]) { + if (uvs[uv_idx] == uv) { + found_same = true; + uvidx.push_back(uv_idx); + break; + } + } + + /* UV doesn't exists for this vertex, add it. */ + if (!found_same) { + uint32_t uv_idx = idx_count++; + idx_map[looppoly->v].push_back(uv_idx); + uvidx.push_back(uv_idx); + uvs.push_back(uv); + } + } + } + } +} + +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data) +{ + const int active_uvlayer = CustomData_get_active_layer(data, CD_MLOOPUV); + + if (active_uvlayer < 0) { + return ""; + } + + void *cd_data = CustomData_get_layer_n(data, CD_MLOOPUV, active_uvlayer); + + get_uvs(config, sample.uvs, sample.indices, cd_data); + + return CustomData_get_layer_name(data, CD_MLOOPUV, active_uvlayer); +} + +/* Convention to write UVs: + * - V2fGeomParam on the arbGeomParam + * - set scope as face varying + * - (optional due to its behavior) tag as UV using Alembic::AbcGeom::SetIsUV + */ +static void write_uv(const OCompoundProperty &prop, + const CDStreamConfig &config, + void *data, + const char *name) +{ + std::vector indices; + std::vector uvs; + + get_uvs(config, uvs, indices, data); + + if (indices.empty() || uvs.empty()) { + return; + } + + OV2fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OV2fGeomParam::Sample sample(V2fArraySample(&uvs.front(), uvs.size()), + UInt32ArraySample(&indices.front(), indices.size()), + kFacevaryingScope); + + param.set(sample); +} + +/* Convention to write Vertex Colors: + * - C3fGeomParam/C4fGeomParam on the arbGeomParam + * - set scope as vertex varying + */ +static void write_mcol(const OCompoundProperty &prop, + const CDStreamConfig &config, + void *data, + const char *name) +{ + const float cscale = 1.0f / 255.0f; + MPoly *polys = config.mpoly; + MLoop *mloops = config.mloop; + MCol *cfaces = static_cast(data); + + std::vector buffer; + std::vector indices; + + buffer.reserve(config.totvert); + indices.reserve(config.totvert); + + Imath::C4f col; + + for (int i = 0; i < config.totpoly; i++) { + MPoly *p = &polys[i]; + MCol *cface = &cfaces[p->loopstart + p->totloop]; + MLoop *mloop = &mloops[p->loopstart + p->totloop]; + + for (int j = 0; j < p->totloop; j++) { + cface--; + mloop--; + + col[0] = cface->a * cscale; + col[1] = cface->r * cscale; + col[2] = cface->g * cscale; + col[3] = cface->b * cscale; + + buffer.push_back(col); + indices.push_back(buffer.size() - 1); + } + } + + OC4fGeomParam param(prop, name, true, kFacevaryingScope, 1); + + OC4fGeomParam::Sample sample(C4fArraySample(&buffer.front(), buffer.size()), + UInt32ArraySample(&indices.front(), indices.size()), + kVertexScope); + + param.set(sample); +} + +void write_custom_data(const OCompoundProperty &prop, + const CDStreamConfig &config, + CustomData *data, + int data_type) +{ + CustomDataType cd_data_type = static_cast(data_type); + + if (!CustomData_has_layer(data, cd_data_type)) { + return; + } + + const int active_layer = CustomData_get_active_layer(data, cd_data_type); + const int tot_layers = CustomData_number_of_layers(data, cd_data_type); + + for (int i = 0; i < tot_layers; i++) { + void *cd_data = CustomData_get_layer_n(data, cd_data_type, i); + const char *name = CustomData_get_layer_name(data, cd_data_type, i); + + if (cd_data_type == CD_MLOOPUV) { + /* Already exported. */ + if (i == active_layer) { + continue; + } + + write_uv(prop, config, cd_data, name); + } + else if (cd_data_type == CD_MLOOPCOL) { + write_mcol(prop, config, cd_data, name); + } + } +} + +/* ************************************************************************** */ + +using Alembic::Abc::C3fArraySamplePtr; +using Alembic::Abc::C4fArraySamplePtr; +using Alembic::Abc::PropertyHeader; + +using Alembic::AbcGeom::IC3fGeomParam; +using Alembic::AbcGeom::IC4fGeomParam; +using Alembic::AbcGeom::IV2fGeomParam; + +static void read_uvs(const CDStreamConfig &config, + void *data, + const Alembic::AbcGeom::V2fArraySamplePtr &uvs, + const Alembic::AbcGeom::UInt32ArraySamplePtr &indices) +{ + MPoly *mpolys = config.mpoly; + MLoopUV *mloopuvs = static_cast(data); + + unsigned int uv_index, loop_index, rev_loop_index; + + for (int i = 0; i < config.totpoly; i++) { + MPoly &poly = mpolys[i]; + unsigned int rev_loop_offset = poly.loopstart + poly.totloop - 1; + + for (int f = 0; f < poly.totloop; f++) { + loop_index = poly.loopstart + f; + rev_loop_index = rev_loop_offset - f; + uv_index = (*indices)[loop_index]; + const Imath::V2f &uv = (*uvs)[uv_index]; + + MLoopUV &loopuv = mloopuvs[rev_loop_index]; + loopuv.uv[0] = uv[0]; + loopuv.uv[1] = uv[1]; + } + } +} + +static size_t mcols_out_of_bounds_check(const size_t color_index, + const size_t array_size, + const std::string &iobject_full_name, + const PropertyHeader &prop_header, + bool &r_is_out_of_bounds, + bool &r_bounds_warning_given) +{ + if (color_index < array_size) { + return color_index; + } + + if (!r_bounds_warning_given) { + std::cerr << "Alembic: color index out of bounds " + "reading face colors for object " + << iobject_full_name << ", property " << prop_header.getName() << std::endl; + r_bounds_warning_given = true; + } + r_is_out_of_bounds = true; + return 0; +} + +static void read_custom_data_mcols(const std::string &iobject_full_name, + const ICompoundProperty &arbGeomParams, + const PropertyHeader &prop_header, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss) +{ + C3fArraySamplePtr c3f_ptr = C3fArraySamplePtr(); + C4fArraySamplePtr c4f_ptr = C4fArraySamplePtr(); + Alembic::Abc::UInt32ArraySamplePtr indices; + bool use_c3f_ptr; + bool is_facevarying; + + /* Find the correct interpretation of the data */ + if (IC3fGeomParam::matches(prop_header)) { + IC3fGeomParam color_param(arbGeomParams, prop_header.getName()); + IC3fGeomParam::Sample sample; + BLI_assert(!strcmp("rgb", color_param.getInterpretation())); + + color_param.getIndexed(sample, iss); + is_facevarying = sample.getScope() == kFacevaryingScope && + config.totloop == sample.getIndices()->size(); + + c3f_ptr = sample.getVals(); + indices = sample.getIndices(); + use_c3f_ptr = true; + } + else if (IC4fGeomParam::matches(prop_header)) { + IC4fGeomParam color_param(arbGeomParams, prop_header.getName()); + IC4fGeomParam::Sample sample; + BLI_assert(!strcmp("rgba", color_param.getInterpretation())); + + color_param.getIndexed(sample, iss); + is_facevarying = sample.getScope() == kFacevaryingScope && + config.totloop == sample.getIndices()->size(); + + c4f_ptr = sample.getVals(); + indices = sample.getIndices(); + use_c3f_ptr = false; + } + else { + /* this won't happen due to the checks in read_custom_data() */ + return; + } + BLI_assert(c3f_ptr || c4f_ptr); + + /* Read the vertex colors */ + void *cd_data = config.add_customdata_cb( + config.mesh, prop_header.getName().c_str(), CD_MLOOPCOL); + MCol *cfaces = static_cast(cd_data); + MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; + + size_t face_index = 0; + size_t color_index; + bool bounds_warning_given = false; + + /* The colors can go through two layers of indexing. Often the 'indices' + * array doesn't do anything (i.e. indices[n] = n), but when it does, it's + * important. Blender 2.79 writes indices incorrectly (see T53745), which + * is why we have to check for indices->size() > 0 */ + bool use_dual_indexing = is_facevarying && indices->size() > 0; + + for (int i = 0; i < config.totpoly; i++) { + MPoly *poly = &mpolys[i]; + MCol *cface = &cfaces[poly->loopstart + poly->totloop]; + MLoop *mloop = &mloops[poly->loopstart + poly->totloop]; + + for (int j = 0; j < poly->totloop; j++, face_index++) { + cface--; + mloop--; + + color_index = is_facevarying ? face_index : mloop->v; + if (use_dual_indexing) { + color_index = (*indices)[color_index]; + } + if (use_c3f_ptr) { + bool is_mcols_out_of_bounds = false; + color_index = mcols_out_of_bounds_check(color_index, + c3f_ptr->size(), + iobject_full_name, + prop_header, + is_mcols_out_of_bounds, + bounds_warning_given); + if (is_mcols_out_of_bounds) { + continue; + } + const Imath::C3f &color = (*c3f_ptr)[color_index]; + cface->a = unit_float_to_uchar_clamp(color[0]); + cface->r = unit_float_to_uchar_clamp(color[1]); + cface->g = unit_float_to_uchar_clamp(color[2]); + cface->b = 255; + } + else { + bool is_mcols_out_of_bounds = false; + color_index = mcols_out_of_bounds_check(color_index, + c4f_ptr->size(), + iobject_full_name, + prop_header, + is_mcols_out_of_bounds, + bounds_warning_given); + if (is_mcols_out_of_bounds) { + continue; + } + const Imath::C4f &color = (*c4f_ptr)[color_index]; + cface->a = unit_float_to_uchar_clamp(color[0]); + cface->r = unit_float_to_uchar_clamp(color[1]); + cface->g = unit_float_to_uchar_clamp(color[2]); + cface->b = unit_float_to_uchar_clamp(color[3]); + } + } + } +} + +static void read_custom_data_uvs(const ICompoundProperty &prop, + const PropertyHeader &prop_header, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss) +{ + IV2fGeomParam uv_param(prop, prop_header.getName()); + + if (!uv_param.isIndexed()) { + return; + } + + IV2fGeomParam::Sample sample; + uv_param.getIndexed(sample, iss); + + if (uv_param.getScope() != kFacevaryingScope) { + return; + } + + void *cd_data = config.add_customdata_cb(config.mesh, prop_header.getName().c_str(), CD_MLOOPUV); + + read_uvs(config, cd_data, sample.getVals(), sample.getIndices()); +} + +void read_custom_data(const std::string &iobject_full_name, + const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss) +{ + if (!prop.valid()) { + return; + } + + int num_uvs = 0; + int num_colors = 0; + + const size_t num_props = prop.getNumProperties(); + + for (size_t i = 0; i < num_props; i++) { + const Alembic::Abc::PropertyHeader &prop_header = prop.getPropertyHeader(i); + + /* Read UVs according to convention. */ + if (IV2fGeomParam::matches(prop_header) && Alembic::AbcGeom::isUV(prop_header)) { + if (++num_uvs > MAX_MTFACE) { + continue; + } + + read_custom_data_uvs(prop, prop_header, config, iss); + continue; + } + + /* Read vertex colors according to convention. */ + if (IC3fGeomParam::matches(prop_header) || IC4fGeomParam::matches(prop_header)) { + if (++num_colors > MAX_MCOL) { + continue; + } + + read_custom_data_mcols(iobject_full_name, prop, prop_header, config, iss); + continue; + } + } +} diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h new file mode 100644 index 00000000000..6107e230627 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -0,0 +1,104 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_CUSTOMDATA_H__ +#define __ABC_CUSTOMDATA_H__ + +#include +#include + +struct CustomData; +struct MLoop; +struct MLoopUV; +struct MPoly; +struct MVert; +struct Mesh; + +using Alembic::Abc::ICompoundProperty; +using Alembic::Abc::OCompoundProperty; + +struct UVSample { + std::vector uvs; + std::vector indices; +}; + +struct CDStreamConfig { + MLoop *mloop; + int totloop; + + MPoly *mpoly; + int totpoly; + + MVert *mvert; + int totvert; + + MLoopUV *mloopuv; + + CustomData *loopdata; + + bool pack_uvs; + + /* TODO(kevin): might need a better way to handle adding and/or updating + * custom data such that it updates the custom data holder and its pointers properly. */ + Mesh *mesh; + void *(*add_customdata_cb)(Mesh *mesh, const char *name, int data_type); + + float weight; + float time; + Alembic::AbcGeom::index_t index; + Alembic::AbcGeom::index_t ceil_index; + + CDStreamConfig() + : mloop(NULL), + totloop(0), + mpoly(NULL), + totpoly(0), + totvert(0), + pack_uvs(false), + mesh(NULL), + add_customdata_cb(NULL), + weight(0.0f), + time(0.0f), + index(0), + ceil_index(0) + { + } +}; + +/* Get the UVs for the main UV property on a OSchema. + * Returns the name of the UV layer. + * + * For now the active layer is used, maybe needs a better way to choose this. */ +const char *get_uv_sample(UVSample &sample, const CDStreamConfig &config, CustomData *data); + +void write_custom_data(const OCompoundProperty &prop, + const CDStreamConfig &config, + CustomData *data, + int data_type); + +void read_custom_data(const std::string &iobject_full_name, + const ICompoundProperty &prop, + const CDStreamConfig &config, + const Alembic::Abc::ISampleSelector &iss); + +#endif /* __ABC_CUSTOMDATA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_exporter.cc b/source/blender/io/alembic/intern/abc_exporter.cc new file mode 100644 index 00000000000..a58b0a29e5e --- /dev/null +++ b/source/blender/io/alembic/intern/abc_exporter.cc @@ -0,0 +1,677 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_exporter.h" + +#include + +#include "abc_writer_archive.h" +#include "abc_writer_camera.h" +#include "abc_writer_curves.h" +#include "abc_writer_hair.h" +#include "abc_writer_mball.h" +#include "abc_writer_mesh.h" +#include "abc_writer_nurbs.h" +#include "abc_writer_points.h" +#include "abc_writer_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_curve_types.h" +#include "DNA_meta_types.h" +#include "DNA_mesh_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ +#include "DNA_fluid_types.h" + +#include "BLI_string.h" + +#ifdef WIN32 +/* needed for MSCV because of snprintf from BLI_string */ +# include "BLI_winstuff.h" +#endif + +#include "BKE_anim.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_layer.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_modifier.h" +#include "BKE_particle.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph_query.h" +} + +using Alembic::Abc::OBox3dProperty; +using Alembic::Abc::TimeSamplingPtr; + +/* ************************************************************************** */ + +ExportSettings::ExportSettings() + : scene(NULL), + view_layer(NULL), + depsgraph(NULL), + logger(), + selected_only(false), + visible_objects_only(false), + renderable_only(false), + frame_start(1), + frame_end(1), + frame_samples_xform(1), + frame_samples_shape(1), + shutter_open(0.0), + shutter_close(1.0), + global_scale(1.0f), + flatten_hierarchy(false), + export_normals(false), + export_uvs(false), + export_vcols(false), + export_face_sets(false), + export_vweigths(false), + export_hair(true), + export_particles(true), + apply_subdiv(false), + use_subdiv_schema(false), + export_child_hairs(true), + export_ogawa(true), + pack_uv(false), + triangulate(false), + quad_method(0), + ngon_method(0) +{ +} + +static bool object_is_smoke_sim(Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluid); + + if (md) { + FluidModifierData *smd = reinterpret_cast(md); + return (smd->type == MOD_FLUID_TYPE_DOMAIN && smd->domain && + smd->domain->type == FLUID_DOMAIN_TYPE_GAS); + } + + return false; +} + +static bool object_type_is_exportable(Scene *scene, Object *ob) +{ + switch (ob->type) { + case OB_MESH: + if (object_is_smoke_sim(ob)) { + return false; + } + + return true; + case OB_EMPTY: + case OB_CURVE: + case OB_SURF: + case OB_CAMERA: + return true; + case OB_MBALL: + return AbcMBallWriter::isBasisBall(scene, ob); + default: + return false; + } +} + +/** + * Returns whether this object should be exported into the Alembic file. + * + * \param settings: export settings, used for options like 'selected only'. + * \param ob: the object's base in question. + * \param is_duplicated: Normally false; true when the object is instanced + * into the scene by a dupli-object (e.g. part of a dupligroup). + * This ignores selection and layer visibility, + * and assumes that the dupli-object itself (e.g. the group-instantiating empty) is exported. + */ +static bool export_object(const ExportSettings *const settings, + const Base *const base, + bool is_duplicated) +{ + if (!is_duplicated) { + View3D *v3d = NULL; + + /* These two tests only make sense when the object isn't being instanced + * into the scene. When it is, its exportability is determined by + * its dupli-object and the DupliObject::no_draw property. */ + if (settings->selected_only && !BASE_SELECTED(v3d, base)) { + return false; + } + // FIXME Sybren: handle these cleanly (maybe just remove code), + // now using active scene layer instead. + if (settings->visible_objects_only && !BASE_VISIBLE(v3d, base)) { + return false; + } + } + + Object *ob_eval = DEG_get_evaluated_object(settings->depsgraph, base->object); + if ((ob_eval->id.tag & LIB_TAG_COPIED_ON_WRITE) == 0) { + /* XXX fix after 2.80: the object was not part of the depsgraph, and thus we cannot get the + * evaluated copy to export. This will be handled more elegantly in the new + * AbstractHierarchyIterator that Sybren is working on. This condition is temporary, and avoids + * a BLI_assert() failure getting the evaluated mesh of this object. */ + return false; + } + + // if (settings->renderable_only && (ob->restrictflag & OB_RESTRICT_RENDER)) { + // return false; + // } + + return true; +} + +/* ************************************************************************** */ + +AbcExporter::AbcExporter(Main *bmain, const char *filename, ExportSettings &settings) + : m_bmain(bmain), + m_settings(settings), + m_filename(filename), + m_trans_sampling_index(0), + m_shape_sampling_index(0), + m_writer(NULL) +{ +} + +AbcExporter::~AbcExporter() +{ + /* Free xforms map */ + m_xforms_type::iterator it_x, e_x; + for (it_x = m_xforms.begin(), e_x = m_xforms.end(); it_x != e_x; ++it_x) { + delete it_x->second; + } + + /* Free shapes vector */ + for (int i = 0, e = m_shapes.size(); i != e; i++) { + delete m_shapes[i]; + } + + delete m_writer; +} + +void AbcExporter::getShutterSamples(unsigned int nr_of_samples, + bool time_relative, + std::vector &samples) +{ + Scene *scene = m_settings.scene; /* for use in the FPS macro */ + samples.clear(); + + unsigned int frame_offset = time_relative ? m_settings.frame_start : 0; + double time_factor = time_relative ? FPS : 1.0; + double shutter_open = m_settings.shutter_open; + double shutter_close = m_settings.shutter_close; + double time_inc = (shutter_close - shutter_open) / nr_of_samples; + + /* sample between shutter open & close */ + for (int sample = 0; sample < nr_of_samples; sample++) { + double sample_time = shutter_open + time_inc * sample; + double time = (frame_offset + sample_time) / time_factor; + + samples.push_back(time); + } +} + +Alembic::Abc::TimeSamplingPtr AbcExporter::createTimeSampling(double step) +{ + std::vector samples; + + if (m_settings.frame_start == m_settings.frame_end) { + return TimeSamplingPtr(new Alembic::Abc::TimeSampling()); + } + + getShutterSamples(step, true, samples); + + /* TODO(Sybren): shouldn't we use the FPS macro here? */ + Alembic::Abc::TimeSamplingType ts(static_cast(samples.size()), + 1.0 / m_settings.scene->r.frs_sec); + + return TimeSamplingPtr(new Alembic::Abc::TimeSampling(ts, samples)); +} + +void AbcExporter::getFrameSet(unsigned int nr_of_samples, std::set &frames) +{ + frames.clear(); + + std::vector shutter_samples; + + getShutterSamples(nr_of_samples, false, shutter_samples); + + for (double frame = m_settings.frame_start; frame <= m_settings.frame_end; frame += 1.0) { + for (size_t j = 0; j < nr_of_samples; j++) { + frames.insert(frame + shutter_samples[j]); + } + } +} + +void AbcExporter::operator()(short *do_update, float *progress, bool *was_canceled) +{ + std::string abc_scene_name; + + if (m_bmain->name[0] != '\0') { + char scene_file_name[FILE_MAX]; + BLI_strncpy(scene_file_name, m_bmain->name, FILE_MAX); + abc_scene_name = scene_file_name; + } + else { + abc_scene_name = "untitled"; + } + + m_writer = new ArchiveWriter( + m_filename, abc_scene_name, m_settings.scene, m_settings.export_ogawa); + + /* Create time samplings for transforms and shapes. */ + + TimeSamplingPtr trans_time = createTimeSampling(m_settings.frame_samples_xform); + + m_trans_sampling_index = m_writer->archive().addTimeSampling(*trans_time); + + TimeSamplingPtr shape_time; + + if ((m_settings.frame_samples_shape == m_settings.frame_samples_xform) || + (m_settings.frame_start == m_settings.frame_end)) { + shape_time = trans_time; + m_shape_sampling_index = m_trans_sampling_index; + } + else { + shape_time = createTimeSampling(m_settings.frame_samples_shape); + m_shape_sampling_index = m_writer->archive().addTimeSampling(*shape_time); + } + + OBox3dProperty archive_bounds_prop = Alembic::AbcGeom::CreateOArchiveBounds( + m_writer->archive(), m_trans_sampling_index); + + createTransformWritersHierarchy(); + createShapeWriters(); + + /* Make a list of frames to export. */ + + std::set xform_frames; + getFrameSet(m_settings.frame_samples_xform, xform_frames); + + std::set shape_frames; + getFrameSet(m_settings.frame_samples_shape, shape_frames); + + /* Merge all frames needed. */ + std::set frames(xform_frames); + frames.insert(shape_frames.begin(), shape_frames.end()); + + /* Export all frames. */ + + std::set::const_iterator begin = frames.begin(); + std::set::const_iterator end = frames.end(); + + const float size = static_cast(frames.size()); + size_t i = 0; + + for (; begin != end; ++begin) { + *progress = (++i / size); + *do_update = 1; + + if (G.is_break) { + *was_canceled = true; + break; + } + + const double frame = *begin; + + /* 'frame' is offset by start frame, so need to cancel the offset. */ + setCurrentFrame(m_bmain, frame); + + if (shape_frames.count(frame) != 0) { + for (int i = 0, e = m_shapes.size(); i != e; i++) { + m_shapes[i]->write(); + } + } + + if (xform_frames.count(frame) == 0) { + continue; + } + + m_xforms_type::iterator xit, xe; + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + xit->second->write(); + } + + /* Save the archive 's bounding box. */ + Imath::Box3d bounds; + + for (xit = m_xforms.begin(), xe = m_xforms.end(); xit != xe; ++xit) { + Imath::Box3d box = xit->second->bounds(); + bounds.extendBy(box); + } + + archive_bounds_prop.set(bounds); + } +} + +void AbcExporter::createTransformWritersHierarchy() +{ + for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; + base = base->next) { + Object *ob = base->object; + + if (export_object(&m_settings, base, false)) { + switch (ob->type) { + case OB_LAMP: + case OB_LATTICE: + case OB_SPEAKER: + /* We do not export transforms for objects of these classes. */ + break; + default: + exploreTransform(base, ob, ob->parent, NULL); + } + } + } +} + +void AbcExporter::exploreTransform(Base *base, + Object *object, + Object *parent, + Object *dupliObParent) +{ + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, base, dupliObParent != NULL)) { + return; + } + + Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); + if (object_type_is_exportable(m_settings.scene, ob)) { + createTransformWriter(ob, parent, dupliObParent); + } + + ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); + + if (lb) { + DupliObject *link = static_cast(lb->first); + Object *dupli_ob = NULL; + Object *dupli_parent = NULL; + + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; + } + + if (link->type == OB_DUPLICOLLECTION) { + dupli_ob = link->ob; + dupli_parent = (dupli_ob->parent) ? dupli_ob->parent : ob; + + exploreTransform(base, dupli_ob, dupli_parent, ob); + } + } + + free_object_duplilist(lb); + } +} + +AbcTransformWriter *AbcExporter::createTransformWriter(Object *ob, + Object *parent, + Object *dupliObParent) +{ + /* An object should not be its own parent, or we'll get infinite loops. */ + BLI_assert(ob != parent); + BLI_assert(ob != dupliObParent); + + std::string name; + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + /* check if we have already created a transform writer for this object */ + AbcTransformWriter *my_writer = getXForm(name); + if (my_writer != NULL) { + return my_writer; + } + + AbcTransformWriter *parent_writer = NULL; + Alembic::Abc::OObject alembic_parent; + + if (m_settings.flatten_hierarchy || parent == NULL) { + /* Parentless objects still have the "top object" as parent + * in Alembic. */ + alembic_parent = m_writer->archive().getTop(); + } + else { + /* Since there are so many different ways to find parents (as evident + * in the number of conditions below), we can't really look up the + * parent by name. We'll just call createTransformWriter(), which will + * return the parent's AbcTransformWriter pointer. */ + if (parent->parent) { + if (parent == dupliObParent) { + parent_writer = createTransformWriter(parent, parent->parent, NULL); + } + else { + parent_writer = createTransformWriter(parent, parent->parent, dupliObParent); + } + } + else if (parent == dupliObParent) { + if (dupliObParent->parent == NULL) { + parent_writer = createTransformWriter(parent, NULL, NULL); + } + else { + parent_writer = createTransformWriter( + parent, dupliObParent->parent, dupliObParent->parent); + } + } + else { + parent_writer = createTransformWriter(parent, dupliObParent, dupliObParent); + } + + BLI_assert(parent_writer); + alembic_parent = parent_writer->alembicXform(); + } + + my_writer = new AbcTransformWriter( + ob, alembic_parent, parent_writer, m_trans_sampling_index, m_settings); + + /* When flattening, the matrix of the dupliobject has to be added. */ + if (m_settings.flatten_hierarchy && dupliObParent) { + my_writer->m_proxy_from = dupliObParent; + } + + m_xforms[name] = my_writer; + return my_writer; +} + +void AbcExporter::createShapeWriters() +{ + for (Base *base = static_cast(m_settings.view_layer->object_bases.first); base; + base = base->next) { + exploreObject(base, base->object, NULL); + } +} + +void AbcExporter::exploreObject(Base *base, Object *object, Object *dupliObParent) +{ + /* If an object isn't exported itself, its duplilist shouldn't be + * exported either. */ + if (!export_object(&m_settings, base, dupliObParent != NULL)) { + return; + } + + Object *ob = DEG_get_evaluated_object(m_settings.depsgraph, object); + createShapeWriter(ob, dupliObParent); + + ListBase *lb = object_duplilist(m_settings.depsgraph, m_settings.scene, ob); + + if (lb) { + DupliObject *link = static_cast(lb->first); + + for (; link; link = link->next) { + /* This skips things like custom bone shapes. */ + if (m_settings.renderable_only && link->no_draw) { + continue; + } + if (link->type == OB_DUPLICOLLECTION) { + exploreObject(base, link->ob, ob); + } + } + + free_object_duplilist(lb); + } +} + +void AbcExporter::createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform) +{ + if (!m_settings.export_hair && !m_settings.export_particles) { + return; + } + + ParticleSystem *psys = static_cast(ob->particlesystem.first); + + for (; psys; psys = psys->next) { + if (!psys_check_enabled(ob, psys, G.is_rendering) || !psys->part) { + continue; + } + + if (m_settings.export_hair && psys->part->type == PART_HAIR) { + m_settings.export_child_hairs = true; + m_shapes.push_back(new AbcHairWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); + } + else if (m_settings.export_particles && + (psys->part->type == PART_EMITTER || psys->part->type == PART_FLUID_FLIP || + psys->part->type == PART_FLUID_SPRAY || psys->part->type == PART_FLUID_BUBBLE || + psys->part->type == PART_FLUID_FOAM || psys->part->type == PART_FLUID_TRACER || + psys->part->type == PART_FLUID_SPRAYFOAM || + psys->part->type == PART_FLUID_SPRAYBUBBLE || + psys->part->type == PART_FLUID_FOAMBUBBLE || + psys->part->type == PART_FLUID_SPRAYFOAMBUBBLE)) { + m_shapes.push_back(new AbcPointsWriter(ob, xform, m_shape_sampling_index, m_settings, psys)); + } + } +} + +void AbcExporter::createShapeWriter(Object *ob, Object *dupliObParent) +{ + if (!object_type_is_exportable(m_settings.scene, ob)) { + return; + } + + std::string name; + + if (m_settings.flatten_hierarchy) { + name = get_id_name(ob); + } + else { + name = get_object_dag_path_name(ob, dupliObParent); + } + + AbcTransformWriter *xform = getXForm(name); + + if (!xform) { + ABC_LOG(m_settings.logger) << __func__ << ": xform " << name << " is NULL\n"; + return; + } + + createParticleSystemsWriters(ob, xform); + + switch (ob->type) { + case OB_MESH: { + Mesh *me = static_cast(ob->data); + + if (!me) { + return; + } + + m_shapes.push_back(new AbcMeshWriter(ob, xform, m_shape_sampling_index, m_settings)); + break; + } + case OB_SURF: { + Curve *cu = static_cast(ob->data); + + if (!cu) { + return; + } + + AbcObjectWriter *writer; + if (m_settings.curves_as_mesh) { + writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); + } + else { + writer = new AbcNurbsWriter(ob, xform, m_shape_sampling_index, m_settings); + } + m_shapes.push_back(writer); + break; + } + case OB_CURVE: { + Curve *cu = static_cast(ob->data); + + if (!cu) { + return; + } + + AbcObjectWriter *writer; + if (m_settings.curves_as_mesh) { + writer = new AbcCurveMeshWriter(ob, xform, m_shape_sampling_index, m_settings); + } + else { + writer = new AbcCurveWriter(ob, xform, m_shape_sampling_index, m_settings); + } + m_shapes.push_back(writer); + break; + } + case OB_CAMERA: { + Camera *cam = static_cast(ob->data); + + if (cam->type == CAM_PERSP) { + m_shapes.push_back(new AbcCameraWriter(ob, xform, m_shape_sampling_index, m_settings)); + } + + break; + } + case OB_MBALL: { + MetaBall *mball = static_cast(ob->data); + if (!mball) { + return; + } + + m_shapes.push_back( + new AbcMBallWriter(m_bmain, ob, xform, m_shape_sampling_index, m_settings)); + break; + } + } +} + +AbcTransformWriter *AbcExporter::getXForm(const std::string &name) +{ + std::map::iterator it = m_xforms.find(name); + + if (it == m_xforms.end()) { + return NULL; + } + + return it->second; +} + +void AbcExporter::setCurrentFrame(Main *bmain, double t) +{ + m_settings.scene->r.cfra = static_cast(t); + m_settings.scene->r.subframe = static_cast(t) - m_settings.scene->r.cfra; + BKE_scene_graph_update_for_newframe(m_settings.depsgraph, bmain); +} diff --git a/source/blender/io/alembic/intern/abc_exporter.h b/source/blender/io/alembic/intern/abc_exporter.h new file mode 100644 index 00000000000..398004d2ec5 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_exporter.h @@ -0,0 +1,128 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_EXPORTER_H__ +#define __ABC_EXPORTER_H__ + +#include +#include +#include +#include + +#include "abc_util.h" + +class AbcObjectWriter; +class AbcTransformWriter; +class ArchiveWriter; + +struct Base; +struct Depsgraph; +struct Main; +struct Object; +struct Scene; +struct ViewLayer; + +struct ExportSettings { + ExportSettings(); + + Scene *scene; + /** Scene layer to export; all its objects will be exported, unless selected_only=true. */ + ViewLayer *view_layer; + Depsgraph *depsgraph; + SimpleLogger logger; + + bool selected_only; + bool visible_objects_only; + bool renderable_only; + + double frame_start, frame_end; + double frame_samples_xform; + double frame_samples_shape; + double shutter_open; + double shutter_close; + float global_scale; + + bool flatten_hierarchy; + + bool export_normals; + bool export_uvs; + bool export_vcols; + bool export_face_sets; + bool export_vweigths; + bool export_hair; + bool export_particles; + + bool apply_subdiv; + bool curves_as_mesh; + bool use_subdiv_schema; + bool export_child_hairs; + bool export_ogawa; + bool pack_uv; + bool triangulate; + + int quad_method; + int ngon_method; +}; + +class AbcExporter { + Main *m_bmain; + ExportSettings &m_settings; + + const char *m_filename; + + unsigned int m_trans_sampling_index, m_shape_sampling_index; + + ArchiveWriter *m_writer; + + /* mapping from name to transform writer */ + typedef std::map m_xforms_type; + m_xforms_type m_xforms; + + std::vector m_shapes; + + public: + AbcExporter(Main *bmain, const char *filename, ExportSettings &settings); + ~AbcExporter(); + + void operator()(short *do_update, float *progress, bool *was_canceled); + + protected: + void getShutterSamples(unsigned int nr_of_samples, + bool time_relative, + std::vector &samples); + void getFrameSet(unsigned int nr_of_samples, std::set &frames); + + private: + Alembic::Abc::TimeSamplingPtr createTimeSampling(double step); + + void createTransformWritersHierarchy(); + AbcTransformWriter *createTransformWriter(Object *ob, Object *parent, Object *dupliObParent); + void exploreTransform(Base *base, Object *object, Object *parent, Object *dupliObParent); + void exploreObject(Base *base, Object *object, Object *dupliObParent); + void createShapeWriters(); + void createShapeWriter(Object *ob, Object *dupliObParent); + void createParticleSystemsWriters(Object *ob, AbcTransformWriter *xform); + + AbcTransformWriter *getXForm(const std::string &name); + + void setCurrentFrame(Main *bmain, double t); +}; + +#endif /* __ABC_EXPORTER_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_archive.cc b/source/blender/io/alembic/intern/abc_reader_archive.cc new file mode 100644 index 00000000000..6ad44553701 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_archive.cc @@ -0,0 +1,140 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_archive.h" + +extern "C" { +#include "BKE_main.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" +} + +#ifdef WIN32 +# include "utfconv.h" +#endif + +#include + +using Alembic::Abc::ErrorHandler; +using Alembic::Abc::Exception; +using Alembic::Abc::IArchive; +using Alembic::Abc::kWrapExisting; + +static IArchive open_archive(const std::string &filename, + const std::vector &input_streams, + bool &is_hdf5) +{ + is_hdf5 = false; + + try { + Alembic::AbcCoreOgawa::ReadArchive archive_reader(input_streams); + + return IArchive(archive_reader(filename), kWrapExisting, ErrorHandler::kThrowPolicy); + } + catch (const Exception &e) { + std::cerr << e.what() << '\n'; + +#ifdef WITH_ALEMBIC_HDF5 + try { + is_hdf5 = true; + Alembic::AbcCoreAbstract::ReadArraySampleCachePtr cache_ptr; + + return IArchive(Alembic::AbcCoreHDF5::ReadArchive(), + filename.c_str(), + ErrorHandler::kThrowPolicy, + cache_ptr); + } + catch (const Exception &) { + std::cerr << e.what() << '\n'; + return IArchive(); + } +#else + /* Inspect the file to see whether it's really a HDF5 file. */ + char header[4]; /* char(0x89) + "HDF" */ + std::ifstream the_file(filename.c_str(), std::ios::in | std::ios::binary); + if (!the_file) { + std::cerr << "Unable to open " << filename << std::endl; + } + else if (!the_file.read(header, sizeof(header))) { + std::cerr << "Unable to read from " << filename << std::endl; + } + else if (strncmp(header + 1, "HDF", 3)) { + std::cerr << filename << " has an unknown file format, unable to read." << std::endl; + } + else { + is_hdf5 = true; + std::cerr << filename << " is in the obsolete HDF5 format, unable to read." << std::endl; + } + + if (the_file.is_open()) { + the_file.close(); + } + + return IArchive(); +#endif + } + + return IArchive(); +} + +ArchiveReader::ArchiveReader(struct Main *bmain, const char *filename) +{ + char abs_filename[FILE_MAX]; + BLI_strncpy(abs_filename, filename, FILE_MAX); + BLI_path_abs(abs_filename, BKE_main_blendfile_path(bmain)); + +#ifdef WIN32 + UTF16_ENCODE(abs_filename); + std::wstring wstr(abs_filename_16); + m_infile.open(wstr.c_str(), std::ios::in | std::ios::binary); + UTF16_UN_ENCODE(abs_filename); +#else + m_infile.open(abs_filename, std::ios::in | std::ios::binary); +#endif + + m_streams.push_back(&m_infile); + + m_archive = open_archive(abs_filename, m_streams, m_is_hdf5); + + /* We can't open an HDF5 file from a stream, so close it. */ + if (m_is_hdf5) { + m_infile.close(); + m_streams.clear(); + } +} + +bool ArchiveReader::is_hdf5() const +{ + return m_is_hdf5; +} + +bool ArchiveReader::valid() const +{ + return m_archive.valid(); +} + +Alembic::Abc::IObject ArchiveReader::getTop() +{ + return m_archive.getTop(); +} diff --git a/source/blender/io/alembic/intern/abc_reader_archive.h b/source/blender/io/alembic/intern/abc_reader_archive.h new file mode 100644 index 00000000000..bdb53bd0b8c --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_archive.h @@ -0,0 +1,67 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_ARCHIVE_H__ +#define __ABC_READER_ARCHIVE_H__ + +#include + +#ifdef WITH_ALEMBIC_HDF5 +# include +#endif + +#include + +#include + +struct Main; +struct Scene; + +/* Wrappers around input and output archives. The goal is to be able to use + * streams so that unicode paths work on Windows (T49112), and to make sure that + * the stream objects remain valid as long as the archives are open. + */ + +class ArchiveReader { + Alembic::Abc::IArchive m_archive; + std::ifstream m_infile; + std::vector m_streams; + bool m_is_hdf5; + + public: + ArchiveReader(struct Main *bmain, const char *filename); + + bool valid() const; + + /** + * Returns true when either Blender is compiled with HDF5 support and + * the archive was successfully opened (valid() will also return true), + * or when Blender was built without HDF5 support but a HDF5 file was + * detected (valid() will return false). + */ + bool is_hdf5() const; + + Alembic::Abc::IObject getTop(); +}; + +#endif /* __ABC_READER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_camera.cc b/source/blender/io/alembic/intern/abc_reader_camera.cc new file mode 100644 index 00000000000..ab506f32cbe --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_camera.cc @@ -0,0 +1,113 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_camera.h" +#include "abc_reader_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_object_types.h" + +#include "BKE_camera.h" +#include "BKE_object.h" + +#include "BLI_math.h" +} + +using Alembic::AbcGeom::CameraSample; +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IFloatProperty; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::kWrapExisting; + +AbcCameraReader::AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + ICamera abc_cam(m_iobject, kWrapExisting); + m_schema = abc_cam.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcCameraReader::valid() const +{ + return m_schema.valid(); +} + +bool AbcCameraReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::ICamera::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to Camera when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_CAMERA) { + *err_str = "Object type mismatch, Alembic object path points to Camera."; + return false; + } + + return true; +} + +void AbcCameraReader::readObjectData(Main *bmain, const ISampleSelector &sample_sel) +{ + Camera *bcam = static_cast(BKE_camera_add(bmain, m_data_name.c_str())); + + CameraSample cam_sample; + m_schema.get(cam_sample, sample_sel); + + ICompoundProperty customDataContainer = m_schema.getUserProperties(); + + if (customDataContainer.valid() && customDataContainer.getPropertyHeader("stereoDistance") && + customDataContainer.getPropertyHeader("eyeSeparation")) { + IFloatProperty convergence_plane(customDataContainer, "stereoDistance"); + IFloatProperty eye_separation(customDataContainer, "eyeSeparation"); + + bcam->stereo.interocular_distance = eye_separation.getValue(sample_sel); + bcam->stereo.convergence_distance = convergence_plane.getValue(sample_sel); + } + + const float lens = static_cast(cam_sample.getFocalLength()); + const float apperture_x = static_cast(cam_sample.getHorizontalAperture()); + const float apperture_y = static_cast(cam_sample.getVerticalAperture()); + const float h_film_offset = static_cast(cam_sample.getHorizontalFilmOffset()); + const float v_film_offset = static_cast(cam_sample.getVerticalFilmOffset()); + const float film_aspect = apperture_x / apperture_y; + + bcam->lens = lens; + bcam->sensor_x = apperture_x * 10; + bcam->sensor_y = apperture_y * 10; + bcam->shiftx = h_film_offset / apperture_x; + bcam->shifty = v_film_offset / apperture_y / film_aspect; + bcam->clip_start = max_ff(0.1f, static_cast(cam_sample.getNearClippingPlane())); + bcam->clip_end = static_cast(cam_sample.getFarClippingPlane()); + bcam->dof.focus_distance = static_cast(cam_sample.getFocusDistance()); + bcam->dof.aperture_fstop = static_cast(cam_sample.getFStop()); + + m_object = BKE_object_add_only_object(bmain, OB_CAMERA, m_object_name.c_str()); + m_object->data = bcam; +} diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h new file mode 100644 index 00000000000..1d9763b0454 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_camera.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_CAMERA_H__ +#define __ABC_READER_CAMERA_H__ + +#include "abc_reader_object.h" + +class AbcCameraReader : public AbcObjectReader { + Alembic::AbcGeom::ICameraSchema m_schema; + + public: + AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); +}; + +#endif /* __ABC_READER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_curves.cc b/source/blender/io/alembic/intern/abc_reader_curves.cc new file mode 100644 index 00000000000..1be164c7c94 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_curves.cc @@ -0,0 +1,354 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_curves.h" +#include "abc_reader_transform.h" +#include "abc_util.h" + +#include + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +} + +using Alembic::Abc::FloatArraySamplePtr; +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; +using Alembic::Abc::PropertyHeader; +using Alembic::Abc::UcharArraySamplePtr; + +using Alembic::AbcGeom::CurvePeriodicity; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFloatGeomParam; +using Alembic::AbcGeom::IInt16Property; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::kWrapExisting; + +AbcCurveReader::AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + ICurves abc_curves(object, kWrapExisting); + m_curves_schema = abc_curves.getSchema(); + + get_min_max_time(m_iobject, m_curves_schema, m_min_time, m_max_time); +} + +bool AbcCurveReader::valid() const +{ + return m_curves_schema.valid(); +} + +bool AbcCurveReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::ICurves::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to Curves when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_CURVE) { + *err_str = "Object type mismatch, Alembic object path points to Curves."; + return false; + } + + return true; +} + +void AbcCurveReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) +{ + Curve *cu = BKE_curve_add(bmain, m_data_name.c_str(), OB_CURVE); + + cu->flag |= CU_DEFORM_FILL | CU_3D; + cu->actvert = CU_ACT_NONE; + cu->resolu = 1; + + ICompoundProperty user_props = m_curves_schema.getUserProperties(); + if (user_props) { + const PropertyHeader *header = user_props.getPropertyHeader(ABC_CURVE_RESOLUTION_U_PROPNAME); + if (header != NULL && header->isScalar() && IInt16Property::matches(*header)) { + IInt16Property resolu(user_props, header->getName()); + cu->resolu = resolu.getValue(sample_sel); + } + } + + m_object = BKE_object_add_only_object(bmain, OB_CURVE, m_object_name.c_str()); + m_object->data = cu; + + read_curve_sample(cu, m_curves_schema, sample_sel); + + if (has_animations(m_curves_schema, m_settings)) { + addCacheModifier(); + } +} + +void AbcCurveReader::read_curve_sample(Curve *cu, + const ICurvesSchema &schema, + const ISampleSelector &sample_sel) +{ + ICurvesSchema::Sample smp; + try { + smp = schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return; + } + + const Int32ArraySamplePtr num_vertices = smp.getCurvesNumVertices(); + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + const FloatArraySamplePtr knots = smp.getKnots(); + const CurvePeriodicity periodicity = smp.getWrap(); + const UcharArraySamplePtr orders = smp.getOrders(); + + const IFloatGeomParam widths_param = schema.getWidthsParam(); + FloatArraySamplePtr radiuses; + + if (widths_param.valid()) { + IFloatGeomParam::Sample wsample = widths_param.getExpandedValue(sample_sel); + radiuses = wsample.getVals(); + } + + int knot_offset = 0; + + size_t idx = 0; + for (size_t i = 0; i < num_vertices->size(); i++) { + const int num_verts = (*num_vertices)[i]; + + Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), "abc_getnurb")); + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + nu->pntsu = num_verts; + nu->pntsv = 1; + nu->flag |= CU_SMOOTH; + + switch (smp.getType()) { + case Alembic::AbcGeom::kCubic: + nu->orderu = 4; + break; + case Alembic::AbcGeom::kVariableOrder: + if (orders && orders->size() > i) { + nu->orderu = static_cast((*orders)[i]); + break; + } + ATTR_FALLTHROUGH; + case Alembic::AbcGeom::kLinear: + default: + nu->orderu = 2; + } + + if (periodicity == Alembic::AbcGeom::kNonPeriodic) { + nu->flagu |= CU_NURB_ENDPOINT; + } + else if (periodicity == Alembic::AbcGeom::kPeriodic) { + nu->flagu |= CU_NURB_CYCLIC; + + /* Check the number of points which overlap, we don't have + * overlapping points in Blender, but other software do use them to + * indicate that a curve is actually cyclic. Usually the number of + * overlapping points is equal to the order/degree of the curve. + */ + + const int start = idx; + const int end = idx + num_verts; + int overlap = 0; + + for (int j = start, k = end - nu->orderu; j < nu->orderu; j++, k++) { + const Imath::V3f &p1 = (*positions)[j]; + const Imath::V3f &p2 = (*positions)[k]; + + if (p1 != p2) { + break; + } + + overlap++; + } + + /* TODO: Special case, need to figure out how it coincides with knots. */ + if (overlap == 0 && num_verts > 2 && (*positions)[start] == (*positions)[end - 1]) { + overlap = 1; + } + + /* There is no real cycles. */ + if (overlap == 0) { + nu->flagu &= ~CU_NURB_CYCLIC; + nu->flagu |= CU_NURB_ENDPOINT; + } + + nu->pntsu -= overlap; + } + + const bool do_weights = (weights != NULL) && (weights->size() > 1); + float weight = 1.0f; + + const bool do_radius = (radiuses != NULL) && (radiuses->size() > 1); + float radius = (radiuses && radiuses->size() == 1) ? (*radiuses)[0] : 1.0f; + + nu->type = CU_NURBS; + + nu->bp = static_cast(MEM_callocN(sizeof(BPoint) * nu->pntsu, "abc_getnurb")); + BPoint *bp = nu->bp; + + for (int j = 0; j < nu->pntsu; j++, bp++, idx++) { + const Imath::V3f &pos = (*positions)[idx]; + + if (do_radius) { + radius = (*radiuses)[idx]; + } + + if (do_weights) { + weight = (*weights)[idx]; + } + + copy_zup_from_yup(bp->vec, pos.getValue()); + bp->vec[3] = weight; + bp->f1 = SELECT; + bp->radius = radius; + bp->weight = 1.0f; + } + + if (knots && knots->size() != 0) { + nu->knotsu = static_cast( + MEM_callocN(KNOTSU(nu) * sizeof(float), "abc_setsplineknotsu")); + + /* TODO: second check is temporary, for until the check for cycles is rock solid. */ + if (periodicity == Alembic::AbcGeom::kPeriodic && (KNOTSU(nu) == knots->size() - 2)) { + /* Skip first and last knots. */ + for (size_t i = 1; i < knots->size() - 1; i++) { + nu->knotsu[i - 1] = (*knots)[knot_offset + i]; + } + } + else { + /* TODO: figure out how to use the knots array from other + * software in this case. */ + BKE_nurb_knot_calc_u(nu); + } + + knot_offset += knots->size(); + } + else { + BKE_nurb_knot_calc_u(nu); + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } +} + +/* NOTE: Alembic only stores data about control points, but the Mesh + * passed from the cache modifier contains the displist, which has more data + * than the control points, so to avoid corrupting the displist we modify the + * object directly and create a new Mesh from that. Also we might need to + * create new or delete existing NURBS in the curve. + */ +Mesh *AbcCurveReader::read_mesh(Mesh *existing_mesh, + const ISampleSelector &sample_sel, + int /*read_flag*/, + const char **err_str) +{ + ICurvesSchema::Sample sample; + + try { + sample = m_curves_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + *err_str = "Error reading curve sample; more detail on the console"; + printf("Alembic: error reading curve sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_curves_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return existing_mesh; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Int32ArraySamplePtr num_vertices = sample.getCurvesNumVertices(); + + int vertex_idx = 0; + int curve_idx; + Curve *curve = static_cast(m_object->data); + + const int curve_count = BLI_listbase_count(&curve->nurb); + bool same_topology = curve_count == num_vertices->size(); + + if (same_topology) { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int num_in_alembic = (*num_vertices)[curve_idx]; + const int num_in_blender = nurbs->pntsu; + + if (num_in_alembic != num_in_blender) { + same_topology = false; + break; + } + } + } + + if (!same_topology) { + BKE_nurbList_free(&curve->nurb); + read_curve_sample(curve, m_curves_schema, sample_sel); + } + else { + Nurb *nurbs = static_cast(curve->nurb.first); + for (curve_idx = 0; nurbs; nurbs = nurbs->next, curve_idx++) { + const int totpoint = (*num_vertices)[curve_idx]; + + if (nurbs->bp) { + BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++, vertex_idx++) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_zup_from_yup(point->vec, pos.getValue()); + } + } + else if (nurbs->bezt) { + BezTriple *bezier = nurbs->bezt; + + for (int i = 0; i < totpoint; i++, bezier++, vertex_idx++) { + const Imath::V3f &pos = (*positions)[vertex_idx]; + copy_zup_from_yup(bezier->vec[1], pos.getValue()); + } + } + } + } + + return BKE_mesh_new_nomain_from_curve(m_object); +} diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h new file mode 100644 index 00000000000..1e4f28edc51 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_CURVES_H__ +#define __ABC_READER_CURVES_H__ + +#include "abc_reader_object.h" +#include "abc_reader_mesh.h" + +struct Curve; + +#define ABC_CURVE_RESOLUTION_U_PROPNAME "blender:resolution" + +class AbcCurveReader : public AbcObjectReader { + Alembic::AbcGeom::ICurvesSchema m_curves_schema; + + public: + AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + struct Mesh *read_mesh(struct Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char **err_str); + + void read_curve_sample(Curve *cu, + const Alembic::AbcGeom::ICurvesSchema &schema, + const Alembic::Abc::ISampleSelector &sample_selector); +}; + +#endif /* __ABC_READER_CURVES_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc new file mode 100644 index 00000000000..a4e412695c3 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -0,0 +1,889 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_mesh.h" +#include "abc_reader_transform.h" +#include "abc_util.h" + +#include + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" + +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +} + +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::P3fArraySamplePtr; + +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::IFaceSetSchema; +using Alembic::AbcGeom::IN3fGeomParam; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::ISubDSchema; +using Alembic::AbcGeom::IV2fGeomParam; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::N3fArraySample; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::UInt32ArraySamplePtr; +using Alembic::AbcGeom::V2fArraySamplePtr; + +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +/* Some helpers for mesh generation */ +namespace utils { + +static void build_mat_map(const Main *bmain, std::map &mat_map) +{ + Material *material = static_cast(bmain->materials.first); + + for (; material; material = static_cast(material->id.next)) { + mat_map[material->id.name + 2] = material; + } +} + +static void assign_materials(Main *bmain, + Object *ob, + const std::map &mat_index_map) +{ + bool can_assign = true; + std::map::const_iterator it = mat_index_map.begin(); + + int matcount = 0; + for (; it != mat_index_map.end(); ++it, matcount++) { + if (!BKE_object_material_slot_add(bmain, ob)) { + can_assign = false; + break; + } + } + + /* TODO(kevin): use global map? */ + std::map mat_map; + build_mat_map(bmain, mat_map); + + std::map::iterator mat_iter; + + if (can_assign) { + it = mat_index_map.begin(); + + for (; it != mat_index_map.end(); ++it) { + std::string mat_name = it->first; + mat_iter = mat_map.find(mat_name.c_str()); + + Material *assigned_mat; + + if (mat_iter == mat_map.end()) { + assigned_mat = BKE_material_add(bmain, mat_name.c_str()); + mat_map[mat_name] = assigned_mat; + } + else { + assigned_mat = mat_iter->second; + } + + BKE_object_material_assign(bmain, ob, assigned_mat, it->second, BKE_MAT_ASSIGN_OBDATA); + } + } +} + +} /* namespace utils */ + +struct AbcMeshData { + Int32ArraySamplePtr face_indices; + Int32ArraySamplePtr face_counts; + + P3fArraySamplePtr positions; + P3fArraySamplePtr ceil_positions; + + V2fArraySamplePtr uvs; + UInt32ArraySamplePtr uvs_indices; +}; + +static void read_mverts_interp(MVert *mverts, + const P3fArraySamplePtr &positions, + const P3fArraySamplePtr &ceil_positions, + const float weight) +{ + float tmp[3]; + for (int i = 0; i < positions->size(); i++) { + MVert &mvert = mverts[i]; + const Imath::V3f &floor_pos = (*positions)[i]; + const Imath::V3f &ceil_pos = (*ceil_positions)[i]; + + interp_v3_v3v3(tmp, floor_pos.getValue(), ceil_pos.getValue(), weight); + copy_zup_from_yup(mvert.co, tmp); + + mvert.bweight = 0; + } +} + +static void read_mverts(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MVert *mverts = config.mvert; + const P3fArraySamplePtr &positions = mesh_data.positions; + + if (config.weight != 0.0f && mesh_data.ceil_positions != NULL && + mesh_data.ceil_positions->size() == positions->size()) { + read_mverts_interp(mverts, positions, mesh_data.ceil_positions, config.weight); + return; + } + + read_mverts(mverts, positions, nullptr); +} + +void read_mverts(MVert *mverts, const P3fArraySamplePtr positions, const N3fArraySamplePtr normals) +{ + for (int i = 0; i < positions->size(); i++) { + MVert &mvert = mverts[i]; + Imath::V3f pos_in = (*positions)[i]; + + copy_zup_from_yup(mvert.co, pos_in.getValue()); + + mvert.bweight = 0; + + if (normals) { + Imath::V3f nor_in = (*normals)[i]; + + short no[3]; + normal_float_to_short_v3(no, nor_in.getValue()); + + copy_zup_from_yup(mvert.no, no); + } + } +} + +static void read_mpolys(CDStreamConfig &config, const AbcMeshData &mesh_data) +{ + MPoly *mpolys = config.mpoly; + MLoop *mloops = config.mloop; + MLoopUV *mloopuvs = config.mloopuv; + + const Int32ArraySamplePtr &face_indices = mesh_data.face_indices; + const Int32ArraySamplePtr &face_counts = mesh_data.face_counts; + const V2fArraySamplePtr &uvs = mesh_data.uvs; + const size_t uvs_size = uvs == nullptr ? 0 : uvs->size(); + + const UInt32ArraySamplePtr &uvs_indices = mesh_data.uvs_indices; + + const bool do_uvs = (mloopuvs && uvs && uvs_indices) && + (uvs_indices->size() == face_indices->size()); + unsigned int loop_index = 0; + unsigned int rev_loop_index = 0; + unsigned int uv_index = 0; + + for (int i = 0; i < face_counts->size(); i++) { + const int face_size = (*face_counts)[i]; + + MPoly &poly = mpolys[i]; + poly.loopstart = loop_index; + poly.totloop = face_size; + + /* Polygons are always assumed to be smooth-shaded. If the Alembic mesh should be flat-shaded, + * this is encoded in custom loop normals. See T71246. */ + poly.flag |= ME_SMOOTH; + + /* NOTE: Alembic data is stored in the reverse order. */ + rev_loop_index = loop_index + (face_size - 1); + + for (int f = 0; f < face_size; f++, loop_index++, rev_loop_index--) { + MLoop &loop = mloops[rev_loop_index]; + loop.v = (*face_indices)[loop_index]; + + if (do_uvs) { + MLoopUV &loopuv = mloopuvs[rev_loop_index]; + + uv_index = (*uvs_indices)[loop_index]; + + /* Some Alembic files are broken (or at least export UVs in a way we don't expect). */ + if (uv_index >= uvs_size) { + continue; + } + + loopuv.uv[0] = (*uvs)[uv_index][0]; + loopuv.uv[1] = (*uvs)[uv_index][1]; + } + } + } + + BKE_mesh_calc_edges(config.mesh, false, false); +} + +static void process_no_normals(CDStreamConfig &config) +{ + /* Absense of normals in the Alembic mesh is interpreted as 'smooth'. */ + BKE_mesh_calc_normals(config.mesh); +} + +static void process_loop_normals(CDStreamConfig &config, const N3fArraySamplePtr loop_normals_ptr) +{ + size_t loop_count = loop_normals_ptr->size(); + + if (loop_count == 0) { + process_no_normals(config); + return; + } + + float(*lnors)[3] = static_cast( + MEM_malloc_arrayN(loop_count, sizeof(float[3]), "ABC::FaceNormals")); + + Mesh *mesh = config.mesh; + MPoly *mpoly = mesh->mpoly; + const N3fArraySample &loop_normals = *loop_normals_ptr; + int abc_index = 0; + for (int i = 0, e = mesh->totpoly; i < e; i++, mpoly++) { + /* As usual, ABC orders the loops in reverse. */ + for (int j = mpoly->totloop - 1; j >= 0; j--, abc_index++) { + int blender_index = mpoly->loopstart + j; + copy_zup_from_yup(lnors[blender_index], loop_normals[abc_index].getValue()); + } + } + + mesh->flag |= ME_AUTOSMOOTH; + BKE_mesh_set_custom_normals(mesh, lnors); + + MEM_freeN(lnors); +} + +static void process_vertex_normals(CDStreamConfig &config, + const N3fArraySamplePtr vertex_normals_ptr) +{ + size_t normals_count = vertex_normals_ptr->size(); + if (normals_count == 0) { + process_no_normals(config); + return; + } + + float(*vnors)[3] = static_cast( + MEM_malloc_arrayN(normals_count, sizeof(float[3]), "ABC::VertexNormals")); + + const N3fArraySample &vertex_normals = *vertex_normals_ptr; + for (int index = 0; index < normals_count; index++) { + copy_zup_from_yup(vnors[index], vertex_normals[index].getValue()); + } + + config.mesh->flag |= ME_AUTOSMOOTH; + BKE_mesh_set_custom_normals_from_vertices(config.mesh, vnors); + MEM_freeN(vnors); +} + +static void process_normals(CDStreamConfig &config, + const IN3fGeomParam &normals, + const ISampleSelector &selector) +{ + if (!normals.valid()) { + process_no_normals(config); + return; + } + + IN3fGeomParam::Sample normsamp = normals.getExpandedValue(selector); + Alembic::AbcGeom::GeometryScope scope = normals.getScope(); + + switch (scope) { + case Alembic::AbcGeom::kFacevaryingScope: // 'Vertex Normals' in Houdini. + process_loop_normals(config, normsamp.getVals()); + break; + case Alembic::AbcGeom::kVertexScope: + case Alembic::AbcGeom::kVaryingScope: // 'Point Normals' in Houdini. + process_vertex_normals(config, normsamp.getVals()); + break; + case Alembic::AbcGeom::kConstantScope: + case Alembic::AbcGeom::kUniformScope: + case Alembic::AbcGeom::kUnknownScope: + process_no_normals(config); + break; + } +} + +ABC_INLINE void read_uvs_params(CDStreamConfig &config, + AbcMeshData &abc_data, + const IV2fGeomParam &uv, + const ISampleSelector &selector) +{ + if (!uv.valid()) { + return; + } + + IV2fGeomParam::Sample uvsamp; + uv.getIndexed(uvsamp, selector); + + abc_data.uvs = uvsamp.getVals(); + abc_data.uvs_indices = uvsamp.getIndices(); + + if (abc_data.uvs_indices->size() == config.totloop) { + std::string name = Alembic::Abc::GetSourceName(uv.getMetaData()); + + /* According to the convention, primary UVs should have had their name + * set using Alembic::Abc::SetSourceName, but you can't expect everyone + * to follow it! :) */ + if (name.empty()) { + name = uv.getName(); + } + + void *cd_ptr = config.add_customdata_cb(config.mesh, name.c_str(), CD_MLOOPUV); + config.mloopuv = static_cast(cd_ptr); + } +} + +static void *add_customdata_cb(Mesh *mesh, const char *name, int data_type) +{ + CustomDataType cd_data_type = static_cast(data_type); + void *cd_ptr; + CustomData *loopdata; + int numloops; + + /* unsupported custom data type -- don't do anything. */ + if (!ELEM(cd_data_type, CD_MLOOPUV, CD_MLOOPCOL)) { + return NULL; + } + + loopdata = &mesh->ldata; + cd_ptr = CustomData_get_layer_named(loopdata, cd_data_type, name); + if (cd_ptr != NULL) { + /* layer already exists, so just return it. */ + return cd_ptr; + } + + /* Create a new layer. */ + numloops = mesh->totloop; + cd_ptr = CustomData_add_layer_named(loopdata, cd_data_type, CD_DEFAULT, NULL, numloops, name); + return cd_ptr; +} + +static void get_weight_and_index(CDStreamConfig &config, + Alembic::AbcCoreAbstract::TimeSamplingPtr time_sampling, + size_t samples_number) +{ + Alembic::AbcGeom::index_t i0, i1; + + config.weight = get_weight_and_index(config.time, time_sampling, samples_number, i0, i1); + + config.index = i0; + config.ceil_index = i1; +} + +static void read_mesh_sample(const std::string &iobject_full_name, + ImportSettings *settings, + const IPolyMeshSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config) +{ + const IPolyMeshSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.positions = sample.getPositions(); + + get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples()); + + if (config.weight != 0.0f) { + Alembic::AbcGeom::IPolyMeshSchema::Sample ceil_sample; + schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index)); + abc_mesh_data.ceil_positions = ceil_sample.getPositions(); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + read_mpolys(config, abc_mesh_data); + process_normals(config, schema.getNormalsParam(), selector); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); + } +} + +CDStreamConfig get_config(Mesh *mesh) +{ + CDStreamConfig config; + + BLI_assert(mesh->mvert || mesh->totvert == 0); + + config.mesh = mesh; + config.mvert = mesh->mvert; + config.mloop = mesh->mloop; + config.mpoly = mesh->mpoly; + config.totloop = mesh->totloop; + config.totpoly = mesh->totpoly; + config.loopdata = &mesh->ldata; + config.add_customdata_cb = add_customdata_cb; + + return config; +} + +/* ************************************************************************** */ + +AbcMeshReader::AbcMeshReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + IPolyMesh ipoly_mesh(m_iobject, kWrapExisting); + m_schema = ipoly_mesh.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcMeshReader::valid() const +{ + return m_schema.valid(); +} + +void AbcMeshReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL); + if (read_mesh != mesh) { + /* XXX fixme after 2.80; mesh->flag isn't copied by BKE_mesh_nomain_to_mesh() */ + /* read_mesh can be freed by BKE_mesh_nomain_to_mesh(), so get the flag before that happens. */ + short autosmooth = (read_mesh->flag & ME_AUTOSMOOTH); + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + mesh->flag |= autosmooth; + } + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + readFaceSetsSample(bmain, mesh, sample_sel); + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +bool AbcMeshReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::IPolyMesh::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to PolyMesh when importing, but not " + "any more."; + return false; + } + + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch, Alembic object path points to PolyMesh."; + return false; + } + + return true; +} + +bool AbcMeshReader::topology_changed(Mesh *existing_mesh, const ISampleSelector &sample_sel) +{ + IPolyMeshSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + // A similar error in read_mesh() would just return existing_mesh. + return false; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + return positions->size() != existing_mesh->totvert || + face_counts->size() != existing_mesh->totpoly || + face_indices->size() != existing_mesh->totloop; +} + +Mesh *AbcMeshReader::read_mesh(Mesh *existing_mesh, + const ISampleSelector &sample_sel, + int read_flag, + const char **err_str) +{ + IPolyMeshSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + if (err_str != nullptr) { + *err_str = "Error reading mesh sample; more detail on the console"; + } + printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return existing_mesh; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + Mesh *new_mesh = NULL; + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + ImportSettings settings; + settings.read_flag |= read_flag; + + if (topology_changed(existing_mesh, sample_sel)) { + new_mesh = BKE_mesh_new_nomain_from_template( + existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + else { + /* If the face count changed (e.g. by triangulation), only read points. + * This prevents crash from T49813. + * TODO(kevin): perhaps find a better way to do this? */ + if (face_counts->size() != existing_mesh->totpoly || + face_indices->size() != existing_mesh->totloop) { + settings.read_flag = MOD_MESHSEQ_READ_VERT; + + if (err_str) { + *err_str = + "Topology has changed, perhaps by triangulating the" + " mesh. Only vertices will be read!"; + } + } + } + + CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); + config.time = sample_sel.getRequestedTime(); + + read_mesh_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config); + + if (new_mesh) { + /* Here we assume that the number of materials doesn't change, i.e. that + * the material slots that were created when the object was loaded from + * Alembic are still valid now. */ + size_t num_polys = new_mesh->totpoly; + if (num_polys > 0) { + std::map mat_map; + assign_facesets_to_mpoly(sample_sel, new_mesh->mpoly, num_polys, mat_map); + } + + return new_mesh; + } + + return existing_mesh; +} + +void AbcMeshReader::assign_facesets_to_mpoly(const ISampleSelector &sample_sel, + MPoly *mpoly, + int totpoly, + std::map &r_mat_map) +{ + std::vector face_sets; + m_schema.getFaceSetNames(face_sets); + + if (face_sets.empty()) { + return; + } + + int current_mat = 0; + + for (int i = 0; i < face_sets.size(); i++) { + const std::string &grp_name = face_sets[i]; + + if (r_mat_map.find(grp_name) == r_mat_map.end()) { + r_mat_map[grp_name] = 1 + current_mat++; + } + + const int assigned_mat = r_mat_map[grp_name]; + + const IFaceSet faceset = m_schema.getFaceSet(grp_name); + + if (!faceset.valid()) { + std::cerr << " Face set " << grp_name << " invalid for " << m_object_name << "\n"; + continue; + } + + const IFaceSetSchema face_schem = faceset.getSchema(); + const IFaceSetSchema::Sample face_sample = face_schem.getValue(sample_sel); + const Int32ArraySamplePtr group_faces = face_sample.getFaces(); + const size_t num_group_faces = group_faces->size(); + + for (size_t l = 0; l < num_group_faces; l++) { + size_t pos = (*group_faces)[l]; + + if (pos >= totpoly) { + std::cerr << "Faceset overflow on " << faceset.getName() << '\n'; + break; + } + + MPoly &poly = mpoly[pos]; + poly.mat_nr = assigned_mat - 1; + } + } +} + +void AbcMeshReader::readFaceSetsSample(Main *bmain, Mesh *mesh, const ISampleSelector &sample_sel) +{ + std::map mat_map; + assign_facesets_to_mpoly(sample_sel, mesh->mpoly, mesh->totpoly, mat_map); + utils::assign_materials(bmain, m_object, mat_map); +} + +/* ************************************************************************** */ + +ABC_INLINE MEdge *find_edge(MEdge *edges, int totedge, int v1, int v2) +{ + for (int i = 0, e = totedge; i < e; i++) { + MEdge &edge = edges[i]; + + if (edge.v1 == v1 && edge.v2 == v2) { + return &edge; + } + } + + return NULL; +} + +static void read_subd_sample(const std::string &iobject_full_name, + ImportSettings *settings, + const ISubDSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config) +{ + const ISubDSchema::Sample sample = schema.getValue(selector); + + AbcMeshData abc_mesh_data; + abc_mesh_data.face_counts = sample.getFaceCounts(); + abc_mesh_data.face_indices = sample.getFaceIndices(); + abc_mesh_data.positions = sample.getPositions(); + + get_weight_and_index(config, schema.getTimeSampling(), schema.getNumSamples()); + + if (config.weight != 0.0f) { + Alembic::AbcGeom::ISubDSchema::Sample ceil_sample; + schema.get(ceil_sample, Alembic::Abc::ISampleSelector(config.ceil_index)); + abc_mesh_data.ceil_positions = ceil_sample.getPositions(); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_UV) != 0) { + read_uvs_params(config, abc_mesh_data, schema.getUVsParam(), selector); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_VERT) != 0) { + read_mverts(config, abc_mesh_data); + } + + if ((settings->read_flag & MOD_MESHSEQ_READ_POLY) != 0) { + /* Alembic's 'SubD' scheme is used to store subdivision surfaces, i.e. the pre-subdivision + * mesh. Currently we don't add a subdivision modifier when we load such data. This code is + * assuming that the subdivided surface should be smooth. */ + read_mpolys(config, abc_mesh_data); + process_no_normals(config); + } + + if ((settings->read_flag & (MOD_MESHSEQ_READ_UV | MOD_MESHSEQ_READ_COLOR)) != 0) { + read_custom_data(iobject_full_name, schema.getArbGeomParams(), config, selector); + } +} + +/* ************************************************************************** */ + +AbcSubDReader::AbcSubDReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + m_settings->read_flag |= MOD_MESHSEQ_READ_ALL; + + ISubD isubd_mesh(m_iobject, kWrapExisting); + m_schema = isubd_mesh.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcSubDReader::valid() const +{ + return m_schema.valid(); +} + +bool AbcSubDReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::ISubD::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to SubD when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch, Alembic object path points to SubD."; + return false; + } + + return true; +} + +void AbcSubDReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, MOD_MESHSEQ_READ_ALL, NULL); + if (read_mesh != mesh) { + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + } + + ISubDSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return; + } + + Int32ArraySamplePtr indices = sample.getCreaseIndices(); + Alembic::Abc::FloatArraySamplePtr sharpnesses = sample.getCreaseSharpnesses(); + + if (indices && sharpnesses) { + MEdge *edges = mesh->medge; + int totedge = mesh->totedge; + + for (int i = 0, s = 0, e = indices->size(); i < e; i += 2, s++) { + int v1 = (*indices)[i]; + int v2 = (*indices)[i + 1]; + + if (v2 < v1) { + /* It appears to be common to store edges with the smallest index first, in which case this + * prevents us from doing the second search below. */ + std::swap(v1, v2); + } + + MEdge *edge = find_edge(edges, totedge, v1, v2); + if (edge == NULL) { + edge = find_edge(edges, totedge, v2, v1); + } + + if (edge) { + edge->crease = unit_float_to_uchar_clamp((*sharpnesses)[s]); + } + } + + mesh->cd_flag |= ME_CDFLAG_EDGE_CREASE; + } + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +Mesh *AbcSubDReader::read_mesh(Mesh *existing_mesh, + const ISampleSelector &sample_sel, + int read_flag, + const char **err_str) +{ + ISubDSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + if (err_str != nullptr) { + *err_str = "Error reading mesh sample; more detail on the console"; + } + printf("Alembic: error reading mesh sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return existing_mesh; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + const Alembic::Abc::Int32ArraySamplePtr &face_indices = sample.getFaceIndices(); + const Alembic::Abc::Int32ArraySamplePtr &face_counts = sample.getFaceCounts(); + + Mesh *new_mesh = NULL; + + ImportSettings settings; + settings.read_flag |= read_flag; + + if (existing_mesh->totvert != positions->size()) { + new_mesh = BKE_mesh_new_nomain_from_template( + existing_mesh, positions->size(), 0, 0, face_indices->size(), face_counts->size()); + + settings.read_flag |= MOD_MESHSEQ_READ_ALL; + } + else { + /* If the face count changed (e.g. by triangulation), only read points. + * This prevents crash from T49813. + * TODO(kevin): perhaps find a better way to do this? */ + if (face_counts->size() != existing_mesh->totpoly || + face_indices->size() != existing_mesh->totloop) { + settings.read_flag = MOD_MESHSEQ_READ_VERT; + + if (err_str) { + *err_str = + "Topology has changed, perhaps by triangulating the" + " mesh. Only vertices will be read!"; + } + } + } + + /* Only read point data when streaming meshes, unless we need to create new ones. */ + CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); + config.time = sample_sel.getRequestedTime(); + read_subd_sample(m_iobject.getFullName(), &settings, m_schema, sample_sel, config); + + return config.mesh; +} diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h new file mode 100644 index 00000000000..bc95c7ec134 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -0,0 +1,86 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_MESH_H__ +#define __ABC_READER_MESH_H__ + +#include "abc_customdata.h" +#include "abc_reader_object.h" + +struct Mesh; + +class AbcMeshReader : public AbcObjectReader { + Alembic::AbcGeom::IPolyMeshSchema m_schema; + + CDStreamConfig m_mesh_data; + + public: + AbcMeshReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const override; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const override; + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; + + struct Mesh *read_mesh(struct Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char **err_str) override; + bool topology_changed(Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel) override; + + private: + void readFaceSetsSample(Main *bmain, + Mesh *mesh, + const Alembic::AbcGeom::ISampleSelector &sample_sel); + + void assign_facesets_to_mpoly(const Alembic::Abc::ISampleSelector &sample_sel, + MPoly *mpoly, + int totpoly, + std::map &r_mat_map); +}; + +class AbcSubDReader : public AbcObjectReader { + Alembic::AbcGeom::ISubDSchema m_schema; + + CDStreamConfig m_mesh_data; + + public: + AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const; + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + struct Mesh *read_mesh(struct Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char **err_str); +}; + +void read_mverts(MVert *mverts, + const Alembic::AbcGeom::P3fArraySamplePtr positions, + const Alembic::AbcGeom::N3fArraySamplePtr normals); + +CDStreamConfig get_config(struct Mesh *mesh); + +#endif /* __ABC_READER_MESH_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc new file mode 100644 index 00000000000..0ada10baba5 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -0,0 +1,225 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_nurbs.h" +#include "abc_reader_transform.h" +#include "abc_util.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "BKE_curve.h" +#include "BKE_object.h" +} + +using Alembic::AbcGeom::FloatArraySamplePtr; +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; + +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::INuPatchSchema; +using Alembic::AbcGeom::IObject; + +AbcNurbsReader::AbcNurbsReader(const IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + getNurbsPatches(m_iobject); + get_min_max_time(m_iobject, m_schemas[0].first, m_min_time, m_max_time); +} + +bool AbcNurbsReader::valid() const +{ + if (m_schemas.empty()) { + return false; + } + + std::vector>::const_iterator it; + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + const INuPatchSchema &schema = it->first; + + if (!schema.valid()) { + return false; + } + } + + return true; +} + +static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) +{ + if (!knots || knots->size() == 0) { + return false; + } + + /* Skip first and last knots, as they are used for padding. */ + const size_t num_knots = knots->size() - 2; + nu_knots = static_cast(MEM_callocN(num_knots * sizeof(float), "abc_setsplineknotsu")); + + for (size_t i = 0; i < num_knots; i++) { + nu_knots[i] = (*knots)[i + 1]; + } + + return true; +} + +void AbcNurbsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) +{ + Curve *cu = static_cast(BKE_curve_add(bmain, "abc_curve", OB_SURF)); + cu->actvert = CU_ACT_NONE; + + std::vector>::iterator it; + + for (it = m_schemas.begin(); it != m_schemas.end(); ++it) { + Nurb *nu = static_cast(MEM_callocN(sizeof(Nurb), "abc_getnurb")); + nu->flag = CU_SMOOTH; + nu->type = CU_NURBS; + nu->resolu = cu->resolu; + nu->resolv = cu->resolv; + + const INuPatchSchema &schema = it->first; + INuPatchSchema::Sample smp; + try { + smp = schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading nurbs sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return; + } + + nu->orderu = smp.getUOrder() - 1; + nu->orderv = smp.getVOrder() - 1; + nu->pntsu = smp.getNumU(); + nu->pntsv = smp.getNumV(); + + /* Read positions and weights. */ + + const P3fArraySamplePtr positions = smp.getPositions(); + const FloatArraySamplePtr weights = smp.getPositionWeights(); + + const size_t num_points = positions->size(); + + nu->bp = static_cast(MEM_callocN(num_points * sizeof(BPoint), "abc_setsplinetype")); + + BPoint *bp = nu->bp; + float posw_in = 1.0f; + + for (int i = 0; i < num_points; i++, bp++) { + const Imath::V3f &pos_in = (*positions)[i]; + + if (weights) { + posw_in = (*weights)[i]; + } + + copy_zup_from_yup(bp->vec, pos_in.getValue()); + bp->vec[3] = posw_in; + bp->f1 = SELECT; + bp->radius = 1.0f; + bp->weight = 1.0f; + } + + /* Read knots. */ + + if (!set_knots(smp.getUKnot(), nu->knotsu)) { + BKE_nurb_knot_calc_u(nu); + } + + if (!set_knots(smp.getVKnot(), nu->knotsv)) { + BKE_nurb_knot_calc_v(nu); + } + + /* Read flags. */ + + ICompoundProperty user_props = schema.getUserProperties(); + + if (has_property(user_props, "enpoint_u")) { + nu->flagu |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "enpoint_v")) { + nu->flagv |= CU_NURB_ENDPOINT; + } + + if (has_property(user_props, "cyclic_u")) { + nu->flagu |= CU_NURB_CYCLIC; + } + + if (has_property(user_props, "cyclic_v")) { + nu->flagv |= CU_NURB_CYCLIC; + } + + BLI_addtail(BKE_curve_nurbs_get(cu), nu); + } + + BLI_strncpy(cu->id.name + 2, m_data_name.c_str(), m_data_name.size() + 1); + + m_object = BKE_object_add_only_object(bmain, OB_SURF, m_object_name.c_str()); + m_object->data = cu; +} + +void AbcNurbsReader::getNurbsPatches(const IObject &obj) +{ + if (!obj.valid()) { + return; + } + + const int num_children = obj.getNumChildren(); + + if (num_children == 0) { + INuPatch abc_nurb(obj, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair(schem, obj)); + return; + } + + for (int i = 0; i < num_children; i++) { + bool ok = true; + IObject child(obj, obj.getChildHeader(i).getName()); + + if (!m_name.empty() && child.valid() && !begins_with(child.getFullName(), m_name)) { + ok = false; + } + + if (!child.valid()) { + continue; + } + + const MetaData &md = child.getMetaData(); + + if (INuPatch::matches(md) && ok) { + INuPatch abc_nurb(child, kWrapExisting); + INuPatchSchema schem = abc_nurb.getSchema(); + m_schemas.push_back(std::pair(schem, child)); + } + + getNurbsPatches(child); + } +} diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h new file mode 100644 index 00000000000..f4284c136fb --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_NURBS_H__ +#define __ABC_READER_NURBS_H__ + +#include "abc_reader_object.h" + +class AbcNurbsReader : public AbcObjectReader { + std::vector> m_schemas; + + public: + AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + + private: + void getNurbsPatches(const Alembic::Abc::IObject &obj); +}; + +#endif /* __ABC_READER_NURBS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_object.cc b/source/blender/io/alembic/intern/abc_reader_object.cc new file mode 100644 index 00000000000..3e7f87d78cc --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_object.cc @@ -0,0 +1,333 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_object.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_cachefile_types.h" +#include "DNA_constraint_types.h" +#include "DNA_modifier_types.h" +#include "DNA_space_types.h" /* for FILE_MAX */ + +#include "BKE_constraint.h" +#include "BKE_lib_id.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BLI_utildefines.h" +#include "BLI_listbase.h" +#include "BLI_math_geom.h" +#include "BLI_string.h" +} + +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; + +AbcObjectReader::AbcObjectReader(const IObject &object, ImportSettings &settings) + : m_name(""), + m_object_name(""), + m_data_name(""), + m_object(NULL), + m_iobject(object), + m_settings(&settings), + m_min_time(std::numeric_limits::max()), + m_max_time(std::numeric_limits::min()), + m_refcount(0), + parent_reader(NULL) +{ + m_name = object.getFullName(); + std::vector parts; + split(m_name, '/', parts); + + if (parts.size() >= 2) { + m_object_name = parts[parts.size() - 2]; + m_data_name = parts[parts.size() - 1]; + } + else { + m_object_name = m_data_name = parts[parts.size() - 1]; + } + + determine_inherits_xform(); +} + +/* Determine whether we can inherit our parent's XForm */ +void AbcObjectReader::determine_inherits_xform() +{ + m_inherits_xform = false; + + IXform ixform = xform(); + if (!ixform) { + return; + } + + const IXformSchema &schema(ixform.getSchema()); + if (!schema.valid()) { + std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema." + << std::endl; + return; + } + + m_inherits_xform = schema.getInheritsXforms(); + + IObject ixform_parent = ixform.getParent(); + if (!ixform_parent.getParent()) { + /* The archive top object certainly is not a transform itself, so handle + * it as "no parent". */ + m_inherits_xform = false; + } + else { + m_inherits_xform = ixform_parent && m_inherits_xform; + } +} + +AbcObjectReader::~AbcObjectReader() +{ +} + +const IObject &AbcObjectReader::iobject() const +{ + return m_iobject; +} + +Object *AbcObjectReader::object() const +{ + return m_object; +} + +void AbcObjectReader::object(Object *ob) +{ + m_object = ob; +} + +static Imath::M44d blend_matrices(const Imath::M44d &m0, const Imath::M44d &m1, const float weight) +{ + float mat0[4][4], mat1[4][4], ret[4][4]; + + /* Cannot use Imath::M44d::getValue() since this returns a pointer to + * doubles and interp_m4_m4m4 expects pointers to floats. So need to convert + * the matrices manually. + */ + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + mat0[i][j] = static_cast(m0[i][j]); + } + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + mat1[i][j] = static_cast(m1[i][j]); + } + } + + interp_m4_m4m4(ret, mat0, mat1, weight); + + Imath::M44d m; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = ret[i][j]; + } + } + + return m; +} + +Imath::M44d get_matrix(const IXformSchema &schema, const float time) +{ + Alembic::AbcGeom::index_t i0, i1; + Alembic::AbcGeom::XformSample s0, s1; + + const float weight = get_weight_and_index( + time, schema.getTimeSampling(), schema.getNumSamples(), i0, i1); + + schema.get(s0, Alembic::AbcGeom::ISampleSelector(i0)); + + if (i0 != i1) { + schema.get(s1, Alembic::AbcGeom::ISampleSelector(i1)); + return blend_matrices(s0.getMatrix(), s1.getMatrix(), weight); + } + + return s0.getMatrix(); +} + +struct Mesh *AbcObjectReader::read_mesh(struct Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &UNUSED(sample_sel), + int UNUSED(read_flag), + const char **UNUSED(err_str)) +{ + return existing_mesh; +} + +bool AbcObjectReader::topology_changed(Mesh * /*existing_mesh*/, + const Alembic::Abc::ISampleSelector & /*sample_sel*/) +{ + /* The default implementation of read_mesh() just returns the original mesh, so never changes the + * topology. */ + return false; +} + +void AbcObjectReader::setupObjectTransform(const float time) +{ + bool is_constant = false; + float transform_from_alembic[4][4]; + + /* If the parent is a camera, apply the inverse rotation to make up for the from-Maya rotation. + * This assumes that the parent object also was imported from Alembic. */ + if (m_object->parent != nullptr && m_object->parent->type == OB_CAMERA) { + axis_angle_to_mat4_single(m_object->parentinv, 'X', -M_PI_2); + } + + this->read_matrix(transform_from_alembic, time, m_settings->scale, is_constant); + + /* Apply the matrix to the object. */ + BKE_object_apply_mat4(m_object, transform_from_alembic, true, false); + BKE_object_to_mat4(m_object, m_object->obmat); + + if (!is_constant) { + bConstraint *con = BKE_constraint_add_for_object( + m_object, NULL, CONSTRAINT_TYPE_TRANSFORM_CACHE); + bTransformCacheConstraint *data = static_cast(con->data); + BLI_strncpy(data->object_path, m_iobject.getFullName().c_str(), FILE_MAX); + + data->cache_file = m_settings->cache_file; + id_us_plus(&data->cache_file->id); + } +} + +Alembic::AbcGeom::IXform AbcObjectReader::xform() +{ + /* Check that we have an empty object (locator, bone head/tail...). */ + if (IXform::matches(m_iobject.getMetaData())) { + try { + return IXform(m_iobject, Alembic::AbcGeom::kWrapExisting); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading object transform for '%s': %s\n", + m_iobject.getFullName().c_str(), + ex.what()); + return IXform(); + } + } + + /* Check that we have an object with actual data, in which case the + * parent Alembic object should contain the transform. */ + IObject abc_parent = m_iobject.getParent(); + + /* The archive's top object can be recognised by not having a parent. */ + if (abc_parent.getParent() && IXform::matches(abc_parent.getMetaData())) { + try { + return IXform(abc_parent, Alembic::AbcGeom::kWrapExisting); + } + catch (Alembic::Util::Exception &ex) { + printf("Alembic: error reading object transform for '%s': %s\n", + abc_parent.getFullName().c_str(), + ex.what()); + return IXform(); + } + } + + /* This can happen in certain cases. For example, MeshLab exports + * point clouds without parent XForm. */ + return IXform(); +} + +void AbcObjectReader::read_matrix(float r_mat[4][4] /* local matrix */, + const float time, + const float scale, + bool &is_constant) +{ + IXform ixform = xform(); + if (!ixform) { + unit_m4(r_mat); + is_constant = true; + return; + } + + const IXformSchema &schema(ixform.getSchema()); + if (!schema.valid()) { + std::cerr << "Alembic object " << ixform.getFullName() << " has an invalid schema." + << std::endl; + return; + } + + const Imath::M44d matrix = get_matrix(schema, time); + convert_matrix_datatype(matrix, r_mat); + copy_m44_axis_swap(r_mat, r_mat, ABC_ZUP_FROM_YUP); + + /* Convert from Maya to Blender camera orientation. Children of this camera + * will have the opposite transform as their Parent Inverse matrix. + * See AbcObjectReader::setupObjectTransform(). */ + if (m_object->type == OB_CAMERA) { + float camera_rotation[4][4]; + axis_angle_to_mat4_single(camera_rotation, 'X', M_PI_2); + mul_m4_m4m4(r_mat, r_mat, camera_rotation); + } + + if (!m_inherits_xform) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, scale); + mul_m4_m4m4(r_mat, scale_mat, r_mat); + } + + is_constant = schema.isConstant(); +} + +void AbcObjectReader::addCacheModifier() +{ + ModifierData *md = modifier_new(eModifierType_MeshSequenceCache); + BLI_addtail(&m_object->modifiers, md); + + MeshSeqCacheModifierData *mcmd = reinterpret_cast(md); + + mcmd->cache_file = m_settings->cache_file; + id_us_plus(&mcmd->cache_file->id); + + BLI_strncpy(mcmd->object_path, m_iobject.getFullName().c_str(), FILE_MAX); +} + +chrono_t AbcObjectReader::minTime() const +{ + return m_min_time; +} + +chrono_t AbcObjectReader::maxTime() const +{ + return m_max_time; +} + +int AbcObjectReader::refcount() const +{ + return m_refcount; +} + +void AbcObjectReader::incref() +{ + m_refcount++; +} + +void AbcObjectReader::decref() +{ + m_refcount--; + BLI_assert(m_refcount >= 0); +} diff --git a/source/blender/io/alembic/intern/abc_reader_object.h b/source/blender/io/alembic/intern/abc_reader_object.h new file mode 100644 index 00000000000..94923df2df9 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_object.h @@ -0,0 +1,171 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_OBJECT_H__ +#define __ABC_READER_OBJECT_H__ + +#include +#include + +extern "C" { +#include "DNA_ID.h" +} + +struct CacheFile; +struct Main; +struct Mesh; +struct Object; + +using Alembic::AbcCoreAbstract::chrono_t; + +struct ImportSettings { + bool do_convert_mat; + float conversion_mat[4][4]; + + int from_up; + int from_forward; + float scale; + bool is_sequence; + bool set_frame_range; + + /* Length and frame offset of file sequences. */ + int sequence_len; + int sequence_offset; + + /* From MeshSeqCacheModifierData.read_flag */ + int read_flag; + + bool validate_meshes; + + CacheFile *cache_file; + + ImportSettings() + : do_convert_mat(false), + from_up(0), + from_forward(0), + scale(1.0f), + is_sequence(false), + set_frame_range(false), + sequence_len(1), + sequence_offset(0), + read_flag(0), + validate_meshes(false), + cache_file(NULL) + { + } +}; + +template static bool has_animations(Schema &schema, ImportSettings *settings) +{ + return settings->is_sequence || !schema.isConstant(); +} + +class AbcObjectReader { + protected: + std::string m_name; + std::string m_object_name; + std::string m_data_name; + Object *m_object; + Alembic::Abc::IObject m_iobject; + + ImportSettings *m_settings; + + chrono_t m_min_time; + chrono_t m_max_time; + + /* Use reference counting since the same reader may be used by multiple + * modifiers and/or constraints. */ + int m_refcount; + + bool m_inherits_xform; + + public: + AbcObjectReader *parent_reader; + + public: + explicit AbcObjectReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + virtual ~AbcObjectReader(); + + const Alembic::Abc::IObject &iobject() const; + + typedef std::vector ptr_vector; + + /** + * Returns the transform of this object. This can be the Alembic object + * itself (in case of an Empty) or it can be the parent Alembic object. + */ + virtual Alembic::AbcGeom::IXform xform(); + + Object *object() const; + void object(Object *ob); + + const std::string &name() const + { + return m_name; + } + const std::string &object_name() const + { + return m_object_name; + } + const std::string &data_name() const + { + return m_data_name; + } + bool inherits_xform() const + { + return m_inherits_xform; + } + + virtual bool valid() const = 0; + virtual bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const = 0; + + virtual void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) = 0; + + virtual struct Mesh *read_mesh(struct Mesh *mesh, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char **err_str); + virtual bool topology_changed(Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel); + + /** Reads the object matrix and sets up an object transform if animated. */ + void setupObjectTransform(const float time); + + void addCacheModifier(); + + chrono_t minTime() const; + chrono_t maxTime() const; + + int refcount() const; + void incref(); + void decref(); + + void read_matrix(float r_mat[4][4], const float time, const float scale, bool &is_constant); + + protected: + void determine_inherits_xform(); +}; + +Imath::M44d get_matrix(const Alembic::AbcGeom::IXformSchema &schema, const float time); + +#endif /* __ABC_READER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc new file mode 100644 index 00000000000..e4dc345f868 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -0,0 +1,157 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_points.h" +#include "abc_reader_mesh.h" +#include "abc_reader_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +} + +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::P3fArraySamplePtr; + +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::ISampleSelector; + +AbcPointsReader::AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + IPoints ipoints(m_iobject, kWrapExisting); + m_schema = ipoints.getSchema(); + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcPointsReader::valid() const +{ + return m_schema.valid(); +} + +bool AbcPointsReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::IPoints::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to Points when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_MESH) { + *err_str = "Object type mismatch, Alembic object path points to Points."; + return false; + } + + return true; +} + +void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) +{ + Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, NULL); + + if (read_mesh != mesh) { + BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); + } + + if (m_settings->validate_meshes) { + BKE_mesh_validate(mesh, false, false); + } + + m_object = BKE_object_add_only_object(bmain, OB_MESH, m_object_name.c_str()); + m_object->data = mesh; + + if (has_animations(m_schema, m_settings)) { + addCacheModifier(); + } +} + +void read_points_sample(const IPointsSchema &schema, + const ISampleSelector &selector, + CDStreamConfig &config) +{ + Alembic::AbcGeom::IPointsSchema::Sample sample = schema.getValue(selector); + + const P3fArraySamplePtr &positions = sample.getPositions(); + + ICompoundProperty prop = schema.getArbGeomParams(); + N3fArraySamplePtr vnormals; + + if (has_property(prop, "N")) { + const Alembic::Util::uint32_t itime = static_cast( + selector.getRequestedTime()); + const IN3fArrayProperty &normals_prop = IN3fArrayProperty(prop, "N", itime); + + if (normals_prop) { + vnormals = normals_prop.getValue(selector); + } + } + + read_mverts(config.mvert, positions, vnormals); +} + +struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh, + const ISampleSelector &sample_sel, + int /*read_flag*/, + const char **err_str) +{ + IPointsSchema::Sample sample; + try { + sample = m_schema.getValue(sample_sel); + } + catch (Alembic::Util::Exception &ex) { + *err_str = "Error reading points sample; more detail on the console"; + printf("Alembic: error reading points sample for '%s/%s' at time %f: %s\n", + m_iobject.getFullName().c_str(), + m_schema.getName().c_str(), + sample_sel.getRequestedTime(), + ex.what()); + return existing_mesh; + } + + const P3fArraySamplePtr &positions = sample.getPositions(); + + Mesh *new_mesh = NULL; + + if (existing_mesh->totvert != positions->size()) { + new_mesh = BKE_mesh_new_nomain(positions->size(), 0, 0, 0, 0); + } + + CDStreamConfig config = get_config(new_mesh ? new_mesh : existing_mesh); + read_points_sample(m_schema, sample_sel, config); + + return new_mesh ? new_mesh : existing_mesh; +} diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h new file mode 100644 index 00000000000..31ad6c4589b --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -0,0 +1,54 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_POINTS_H__ +#define __ABC_READER_POINTS_H__ + +#include "abc_reader_object.h" +#include "abc_customdata.h" + +class AbcPointsReader : public AbcObjectReader { + Alembic::AbcGeom::IPointsSchema m_schema; + Alembic::AbcGeom::IPointsSchema::Sample m_sample; + + public: + AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + + struct Mesh *read_mesh(struct Mesh *existing_mesh, + const Alembic::Abc::ISampleSelector &sample_sel, + int read_flag, + const char **err_str); +}; + +void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, + const Alembic::AbcGeom::ISampleSelector &selector, + CDStreamConfig &config); + +#endif /* __ABC_READER_POINTS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_reader_transform.cc b/source/blender/io/alembic/intern/abc_reader_transform.cc new file mode 100644 index 00000000000..ce569a9ccb5 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_transform.cc @@ -0,0 +1,76 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_reader_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_utildefines.h" + +#include "BKE_object.h" +} + +using Alembic::Abc::ISampleSelector; + +AbcEmptyReader::AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings) + : AbcObjectReader(object, settings) +{ + /* Empties have no data. It makes the import of Alembic files easier to + * understand when we name the empty after its name in Alembic. */ + m_object_name = object.getName(); + + Alembic::AbcGeom::IXform xform(object, Alembic::AbcGeom::kWrapExisting); + m_schema = xform.getSchema(); + + get_min_max_time(m_iobject, m_schema, m_min_time, m_max_time); +} + +bool AbcEmptyReader::valid() const +{ + return m_schema.valid(); +} + +bool AbcEmptyReader::accepts_object_type( + const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::IXform::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to XForm when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_EMPTY) { + *err_str = "Object type mismatch, Alembic object path points to XForm."; + return false; + } + + return true; +} + +void AbcEmptyReader::readObjectData(Main *bmain, const ISampleSelector &UNUSED(sample_sel)) +{ + m_object = BKE_object_add_only_object(bmain, OB_EMPTY, m_object_name.c_str()); + m_object->data = NULL; +} diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h new file mode 100644 index 00000000000..6b4d23c1884 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_reader_transform.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_READER_TRANSFORM_H__ +#define __ABC_READER_TRANSFORM_H__ + +#include "abc_reader_object.h" + +#include + +class AbcEmptyReader : public AbcObjectReader { + Alembic::AbcGeom::IXformSchema m_schema; + + public: + AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); + + bool valid() const; + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); +}; + +#endif /* __ABC_READER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/abc_util.cc b/source/blender/io/alembic/intern/abc_util.cc new file mode 100644 index 00000000000..b26ef8b3b76 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_util.cc @@ -0,0 +1,393 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_util.h" + +#include "abc_reader_camera.h" +#include "abc_reader_curves.h" +#include "abc_reader_mesh.h" +#include "abc_reader_nurbs.h" +#include "abc_reader_points.h" +#include "abc_reader_transform.h" + +#include + +#include + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" + +#include "PIL_time.h" +} + +std::string get_id_name(const Object *const ob) +{ + if (!ob) { + return ""; + } + + return get_id_name(&ob->id); +} + +std::string get_id_name(const ID *const id) +{ + std::string name(id->name + 2); + std::replace(name.begin(), name.end(), ' ', '_'); + std::replace(name.begin(), name.end(), '.', '_'); + std::replace(name.begin(), name.end(), ':', '_'); + + return name; +} + +/** + * \brief get_object_dag_path_name returns the name under which the object + * will be exported in the Alembic file. It is of the form + * "[../grandparent/]parent/object" if dupli_parent is NULL, or + * "dupli_parent/[../grandparent/]parent/object" otherwise. + * \param ob: + * \param dupli_parent: + * \return + */ +std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent) +{ + std::string name = get_id_name(ob); + + Object *p = ob->parent; + + while (p) { + name = get_id_name(p) + "/" + name; + p = p->parent; + } + + if (dupli_parent && (ob != dupli_parent)) { + name = get_id_name(dupli_parent) + "/" + name; + } + + return name; +} + +Imath::M44d convert_matrix_datatype(float mat[4][4]) +{ + Imath::M44d m; + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + m[i][j] = mat[i][j]; + } + } + + return m; +} + +void convert_matrix_datatype(const Imath::M44d &xform, float r_mat[4][4]) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r_mat[i][j] = static_cast(xform[i][j]); + } + } +} + +void split(const std::string &s, const char delim, std::vector &tokens) +{ + tokens.clear(); + + std::stringstream ss(s); + std::string item; + + while (std::getline(ss, item, delim)) { + if (!item.empty()) { + tokens.push_back(item); + } + } +} + +void create_swapped_rotation_matrix(float rot_x_mat[3][3], + float rot_y_mat[3][3], + float rot_z_mat[3][3], + const float euler[3], + AbcAxisSwapMode mode) +{ + const float rx = euler[0]; + float ry; + float rz; + + /* Apply transformation */ + switch (mode) { + case ABC_ZUP_FROM_YUP: + ry = -euler[2]; + rz = euler[1]; + break; + case ABC_YUP_FROM_ZUP: + ry = euler[2]; + rz = -euler[1]; + break; + default: + ry = 0.0f; + rz = 0.0f; + BLI_assert(false); + break; + } + + unit_m3(rot_x_mat); + unit_m3(rot_y_mat); + unit_m3(rot_z_mat); + + rot_x_mat[1][1] = cos(rx); + rot_x_mat[2][1] = -sin(rx); + rot_x_mat[1][2] = sin(rx); + rot_x_mat[2][2] = cos(rx); + + rot_y_mat[2][2] = cos(ry); + rot_y_mat[0][2] = -sin(ry); + rot_y_mat[2][0] = sin(ry); + rot_y_mat[0][0] = cos(ry); + + rot_z_mat[0][0] = cos(rz); + rot_z_mat[1][0] = -sin(rz); + rot_z_mat[0][1] = sin(rz); + rot_z_mat[1][1] = cos(rz); +} + +/* Convert matrix from Z=up to Y=up or vice versa. + * Use yup_mat = zup_mat for in-place conversion. */ +void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode) +{ + float dst_rot[3][3], src_rot[3][3], dst_scale_mat[4][4]; + float rot_x_mat[3][3], rot_y_mat[3][3], rot_z_mat[3][3]; + float src_trans[3], dst_scale[3], src_scale[3], euler[3]; + + zero_v3(src_trans); + zero_v3(dst_scale); + zero_v3(src_scale); + zero_v3(euler); + unit_m3(src_rot); + unit_m3(dst_rot); + unit_m4(dst_scale_mat); + + /* TODO(Sybren): This code assumes there is no sheer component and no + * homogeneous scaling component, which is not always true when writing + * non-hierarchical (e.g. flat) objects (e.g. when parent has non-uniform + * scale and the child rotates). This is currently not taken into account + * when axis-swapping. */ + + /* Extract translation, rotation, and scale form matrix. */ + mat4_to_loc_rot_size(src_trans, src_rot, src_scale, src_mat); + + /* Get euler angles from rotation matrix. */ + mat3_to_eulO(euler, ROT_MODE_XZY, src_rot); + + /* Create X, Y, Z rotation matrices from euler angles. */ + create_swapped_rotation_matrix(rot_x_mat, rot_y_mat, rot_z_mat, euler, mode); + + /* Concatenate rotation matrices. */ + mul_m3_m3m3(dst_rot, dst_rot, rot_z_mat); + mul_m3_m3m3(dst_rot, dst_rot, rot_y_mat); + mul_m3_m3m3(dst_rot, dst_rot, rot_x_mat); + + mat3_to_eulO(euler, ROT_MODE_XZY, dst_rot); + + /* Start construction of dst_mat from rotation matrix */ + unit_m4(dst_mat); + copy_m4_m3(dst_mat, dst_rot); + + /* Apply translation */ + switch (mode) { + case ABC_ZUP_FROM_YUP: + copy_zup_from_yup(dst_mat[3], src_trans); + break; + case ABC_YUP_FROM_ZUP: + copy_yup_from_zup(dst_mat[3], src_trans); + break; + default: + BLI_assert(false); + } + + /* Apply scale matrix. Swaps y and z, but does not + * negate like translation does. */ + dst_scale[0] = src_scale[0]; + dst_scale[1] = src_scale[2]; + dst_scale[2] = src_scale[1]; + + size_to_mat4(dst_scale_mat, dst_scale); + mul_m4_m4m4(dst_mat, dst_mat, dst_scale_mat); +} + +/* Recompute transform matrix of object in new coordinate system + * (from Z-Up to Y-Up). */ +void create_transform_matrix(Object *obj, + float r_yup_mat[4][4], + AbcMatrixMode mode, + Object *proxy_from) +{ + float zup_mat[4][4]; + + /* get local or world matrix. */ + if (mode == ABC_MATRIX_LOCAL && obj->parent) { + /* Note that this produces another matrix than the local matrix, due to + * constraints and modifiers as well as the obj->parentinv matrix. */ + invert_m4_m4(obj->parent->imat, obj->parent->obmat); + mul_m4_m4m4(zup_mat, obj->parent->imat, obj->obmat); + } + else { + copy_m4_m4(zup_mat, obj->obmat); + } + + if (proxy_from) { + mul_m4_m4m4(zup_mat, proxy_from->obmat, zup_mat); + } + + copy_m44_axis_swap(r_yup_mat, zup_mat, ABC_YUP_FROM_ZUP); +} + +bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name) +{ + if (!prop.valid()) { + return false; + } + + return prop.getPropertyHeader(name) != NULL; +} + +typedef std::pair index_time_pair_t; + +float get_weight_and_index(float time, + const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, + int samples_number, + Alembic::AbcGeom::index_t &i0, + Alembic::AbcGeom::index_t &i1) +{ + samples_number = std::max(samples_number, 1); + + index_time_pair_t t0 = time_sampling->getFloorIndex(time, samples_number); + i0 = i1 = t0.first; + + if (samples_number == 1 || (fabs(time - t0.second) < 0.0001f)) { + return 0.0f; + } + + index_time_pair_t t1 = time_sampling->getCeilIndex(time, samples_number); + i1 = t1.first; + + if (i0 == i1) { + return 0.0f; + } + + const float bias = (time - t0.second) / (t1.second - t0.second); + + if (fabs(1.0f - bias) < 0.0001f) { + i0 = i1; + return 0.0f; + } + + return bias; +} + +//#define USE_NURBS + +AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings) +{ + AbcObjectReader *reader = NULL; + + const Alembic::AbcGeom::MetaData &md = object.getMetaData(); + + if (Alembic::AbcGeom::IXform::matches(md)) { + reader = new AbcEmptyReader(object, settings); + } + else if (Alembic::AbcGeom::IPolyMesh::matches(md)) { + reader = new AbcMeshReader(object, settings); + } + else if (Alembic::AbcGeom::ISubD::matches(md)) { + reader = new AbcSubDReader(object, settings); + } + else if (Alembic::AbcGeom::INuPatch::matches(md)) { +#ifdef USE_NURBS + /* TODO(kevin): importing cyclic NURBS from other software crashes + * at the moment. This is due to the fact that NURBS in other + * software have duplicated points which causes buffer overflows in + * Blender. Need to figure out exactly how these points are + * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). + * Until this is fixed, disabling NURBS reading. */ + reader = new AbcNurbsReader(child, settings); +#endif + } + else if (Alembic::AbcGeom::ICamera::matches(md)) { + reader = new AbcCameraReader(object, settings); + } + else if (Alembic::AbcGeom::IPoints::matches(md)) { + reader = new AbcPointsReader(object, settings); + } + else if (Alembic::AbcMaterial::IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (Alembic::AbcGeom::ILight::matches(md)) { + /* Pass for now. */ + } + else if (Alembic::AbcGeom::IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (Alembic::AbcGeom::ICurves::matches(md)) { + reader = new AbcCurveReader(object, settings); + } + else { + std::cerr << "Alembic: unknown how to handle objects of schema '" << md.get("schemaObjTitle") + << "', skipping object '" << object.getFullName() << "'" << std::endl; + } + + return reader; +} + +/* ********************** */ + +ScopeTimer::ScopeTimer(const char *message) + : m_message(message), m_start(PIL_check_seconds_timer()) +{ +} + +ScopeTimer::~ScopeTimer() +{ + fprintf(stderr, "%s: %fs\n", m_message, PIL_check_seconds_timer() - m_start); +} + +/* ********************** */ + +std::string SimpleLogger::str() const +{ + return m_stream.str(); +} + +void SimpleLogger::clear() +{ + m_stream.clear(); + m_stream.str(""); +} + +std::ostringstream &SimpleLogger::stream() +{ + return m_stream; +} + +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger) +{ + os << logger.str(); + return os; +} diff --git a/source/blender/io/alembic/intern/abc_util.h b/source/blender/io/alembic/intern/abc_util.h new file mode 100644 index 00000000000..0b3462c2132 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_util.h @@ -0,0 +1,236 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_UTIL_H__ +#define __ABC_UTIL_H__ + +#include +#include + +#ifdef _MSC_VER +# define ABC_INLINE static __forceinline +#else +# define ABC_INLINE static inline +#endif + +/** + * \brief The CacheReader struct is only used for anonymous pointers, + * to interface between C and C++ code. This library only creates + * pointers to AbcObjectReader (or subclasses thereof). + */ +struct CacheReader { + int unused; +}; + +using Alembic::Abc::chrono_t; + +class AbcObjectReader; +struct ImportSettings; + +struct ID; +struct Object; + +std::string get_id_name(const ID *const id); +std::string get_id_name(const Object *const ob); +std::string get_object_dag_path_name(const Object *const ob, Object *dupli_parent); + +/* Convert from float to Alembic matrix representations. Does NOT convert from Z-up to Y-up. */ +Imath::M44d convert_matrix_datatype(float mat[4][4]); +/* Convert from Alembic to float matrix representations. Does NOT convert from Y-up to Z-up. */ +void convert_matrix_datatype(const Imath::M44d &xform, float r_mat[4][4]); + +typedef enum { + ABC_MATRIX_WORLD = 1, + ABC_MATRIX_LOCAL = 2, +} AbcMatrixMode; +void create_transform_matrix(Object *obj, + float r_transform_mat[4][4], + AbcMatrixMode mode, + Object *proxy_from); + +void split(const std::string &s, const char delim, std::vector &tokens); + +template bool begins_with(const TContainer &input, const TContainer &match) +{ + return input.size() >= match.size() && std::equal(match.begin(), match.end(), input.begin()); +} + +template +void get_min_max_time_ex(const Schema &schema, chrono_t &min, chrono_t &max) +{ + const Alembic::Abc::TimeSamplingPtr &time_samp = schema.getTimeSampling(); + + if (!schema.isConstant()) { + const size_t num_samps = schema.getNumSamples(); + + if (num_samps > 0) { + const chrono_t min_time = time_samp->getSampleTime(0); + min = std::min(min, min_time); + + const chrono_t max_time = time_samp->getSampleTime(num_samps - 1); + max = std::max(max, max_time); + } + } +} + +template +void get_min_max_time(const Alembic::AbcGeom::IObject &object, + const Schema &schema, + chrono_t &min, + chrono_t &max) +{ + get_min_max_time_ex(schema, min, max); + + const Alembic::AbcGeom::IObject &parent = object.getParent(); + if (parent.valid() && Alembic::AbcGeom::IXform::matches(parent.getMetaData())) { + Alembic::AbcGeom::IXform xform(parent, Alembic::AbcGeom::kWrapExisting); + get_min_max_time_ex(xform.getSchema(), min, max); + } +} + +bool has_property(const Alembic::Abc::ICompoundProperty &prop, const std::string &name); + +float get_weight_and_index(float time, + const Alembic::AbcCoreAbstract::TimeSamplingPtr &time_sampling, + int samples_number, + Alembic::AbcGeom::index_t &i0, + Alembic::AbcGeom::index_t &i1); + +AbcObjectReader *create_reader(const Alembic::AbcGeom::IObject &object, ImportSettings &settings); + +/* ************************** */ + +/* TODO(kevin): for now keeping these transformations hardcoded to make sure + * everything works properly, and also because Alembic is almost exclusively + * used in Y-up software, but eventually they'll be set by the user in the UI + * like other importers/exporters do, to support other axis. */ + +/* Copy from Y-up to Z-up. */ + +ABC_INLINE void copy_zup_from_yup(float zup[3], const float yup[3]) +{ + const float old_yup1 = yup[1]; /* in case zup == yup */ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = old_yup1; +} + +ABC_INLINE void copy_zup_from_yup(short zup[3], const short yup[3]) +{ + const short old_yup1 = yup[1]; /* in case zup == yup */ + zup[0] = yup[0]; + zup[1] = -yup[2]; + zup[2] = old_yup1; +} + +/* Copy from Z-up to Y-up. */ + +ABC_INLINE void copy_yup_from_zup(float yup[3], const float zup[3]) +{ + const float old_zup1 = zup[1]; /* in case yup == zup */ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -old_zup1; +} + +ABC_INLINE void copy_yup_from_zup(short yup[3], const short zup[3]) +{ + const short old_zup1 = zup[1]; /* in case yup == zup */ + yup[0] = zup[0]; + yup[1] = zup[2]; + yup[2] = -old_zup1; +} + +/* Names are given in (dst, src) order, just like + * the parameters of copy_m44_axis_swap() */ +typedef enum { + ABC_ZUP_FROM_YUP = 1, + ABC_YUP_FROM_ZUP = 2, +} AbcAxisSwapMode; + +/* Create a rotation matrix for each axis from euler angles. + * Euler angles are swapped to change coordinate system. */ +void create_swapped_rotation_matrix(float rot_x_mat[3][3], + float rot_y_mat[3][3], + float rot_z_mat[3][3], + const float euler[3], + AbcAxisSwapMode mode); + +void copy_m44_axis_swap(float dst_mat[4][4], float src_mat[4][4], AbcAxisSwapMode mode); + +/* *************************** */ + +#undef ABC_DEBUG_TIME + +class ScopeTimer { + const char *m_message; + double m_start; + + public: + ScopeTimer(const char *message); + ~ScopeTimer(); +}; + +#ifdef ABC_DEBUG_TIME +# define SCOPE_TIMER(message) ScopeTimer prof(message) +#else +# define SCOPE_TIMER(message) +#endif + +/* *************************** */ + +/** + * Utility class whose purpose is to more easily log related information. An + * instance of the SimpleLogger can be created in any context, and will hold a + * copy of all the strings passed to its output stream. + * + * Different instances of the class may be accessed from different threads, + * although accessing the same instance from different threads will lead to race + * conditions. + */ +class SimpleLogger { + std::ostringstream m_stream; + + public: + /** + * Return a copy of the string contained in the SimpleLogger's stream. + */ + std::string str() const; + + /** + * Remove the bits set on the SimpleLogger's stream and clear its string. + */ + void clear(); + + /** + * Return a reference to the SimpleLogger's stream, in order to e.g. push + * content into it. + */ + std::ostringstream &stream(); +}; + +#define ABC_LOG(logger) logger.stream() + +/** + * Pass the content of the logger's stream to the specified std::ostream. + */ +std::ostream &operator<<(std::ostream &os, const SimpleLogger &logger); + +#endif /* __ABC_UTIL_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_archive.cc b/source/blender/io/alembic/intern/abc_writer_archive.cc new file mode 100644 index 00000000000..af18d480a18 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_archive.cc @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_archive.h" +extern "C" { +#include "BKE_blender_version.h" + +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "DNA_scene_types.h" +} + +#ifdef WIN32 +# include "utfconv.h" +#endif + +#include + +using Alembic::Abc::ErrorHandler; +using Alembic::Abc::kWrapExisting; +using Alembic::Abc::OArchive; + +/* This kinda duplicates CreateArchiveWithInfo, but Alembic does not seem to + * have a version supporting streams. */ +static OArchive create_archive(std::ostream *ostream, + const std::string &filename, + const std::string &scene_name, + double scene_fps, + bool ogawa) +{ + Alembic::Abc::MetaData abc_metadata; + + abc_metadata.set(Alembic::Abc::kApplicationNameKey, "Blender"); + abc_metadata.set(Alembic::Abc::kUserDescriptionKey, scene_name); + abc_metadata.set("blender_version", versionstr); + abc_metadata.set("FramesPerTimeUnit", std::to_string(scene_fps)); + + time_t raw_time; + time(&raw_time); + char buffer[128]; + +#if defined _WIN32 || defined _WIN64 + ctime_s(buffer, 128, &raw_time); +#else + ctime_r(&raw_time, buffer); +#endif + + const std::size_t buffer_len = strlen(buffer); + if (buffer_len > 0 && buffer[buffer_len - 1] == '\n') { + buffer[buffer_len - 1] = '\0'; + } + + abc_metadata.set(Alembic::Abc::kDateWrittenKey, buffer); + + ErrorHandler::Policy policy = ErrorHandler::kThrowPolicy; + +#ifdef WITH_ALEMBIC_HDF5 + if (!ogawa) { + return OArchive(Alembic::AbcCoreHDF5::WriteArchive(), filename, abc_metadata, policy); + } +#else + static_cast(filename); + static_cast(ogawa); +#endif + + Alembic::AbcCoreOgawa::WriteArchive archive_writer; + return OArchive(archive_writer(ostream, abc_metadata), kWrapExisting, policy); +} + +ArchiveWriter::ArchiveWriter(const char *filename, + const std::string &abc_scene_name, + const Scene *scene, + bool do_ogawa) +{ + /* Use stream to support unicode character paths on Windows. */ + if (do_ogawa) { +#ifdef WIN32 + UTF16_ENCODE(filename); + std::wstring wstr(filename_16); + m_outfile.open(wstr.c_str(), std::ios::out | std::ios::binary); + UTF16_UN_ENCODE(filename); +#else + m_outfile.open(filename, std::ios::out | std::ios::binary); +#endif + } + + m_archive = create_archive(&m_outfile, filename, abc_scene_name, FPS, do_ogawa); +} + +OArchive &ArchiveWriter::archive() +{ + return m_archive; +} diff --git a/source/blender/io/alembic/intern/abc_writer_archive.h b/source/blender/io/alembic/intern/abc_writer_archive.h new file mode 100644 index 00000000000..e261e60990a --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_archive.h @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_ARCHIVE_H__ +#define __ABC_WRITER_ARCHIVE_H__ + +#include + +#ifdef WITH_ALEMBIC_HDF5 +# include +#endif + +#include + +#include + +struct Main; +struct Scene; + +/* Wrappers around input and output archives. The goal is to be able to use + * streams so that unicode paths work on Windows (T49112), and to make sure that + * the stream objects remain valid as long as the archives are open. + */ + +class ArchiveWriter { + std::ofstream m_outfile; + Alembic::Abc::OArchive m_archive; + + public: + ArchiveWriter(const char *filename, + const std::string &abc_scene_name, + const Scene *scene, + bool do_ogawa); + + Alembic::Abc::OArchive &archive(); +}; + +#endif /* __ABC_WRITER_ARCHIVE_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_camera.cc b/source/blender/io/alembic/intern/abc_writer_camera.cc new file mode 100644 index 00000000000..e705e5ba911 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_camera.cc @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_camera.h" +#include "abc_writer_transform.h" + +extern "C" { +#include "DNA_camera_types.h" +#include "DNA_object_types.h" +} + +using Alembic::AbcGeom::OCamera; +using Alembic::AbcGeom::OFloatProperty; + +AbcCameraWriter::AbcCameraWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + OCamera camera(parent->alembicXform(), m_name, m_time_sampling); + m_camera_schema = camera.getSchema(); + + m_custom_data_container = m_camera_schema.getUserProperties(); + m_stereo_distance = OFloatProperty(m_custom_data_container, "stereoDistance", m_time_sampling); + m_eye_separation = OFloatProperty(m_custom_data_container, "eyeSeparation", m_time_sampling); +} + +void AbcCameraWriter::do_write() +{ + Camera *cam = static_cast(m_object->data); + + m_stereo_distance.set(cam->stereo.convergence_distance); + m_eye_separation.set(cam->stereo.interocular_distance); + + const double apperture_x = cam->sensor_x / 10.0; + const double apperture_y = cam->sensor_y / 10.0; + const double film_aspect = apperture_x / apperture_y; + + m_camera_sample.setFocalLength(cam->lens); + m_camera_sample.setHorizontalAperture(apperture_x); + m_camera_sample.setVerticalAperture(apperture_y); + m_camera_sample.setHorizontalFilmOffset(apperture_x * cam->shiftx); + m_camera_sample.setVerticalFilmOffset(apperture_y * cam->shifty * film_aspect); + m_camera_sample.setNearClippingPlane(cam->clip_start); + m_camera_sample.setFarClippingPlane(cam->clip_end); + + if (cam->dof.focus_object) { + Imath::V3f v(m_object->loc[0] - cam->dof.focus_object->loc[0], + m_object->loc[1] - cam->dof.focus_object->loc[1], + m_object->loc[2] - cam->dof.focus_object->loc[2]); + m_camera_sample.setFocusDistance(v.length()); + } + else { + m_camera_sample.setFocusDistance(cam->dof.focus_distance); + } + + /* Blender camera does not have an fstop param, so try to find a custom prop + * instead. */ + m_camera_sample.setFStop(cam->dof.aperture_fstop); + + m_camera_sample.setLensSqueezeRatio(1.0); + m_camera_schema.set(m_camera_sample); +} diff --git a/source/blender/io/alembic/intern/abc_writer_camera.h b/source/blender/io/alembic/intern/abc_writer_camera.h new file mode 100644 index 00000000000..3b515911a48 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_camera.h @@ -0,0 +1,45 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_CAMERA_H__ +#define __ABC_WRITER_CAMERA_H__ + +#include "abc_writer_object.h" + +/* ************************************************************************** */ + +class AbcCameraWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCameraSchema m_camera_schema; + Alembic::AbcGeom::CameraSample m_camera_sample; + Alembic::AbcGeom::OCompoundProperty m_custom_data_container; + Alembic::AbcGeom::OFloatProperty m_stereo_distance; + Alembic::AbcGeom::OFloatProperty m_eye_separation; + + public: + AbcCameraWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + private: + virtual void do_write(); +}; + +#endif /* __ABC_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_curves.cc b/source/blender/io/alembic/intern/abc_writer_curves.cc new file mode 100644 index 00000000000..3ab9b365a72 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_curves.cc @@ -0,0 +1,189 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_curves.h" +#include "abc_reader_curves.h" +#include "abc_writer_transform.h" + +extern "C" { +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BKE_curve.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +} + +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::OInt16Property; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +AbcCurveWriter::AbcCurveWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + OCurves curves(parent->alembicXform(), m_name, m_time_sampling); + m_schema = curves.getSchema(); + + Curve *cu = static_cast(m_object->data); + OCompoundProperty user_props = m_schema.getUserProperties(); + OInt16Property user_prop_resolu(user_props, ABC_CURVE_RESOLUTION_U_PROPNAME); + user_prop_resolu.set(cu->resolu); +} + +void AbcCurveWriter::do_write() +{ + Curve *curve = static_cast(m_object->data); + + std::vector verts; + std::vector vert_counts; + std::vector widths; + std::vector weights; + std::vector knots; + std::vector orders; + Imath::V3f temp_vert; + + Alembic::AbcGeom::BasisType curve_basis; + Alembic::AbcGeom::CurveType curve_type; + Alembic::AbcGeom::CurvePeriodicity periodicity; + + Nurb *nurbs = static_cast(curve->nurb.first); + for (; nurbs; nurbs = nurbs->next) { + if (nurbs->bp) { + curve_basis = Alembic::AbcGeom::kNoBasis; + curve_type = Alembic::AbcGeom::kVariableOrder; + + const int totpoint = nurbs->pntsu * nurbs->pntsv; + + const BPoint *point = nurbs->bp; + + for (int i = 0; i < totpoint; i++, point++) { + copy_yup_from_zup(temp_vert.getValue(), point->vec); + verts.push_back(temp_vert); + weights.push_back(point->vec[3]); + widths.push_back(point->radius); + } + } + else if (nurbs->bezt) { + curve_basis = Alembic::AbcGeom::kBezierBasis; + curve_type = Alembic::AbcGeom::kCubic; + + const int totpoint = nurbs->pntsu; + + const BezTriple *bezier = nurbs->bezt; + + /* TODO(kevin): store info about handles, Alembic doesn't have this. */ + for (int i = 0; i < totpoint; i++, bezier++) { + copy_yup_from_zup(temp_vert.getValue(), bezier->vec[1]); + verts.push_back(temp_vert); + widths.push_back(bezier->radius); + } + } + + if ((nurbs->flagu & CU_NURB_ENDPOINT) != 0) { + periodicity = Alembic::AbcGeom::kNonPeriodic; + } + else if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + periodicity = Alembic::AbcGeom::kPeriodic; + + /* Duplicate the start points to indicate that the curve is actually + * cyclic since other software need those. + */ + + for (int i = 0; i < nurbs->orderu; i++) { + verts.push_back(verts[i]); + } + } + + if (nurbs->knotsu != NULL) { + const size_t num_knots = KNOTSU(nurbs); + + /* Add an extra knot at the beginning and end of the array since most apps + * require/expect them. */ + knots.resize(num_knots + 2); + + for (int i = 0; i < num_knots; i++) { + knots[i + 1] = nurbs->knotsu[i]; + } + + if ((nurbs->flagu & CU_NURB_CYCLIC) != 0) { + knots[0] = nurbs->knotsu[0]; + knots[num_knots - 1] = nurbs->knotsu[num_knots - 1]; + } + else { + knots[0] = (2.0f * nurbs->knotsu[0] - nurbs->knotsu[1]); + knots[num_knots - 1] = (2.0f * nurbs->knotsu[num_knots - 1] - + nurbs->knotsu[num_knots - 2]); + } + } + + orders.push_back(nurbs->orderu); + vert_counts.push_back(verts.size()); + } + + Alembic::AbcGeom::OFloatGeomParam::Sample width_sample; + width_sample.setVals(widths); + + m_sample = OCurvesSchema::Sample(verts, + vert_counts, + curve_type, + periodicity, + width_sample, + OV2fGeomParam::Sample(), /* UVs */ + ON3fGeomParam::Sample(), /* normals */ + curve_basis, + weights, + orders, + knots); + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +AbcCurveMeshWriter::AbcCurveMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +{ +} + +Mesh *AbcCurveMeshWriter::getEvaluatedMesh(Scene * /*scene_eval*/, + Object *ob_eval, + bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + if (mesh_eval != NULL) { + /* Mesh_eval only exists when generative modifiers are in use. */ + r_needsfree = false; + return mesh_eval; + } + + r_needsfree = true; + return BKE_mesh_new_nomain_from_curve(ob_eval); +} diff --git a/source/blender/io/alembic/intern/abc_writer_curves.h b/source/blender/io/alembic/intern/abc_writer_curves.h new file mode 100644 index 00000000000..d6d8c0a7f11 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_curves.h @@ -0,0 +1,55 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_CURVES_H__ +#define __ABC_WRITER_CURVES_H__ + +#include "abc_writer_object.h" +#include "abc_writer_mesh.h" + +class AbcCurveWriter : public AbcObjectWriter { + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + + public: + AbcCurveWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + protected: + void do_write(); +}; + +class AbcCurveMeshWriter : public AbcGenericMeshWriter { + public: + AbcCurveMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + protected: + Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree); +}; + +#endif /* __ABC_WRITER_CURVES_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_hair.cc b/source/blender/io/alembic/intern/abc_writer_hair.cc new file mode 100644 index 00000000000..bbba03ed7f4 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_hair.cc @@ -0,0 +1,292 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_hair.h" +#include "abc_writer_transform.h" +#include "abc_util.h" + +#include + +extern "C" { +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" + +#include "BLI_math_geom.h" + +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_particle.h" +} + +using Alembic::Abc::P3fArraySamplePtr; + +using Alembic::AbcGeom::OCurves; +using Alembic::AbcGeom::OCurvesSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OV2fGeomParam; + +/* ************************************************************************** */ + +AbcHairWriter::AbcHairWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(ob, time_sampling, settings, parent), m_uv_warning_shown(false) +{ + m_psys = psys; + + OCurves curves(parent->alembicXform(), psys->name, m_time_sampling); + m_schema = curves.getSchema(); +} + +void AbcHairWriter::do_write() +{ + if (!m_psys) { + return; + } + Mesh *mesh = mesh_get_eval_final( + m_settings.depsgraph, m_settings.scene, m_object, &CD_MASK_MESH); + BKE_mesh_tessface_ensure(mesh); + + std::vector verts; + std::vector hvertices; + std::vector uv_values; + std::vector norm_values; + + if (m_psys->pathcache) { + ParticleSettings *part = m_psys->part; + bool export_children = m_settings.export_child_hairs && m_psys->childcache && + part->childtype != 0; + + if (!export_children || part->draw & PART_DRAW_PARENT) { + write_hair_sample(mesh, part, verts, norm_values, uv_values, hvertices); + } + + if (export_children) { + write_hair_child_sample(mesh, part, verts, norm_values, uv_values, hvertices); + } + } + + Alembic::Abc::P3fArraySample iPos(verts); + m_sample = OCurvesSchema::Sample(iPos, hvertices); + m_sample.setBasis(Alembic::AbcGeom::kNoBasis); + m_sample.setType(Alembic::AbcGeom::kLinear); + m_sample.setWrap(Alembic::AbcGeom::kNonPeriodic); + + if (!uv_values.empty()) { + OV2fGeomParam::Sample uv_smp; + uv_smp.setVals(uv_values); + m_sample.setUVs(uv_smp); + } + + if (!norm_values.empty()) { + ON3fGeomParam::Sample norm_smp; + norm_smp.setVals(norm_values); + m_sample.setNormals(norm_smp); + } + + m_sample.setSelfBounds(bounds()); + m_schema.set(m_sample); +} + +void AbcHairWriter::write_hair_sample(Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = mesh->mtface; + MFace *mface = mesh->mface; + MVert *mverts = mesh->mvert; + + if ((!mtface || !mface) && !m_uv_warning_shown) { + std::fprintf(stderr, + "Warning, no UV set found for underlying geometry of %s.\n", + m_object->id.name + 2); + m_uv_warning_shown = true; + } + + ParticleData *pa = m_psys->particles; + int k; + + ParticleCacheKey **cache = m_psys->pathcache; + ParticleCacheKey *path; + float normal[3]; + Imath::V3f tmp_nor; + + for (int p = 0; p < m_psys->totpart; p++, pa++) { + /* underlying info for faces-only emission */ + path = cache[p]; + + /* Write UV and normal vectors */ + if (part->from == PART_FROM_FACE && mtface) { + const int num = pa->num_dmcache >= 0 ? pa->num_dmcache : pa->num; + + if (num < mesh->totface) { + /* TODO(Sybren): check whether the NULL check here and if(mface) are actually required */ + MFace *face = mface == NULL ? NULL : &mface[num]; + MTFace *tface = mtface + num; + + if (mface) { + float r_uv[2], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pa->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, normal, NULL, NULL, NULL); + + copy_yup_from_zup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + } + } + else { + std::fprintf(stderr, "Particle to faces overflow (%d/%d)\n", num, mesh->totface); + } + } + else if (part->from == PART_FROM_VERT && mtface) { + /* vertex id */ + const int num = (pa->num_dmcache >= 0) ? pa->num_dmcache : pa->num; + + /* iterate over all faces to find a corresponding underlying UV */ + for (int n = 0; n < mesh->totface; n++) { + MFace *face = &mface[n]; + MTFace *tface = mtface + n; + unsigned int vtx[4]; + vtx[0] = face->v1; + vtx[1] = face->v2; + vtx[2] = face->v3; + vtx[3] = face->v4; + bool found = false; + + for (int o = 0; o < 4; o++) { + if (o > 2 && vtx[o] == 0) { + break; + } + + if (vtx[o] == num) { + uv_values.push_back(Imath::V2f(tface->uv[o][0], tface->uv[o][1])); + + MVert *mv = mverts + vtx[o]; + + normal_short_to_float_v3(normal, mv->no); + copy_yup_from_zup(tmp_nor.getValue(), normal); + norm_values.push_back(tmp_nor); + found = true; + break; + } + } + + if (found) { + break; + } + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (k = 0; k < steps; k++, path++) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + } + } +} + +void AbcHairWriter::write_hair_child_sample(Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices) +{ + /* Get untransformed vertices, there's a xform under the hair. */ + float inv_mat[4][4]; + invert_m4_m4_safe(inv_mat, m_object->obmat); + + MTFace *mtface = mesh->mtface; + MVert *mverts = mesh->mvert; + + ParticleCacheKey **cache = m_psys->childcache; + ParticleCacheKey *path; + + ChildParticle *pc = m_psys->child; + + for (int p = 0; p < m_psys->totchild; p++, pc++) { + path = cache[p]; + + if (part->from == PART_FROM_FACE && part->childtype != PART_CHILD_PARTICLES && mtface) { + const int num = pc->num; + if (num < 0) { + ABC_LOG(m_settings.logger) + << "Warning, child particle of hair system " << m_psys->name + << " has unknown face index of geometry of " << (m_object->id.name + 2) + << ", skipping child hair." << std::endl; + continue; + } + + MFace *face = &mesh->mface[num]; + MTFace *tface = mtface + num; + + float r_uv[2], tmpnor[3], mapfw[4], vec[3]; + + psys_interpolate_uvs(tface, face->v4, pc->fuv, r_uv); + uv_values.push_back(Imath::V2f(r_uv[0], r_uv[1])); + + psys_interpolate_face(mverts, face, tface, NULL, mapfw, vec, tmpnor, NULL, NULL, NULL); + + /* Convert Z-up to Y-up. */ + norm_values.push_back(Imath::V3f(tmpnor[0], tmpnor[2], -tmpnor[1])); + } + else { + if (uv_values.size()) { + uv_values.push_back(uv_values[pc->parent]); + } + if (norm_values.size()) { + norm_values.push_back(norm_values[pc->parent]); + } + } + + int steps = path->segments + 1; + hvertices.push_back(steps); + + for (int k = 0; k < steps; k++) { + float vert[3]; + copy_v3_v3(vert, path->co); + mul_m4_v3(inv_mat, vert); + + /* Convert Z-up to Y-up. */ + verts.push_back(Imath::V3f(vert[0], vert[2], -vert[1])); + + path++; + } + } +} diff --git a/source/blender/io/alembic/intern/abc_writer_hair.h b/source/blender/io/alembic/intern/abc_writer_hair.h new file mode 100644 index 00000000000..67d1b7b3d23 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_hair.h @@ -0,0 +1,62 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_HAIR_H__ +#define __ABC_WRITER_HAIR_H__ + +#include "abc_writer_object.h" + +struct ParticleSettings; +struct ParticleSystem; + +class AbcHairWriter : public AbcObjectWriter { + ParticleSystem *m_psys; + + Alembic::AbcGeom::OCurvesSchema m_schema; + Alembic::AbcGeom::OCurvesSchema::Sample m_sample; + + bool m_uv_warning_shown; + + public: + AbcHairWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + private: + virtual void do_write(); + + void write_hair_sample(struct Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices); + + void write_hair_child_sample(struct Mesh *mesh, + ParticleSettings *part, + std::vector &verts, + std::vector &norm_values, + std::vector &uv_values, + std::vector &hvertices); +}; + +#endif /* __ABC_WRITER_HAIR_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_mball.cc b/source/blender/io/alembic/intern/abc_writer_mball.cc new file mode 100644 index 00000000000..cc0775bd537 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_mball.cc @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_mball.h" +#include "abc_writer_mesh.h" + +extern "C" { +#include "DNA_meta_types.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" + +#include "BKE_displist.h" +#include "BKE_lib_id.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_utildefines.h" +} + +AbcMBallWriter::AbcMBallWriter(Main *bmain, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings), m_bmain(bmain) +{ + m_is_animated = isAnimated(); +} + +AbcMBallWriter::~AbcMBallWriter() +{ +} + +bool AbcMBallWriter::isAnimated() const +{ + return true; +} + +Mesh *AbcMBallWriter::getEvaluatedMesh(Scene * /*scene_eval*/, Object *ob_eval, bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); + if (mesh_eval != NULL) { + /* Mesh_eval only exists when generative modifiers are in use. */ + r_needsfree = false; + return mesh_eval; + } + r_needsfree = true; + + /* The approach below is copied from BKE_mesh_new_from_object() */ + Mesh *tmpmesh = BKE_mesh_add(m_bmain, ((ID *)m_object->data)->name + 2); + BLI_assert(tmpmesh != NULL); + + /* BKE_mesh_add gives us a user count we don't need */ + id_us_min(&tmpmesh->id); + + ListBase disp = {NULL, NULL}; + /* TODO(sergey): This is gonna to work for until Depsgraph + * only contains for_render flag. As soon as CoW is + * implemented, this is to be rethought. + */ + BKE_displist_make_mball_forRender(m_settings.depsgraph, m_settings.scene, m_object, &disp); + BKE_mesh_from_metaball(&disp, tmpmesh); + BKE_displist_free(&disp); + + BKE_mesh_texspace_copy_from_object(tmpmesh, m_object); + + return tmpmesh; +} + +void AbcMBallWriter::freeEvaluatedMesh(struct Mesh *mesh) +{ + BKE_id_free(m_bmain, mesh); +} + +bool AbcMBallWriter::isBasisBall(Scene *scene, Object *ob) +{ + Object *basis_ob = BKE_mball_basis_find(scene, ob); + return ob == basis_ob; +} diff --git a/source/blender/io/alembic/intern/abc_writer_mball.h b/source/blender/io/alembic/intern/abc_writer_mball.h new file mode 100644 index 00000000000..c752472c86d --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_mball.h @@ -0,0 +1,56 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_MBALL_H__ +#define __ABC_WRITER_MBALL_H__ + +#include "abc_writer_object.h" +#include "abc_writer_mesh.h" + +struct Main; +struct Object; + +/* AbcMBallWriter converts the metaballs to meshes at every frame, + * and defers to AbcGenericMeshWriter to perform the writing + * to the Alembic file. Only the basis balls are exported, as this + * results in the entire shape as one mesh. */ +class AbcMBallWriter : public AbcGenericMeshWriter { + Main *m_bmain; + + public: + explicit AbcMBallWriter(Main *bmain, + Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMBallWriter(); + + static bool isBasisBall(Scene *scene, Object *ob); + + protected: + Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; + void freeEvaluatedMesh(struct Mesh *mesh) override; + + private: + bool isAnimated() const override; +}; + +#endif /* __ABC_WRITER_MBALL_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.cc b/source/blender/io/alembic/intern/abc_writer_mesh.cc new file mode 100644 index 00000000000..b55d2473f99 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_mesh.cc @@ -0,0 +1,592 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_mesh.h" +#include "abc_writer_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_fluidsim_types.h" + +#include "BKE_animsys.h" +#include "BKE_key.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_modifier.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "DEG_depsgraph_query.h" +} + +using Alembic::Abc::FloatArraySample; +using Alembic::Abc::Int32ArraySample; +using Alembic::Abc::V2fArraySample; +using Alembic::Abc::V3fArraySample; + +using Alembic::AbcGeom::kFacevaryingScope; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::OFaceSet; +using Alembic::AbcGeom::OFaceSetSchema; +using Alembic::AbcGeom::ON3fGeomParam; +using Alembic::AbcGeom::OPolyMesh; +using Alembic::AbcGeom::OPolyMeshSchema; +using Alembic::AbcGeom::OSubD; +using Alembic::AbcGeom::OSubDSchema; +using Alembic::AbcGeom::OV2fGeomParam; +using Alembic::AbcGeom::UInt32ArraySample; + +/* NOTE: Alembic's polygon winding order is clockwise, to match with Renderman. */ + +static void get_vertices(struct Mesh *mesh, std::vector &points) +{ + points.clear(); + points.resize(mesh->totvert); + + MVert *verts = mesh->mvert; + + for (int i = 0, e = mesh->totvert; i < e; i++) { + copy_yup_from_zup(points[i].getValue(), verts[i].co); + } +} + +static void get_topology(struct Mesh *mesh, + std::vector &poly_verts, + std::vector &loop_counts, + bool &r_has_flat_shaded_poly) +{ + const int num_poly = mesh->totpoly; + const int num_loops = mesh->totloop; + MLoop *mloop = mesh->mloop; + MPoly *mpoly = mesh->mpoly; + r_has_flat_shaded_poly = false; + + poly_verts.clear(); + loop_counts.clear(); + poly_verts.reserve(num_loops); + loop_counts.reserve(num_poly); + + /* NOTE: data needs to be written in the reverse order. */ + for (int i = 0; i < num_poly; i++) { + MPoly &poly = mpoly[i]; + loop_counts.push_back(poly.totloop); + + r_has_flat_shaded_poly |= (poly.flag & ME_SMOOTH) == 0; + + MLoop *loop = mloop + poly.loopstart + (poly.totloop - 1); + + for (int j = 0; j < poly.totloop; j++, loop--) { + poly_verts.push_back(loop->v); + } + } +} + +static void get_creases(struct Mesh *mesh, + std::vector &indices, + std::vector &lengths, + std::vector &sharpnesses) +{ + const float factor = 1.0f / 255.0f; + + indices.clear(); + lengths.clear(); + sharpnesses.clear(); + + MEdge *edge = mesh->medge; + + for (int i = 0, e = mesh->totedge; i < e; i++) { + const float sharpness = static_cast(edge[i].crease) * factor; + + if (sharpness != 0.0f) { + indices.push_back(edge[i].v1); + indices.push_back(edge[i].v2); + sharpnesses.push_back(sharpness); + } + } + + lengths.resize(sharpnesses.size(), 2); +} + +static void get_loop_normals(struct Mesh *mesh, + std::vector &normals, + bool has_flat_shaded_poly) +{ + normals.clear(); + + /* If all polygons are smooth shaded, and there are no custom normals, we don't need to export + * normals at all. This is also done by other software, see T71246. */ + if (!has_flat_shaded_poly && !CustomData_has_layer(&mesh->ldata, CD_CUSTOMLOOPNORMAL)) { + return; + } + + BKE_mesh_calc_normals_split(mesh); + const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); + BLI_assert(lnors != NULL || !"BKE_mesh_calc_normals_split() should have computed CD_NORMAL"); + + normals.resize(mesh->totloop); + + /* NOTE: data needs to be written in the reverse order. */ + int abc_index = 0; + MPoly *mp = mesh->mpoly; + for (int i = 0, e = mesh->totpoly; i < e; i++, mp++) { + for (int j = mp->totloop - 1; j >= 0; j--, abc_index++) { + int blender_index = mp->loopstart + j; + copy_yup_from_zup(normals[abc_index].getValue(), lnors[blender_index]); + } + } +} + +/* *************** Modifiers *************** */ + +/* check if the mesh is a subsurf, ignoring disabled modifiers and + * displace if it's after subsurf. */ +static ModifierData *get_subsurf_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = static_cast(ob->modifiers.last); + + for (; md; md = md->prev) { + if (!modifier_isEnabled(scene, md, eModifierMode_Render)) { + continue; + } + + if (md->type == eModifierType_Subsurf) { + SubsurfModifierData *smd = reinterpret_cast(md); + + if (smd->subdivType == ME_CC_SUBSURF) { + return md; + } + } + + /* mesh is not a subsurf. break */ + if ((md->type != eModifierType_Displace) && (md->type != eModifierType_ParticleSystem)) { + return NULL; + } + } + + return NULL; +} + +static ModifierData *get_liquid_sim_modifier(Scene *scene, Object *ob) +{ + ModifierData *md = modifiers_findByType(ob, eModifierType_Fluidsim); + + if (md && (modifier_isEnabled(scene, md, eModifierMode_Render))) { + FluidsimModifierData *fsmd = reinterpret_cast(md); + + if (fsmd->fss && fsmd->fss->type == OB_FLUIDSIM_DOMAIN) { + return md; + } + } + + return NULL; +} + +/* ************************************************************************** */ + +AbcGenericMeshWriter::AbcGenericMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + m_subsurf_mod = NULL; + m_is_subd = false; + + /* If the object is static, use the default static time sampling. */ + if (!m_is_animated) { + time_sampling = 0; + } + + if (!m_settings.apply_subdiv) { + m_subsurf_mod = get_subsurf_modifier(m_settings.scene, m_object); + m_is_subd = (m_subsurf_mod != NULL); + } + + m_is_liquid = (get_liquid_sim_modifier(m_settings.scene, m_object) != NULL); + + while (parent->alembicXform().getChildHeader(m_name)) { + m_name.append("_"); + } + + if (m_settings.use_subdiv_schema && m_is_subd) { + OSubD subd(parent->alembicXform(), m_name, m_time_sampling); + m_subdiv_schema = subd.getSchema(); + } + else { + OPolyMesh mesh(parent->alembicXform(), m_name, m_time_sampling); + m_mesh_schema = mesh.getSchema(); + + OCompoundProperty typeContainer = m_mesh_schema.getUserProperties(); + OBoolProperty type(typeContainer, "meshtype"); + type.set(m_is_subd); + } +} + +AbcGenericMeshWriter::~AbcGenericMeshWriter() +{ + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } +} + +bool AbcGenericMeshWriter::isAnimated() const +{ + if (BKE_animdata_id_is_animated(static_cast(m_object->data))) { + return true; + } + if (BKE_key_from_object(m_object) != NULL) { + return true; + } + + /* Test modifiers. */ + ModifierData *md = static_cast(m_object->modifiers.first); + while (md) { + + if (md->type != eModifierType_Subsurf) { + return true; + } + + md = md->next; + } + + return false; +} + +void AbcGenericMeshWriter::setIsAnimated(bool is_animated) +{ + m_is_animated = is_animated; +} + +void AbcGenericMeshWriter::do_write() +{ + /* We have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + bool needsfree; + struct Mesh *mesh = getFinalMesh(needsfree); + + try { + if (m_settings.use_subdiv_schema && m_subdiv_schema.valid()) { + writeSubD(mesh); + } + else { + writeMesh(mesh); + } + + if (needsfree) { + freeEvaluatedMesh(mesh); + } + } + catch (...) { + if (needsfree) { + freeEvaluatedMesh(mesh); + } + throw; + } +} + +void AbcGenericMeshWriter::freeEvaluatedMesh(struct Mesh *mesh) +{ + BKE_id_free(NULL, mesh); +} + +void AbcGenericMeshWriter::writeMesh(struct Mesh *mesh) +{ + std::vector points, normals; + std::vector poly_verts, loop_counts; + std::vector velocities; + bool has_flat_shaded_poly = false; + + get_vertices(mesh, points); + get_topology(mesh, poly_verts, loop_counts, has_flat_shaded_poly); + + if (m_first_frame && m_settings.export_face_sets) { + writeFaceSets(mesh, m_mesh_schema); + } + + m_mesh_sample = OPolyMeshSchema::Sample( + V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_first_frame && m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_mesh_schema.setUVSourceName(name); + m_mesh_sample.setUVs(uv_sample); + } + + write_custom_data( + m_mesh_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + } + + if (m_settings.export_normals) { + get_loop_normals(mesh, normals, has_flat_shaded_poly); + + ON3fGeomParam::Sample normals_sample; + if (!normals.empty()) { + normals_sample.setScope(kFacevaryingScope); + normals_sample.setVals(V3fArraySample(normals)); + } + + m_mesh_sample.setNormals(normals_sample); + } + + if (m_is_liquid) { + getVelocities(mesh, velocities); + m_mesh_sample.setVelocities(V3fArraySample(velocities)); + } + + m_mesh_sample.setSelfBounds(bounds()); + + m_mesh_schema.set(m_mesh_sample); + + writeArbGeoParams(mesh); +} + +void AbcGenericMeshWriter::writeSubD(struct Mesh *mesh) +{ + std::vector crease_sharpness; + std::vector points; + std::vector poly_verts, loop_counts; + std::vector crease_indices, crease_lengths; + bool has_flat_poly = false; + + get_vertices(mesh, points); + get_topology(mesh, poly_verts, loop_counts, has_flat_poly); + get_creases(mesh, crease_indices, crease_lengths, crease_sharpness); + + if (m_first_frame && m_settings.export_face_sets) { + writeFaceSets(mesh, m_subdiv_schema); + } + + m_subdiv_sample = OSubDSchema::Sample( + V3fArraySample(points), Int32ArraySample(poly_verts), Int32ArraySample(loop_counts)); + + UVSample sample; + if (m_first_frame && m_settings.export_uvs) { + const char *name = get_uv_sample(sample, m_custom_data_config, &mesh->ldata); + + if (!sample.indices.empty() && !sample.uvs.empty()) { + OV2fGeomParam::Sample uv_sample; + uv_sample.setVals(V2fArraySample(sample.uvs)); + uv_sample.setIndices(UInt32ArraySample(sample.indices)); + uv_sample.setScope(kFacevaryingScope); + + m_subdiv_schema.setUVSourceName(name); + m_subdiv_sample.setUVs(uv_sample); + } + + write_custom_data( + m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &mesh->ldata, CD_MLOOPUV); + } + + if (!crease_indices.empty()) { + m_subdiv_sample.setCreaseIndices(Int32ArraySample(crease_indices)); + m_subdiv_sample.setCreaseLengths(Int32ArraySample(crease_lengths)); + m_subdiv_sample.setCreaseSharpnesses(FloatArraySample(crease_sharpness)); + } + + m_subdiv_sample.setSelfBounds(bounds()); + m_subdiv_schema.set(m_subdiv_sample); + + writeArbGeoParams(mesh); +} + +template void AbcGenericMeshWriter::writeFaceSets(struct Mesh *me, Schema &schema) +{ + std::map> geo_groups; + getGeoGroups(me, geo_groups); + + std::map>::iterator it; + for (it = geo_groups.begin(); it != geo_groups.end(); ++it) { + OFaceSet face_set = schema.createFaceSet(it->first); + OFaceSetSchema::Sample samp; + samp.setFaces(Int32ArraySample(it->second)); + face_set.getSchema().set(samp); + } +} + +Mesh *AbcGenericMeshWriter::getFinalMesh(bool &r_needsfree) +{ + /* We don't want subdivided mesh data */ + if (m_subsurf_mod) { + m_subsurf_mod->mode |= eModifierMode_DisableTemporary; + } + + r_needsfree = false; + + Scene *scene = DEG_get_evaluated_scene(m_settings.depsgraph); + Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); + struct Mesh *mesh = getEvaluatedMesh(scene, ob_eval, r_needsfree); + + if (m_subsurf_mod) { + m_subsurf_mod->mode &= ~eModifierMode_DisableTemporary; + } + + if (m_settings.triangulate) { + const bool tag_only = false; + const int quad_method = m_settings.quad_method; + const int ngon_method = m_settings.ngon_method; + + struct BMeshCreateParams bmcp = {false}; + struct BMeshFromMeshParams bmfmp = {true, false, false, 0}; + BMesh *bm = BKE_mesh_to_bmesh_ex(mesh, &bmcp, &bmfmp); + + BM_mesh_triangulate(bm, quad_method, ngon_method, 4, tag_only, NULL, NULL, NULL); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, NULL, mesh); + BM_mesh_free(bm); + + if (r_needsfree) { + BKE_id_free(NULL, mesh); + } + + mesh = result; + r_needsfree = true; + } + + m_custom_data_config.pack_uvs = m_settings.pack_uv; + m_custom_data_config.mpoly = mesh->mpoly; + m_custom_data_config.mloop = mesh->mloop; + m_custom_data_config.totpoly = mesh->totpoly; + m_custom_data_config.totloop = mesh->totloop; + m_custom_data_config.totvert = mesh->totvert; + + return mesh; +} + +void AbcGenericMeshWriter::writeArbGeoParams(struct Mesh *me) +{ + if (m_is_liquid) { + /* We don't need anything more for liquid meshes. */ + return; + } + + if (m_first_frame && m_settings.export_vcols) { + if (m_subdiv_schema.valid()) { + write_custom_data( + m_subdiv_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); + } + else { + write_custom_data( + m_mesh_schema.getArbGeomParams(), m_custom_data_config, &me->ldata, CD_MLOOPCOL); + } + } +} + +void AbcGenericMeshWriter::getVelocities(struct Mesh *mesh, std::vector &vels) +{ + const int totverts = mesh->totvert; + + vels.clear(); + vels.resize(totverts); + + ModifierData *md = get_liquid_sim_modifier(m_settings.scene, m_object); + FluidsimModifierData *fmd = reinterpret_cast(md); + FluidsimSettings *fss = fmd->fss; + + if (fss->meshVelocities) { + float *mesh_vels = reinterpret_cast(fss->meshVelocities); + + for (int i = 0; i < totverts; i++) { + copy_yup_from_zup(vels[i].getValue(), mesh_vels); + mesh_vels += 3; + } + } + else { + std::fill(vels.begin(), vels.end(), Imath::V3f(0.0f)); + } +} + +void AbcGenericMeshWriter::getGeoGroups(struct Mesh *mesh, + std::map> &geo_groups) +{ + const int num_poly = mesh->totpoly; + MPoly *polygons = mesh->mpoly; + + for (int i = 0; i < num_poly; i++) { + MPoly ¤t_poly = polygons[i]; + short mnr = current_poly.mat_nr; + + Material *mat = BKE_object_material_get(m_object, mnr + 1); + + if (!mat) { + continue; + } + + std::string name = get_id_name(&mat->id); + + if (geo_groups.find(name) == geo_groups.end()) { + std::vector faceArray; + geo_groups[name] = faceArray; + } + + geo_groups[name].push_back(i); + } + + if (geo_groups.size() == 0) { + Material *mat = BKE_object_material_get(m_object, 1); + + std::string name = (mat) ? get_id_name(&mat->id) : "default"; + + std::vector faceArray; + + for (int i = 0, e = mesh->totface; i < e; i++) { + faceArray.push_back(i); + } + + geo_groups[name] = faceArray; + } +} + +AbcMeshWriter::AbcMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcGenericMeshWriter(ob, parent, time_sampling, settings) +{ +} + +AbcMeshWriter::~AbcMeshWriter() +{ +} + +Mesh *AbcMeshWriter::getEvaluatedMesh(Scene *scene_eval, + Object *ob_eval, + bool &UNUSED(r_needsfree)) +{ + return mesh_get_eval_final(m_settings.depsgraph, scene_eval, ob_eval, &CD_MASK_MESH); +} diff --git a/source/blender/io/alembic/intern/abc_writer_mesh.h b/source/blender/io/alembic/intern/abc_writer_mesh.h new file mode 100644 index 00000000000..9152a370e4f --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_mesh.h @@ -0,0 +1,91 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_MESH_H__ +#define __ABC_WRITER_MESH_H__ + +#include "abc_customdata.h" +#include "abc_writer_object.h" + +struct Mesh; +struct ModifierData; + +/* Writer for Alembic meshes. Does not assume the object is a mesh object. */ +class AbcGenericMeshWriter : public AbcObjectWriter { + protected: + Alembic::AbcGeom::OPolyMeshSchema m_mesh_schema; + Alembic::AbcGeom::OPolyMeshSchema::Sample m_mesh_sample; + + Alembic::AbcGeom::OSubDSchema m_subdiv_schema; + Alembic::AbcGeom::OSubDSchema::Sample m_subdiv_sample; + + Alembic::Abc::OArrayProperty m_mat_indices; + + bool m_is_animated; + ModifierData *m_subsurf_mod; + + CDStreamConfig m_custom_data_config; + + bool m_is_liquid; + bool m_is_subd; + + public: + AbcGenericMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcGenericMeshWriter(); + void setIsAnimated(bool is_animated); + + protected: + virtual void do_write(); + virtual bool isAnimated() const; + virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) = 0; + virtual void freeEvaluatedMesh(struct Mesh *mesh); + + Mesh *getFinalMesh(bool &r_needsfree); + + void writeMesh(struct Mesh *mesh); + void writeSubD(struct Mesh *mesh); + + void writeArbGeoParams(struct Mesh *mesh); + void getGeoGroups(struct Mesh *mesh, std::map> &geoGroups); + + /* fluid surfaces support */ + void getVelocities(struct Mesh *mesh, std::vector &vels); + + template void writeFaceSets(struct Mesh *mesh, Schema &schema); +}; + +class AbcMeshWriter : public AbcGenericMeshWriter { + public: + AbcMeshWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + ~AbcMeshWriter(); + + protected: + virtual Mesh *getEvaluatedMesh(Scene *scene_eval, Object *ob_eval, bool &r_needsfree) override; +}; + +#endif /* __ABC_WRITER_MESH_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.cc b/source/blender/io/alembic/intern/abc_writer_nurbs.cc new file mode 100644 index 00000000000..9796eaf54c3 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_nurbs.cc @@ -0,0 +1,172 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_nurbs.h" +#include "abc_writer_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_curve_types.h" +#include "DNA_object_types.h" + +#include "BLI_listbase.h" + +#include "BKE_curve.h" +} + +using Alembic::AbcGeom::FloatArraySample; +using Alembic::AbcGeom::OBoolProperty; +using Alembic::AbcGeom::OCompoundProperty; +using Alembic::AbcGeom::ONuPatch; +using Alembic::AbcGeom::ONuPatchSchema; + +AbcNurbsWriter::AbcNurbsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_is_animated = isAnimated(); + + /* if the object is static, use the default static time sampling */ + if (!m_is_animated) { + m_time_sampling = 0; + } + + Curve *curve = static_cast(m_object->data); + size_t numNurbs = BLI_listbase_count(&curve->nurb); + + for (size_t i = 0; i < numNurbs; i++) { + std::stringstream str; + str << m_name << '_' << i; + + while (parent->alembicXform().getChildHeader(str.str())) { + str << "_"; + } + + ONuPatch nurbs(parent->alembicXform(), str.str().c_str(), m_time_sampling); + m_nurbs_schema.push_back(nurbs.getSchema()); + } +} + +bool AbcNurbsWriter::isAnimated() const +{ + /* check if object has shape keys */ + Curve *cu = static_cast(m_object->data); + return (cu->key != NULL); +} + +static void get_knots(std::vector &knots, const int num_knots, float *nu_knots) +{ + if (num_knots <= 1) { + return; + } + + /* Add an extra knot at the beginning and end of the array since most apps + * require/expect them. */ + knots.reserve(num_knots + 2); + + knots.push_back(0.0f); + + for (int i = 0; i < num_knots; i++) { + knots.push_back(nu_knots[i]); + } + + knots[0] = 2.0f * knots[1] - knots[2]; + knots.push_back(2.0f * knots[num_knots] - knots[num_knots - 1]); +} + +void AbcNurbsWriter::do_write() +{ + /* we have already stored a sample for this object. */ + if (!m_first_frame && !m_is_animated) { + return; + } + + if (!ELEM(m_object->type, OB_SURF, OB_CURVE)) { + return; + } + + Curve *curve = static_cast(m_object->data); + ListBase *nulb; + + if (m_object->runtime.curve_cache->deformed_nurbs.first != NULL) { + nulb = &m_object->runtime.curve_cache->deformed_nurbs; + } + else { + nulb = BKE_curve_nurbs_get(curve); + } + + size_t count = 0; + for (Nurb *nu = static_cast(nulb->first); nu; nu = nu->next, count++) { + std::vector knotsU; + get_knots(knotsU, KNOTSU(nu), nu->knotsu); + + std::vector knotsV; + get_knots(knotsV, KNOTSV(nu), nu->knotsv); + + const int size = nu->pntsu * nu->pntsv; + std::vector positions(size); + std::vector weights(size); + + const BPoint *bp = nu->bp; + + for (int i = 0; i < size; i++, bp++) { + copy_yup_from_zup(positions[i].getValue(), bp->vec); + weights[i] = bp->vec[3]; + } + + ONuPatchSchema::Sample sample; + sample.setUOrder(nu->orderu + 1); + sample.setVOrder(nu->orderv + 1); + sample.setPositions(positions); + sample.setPositionWeights(weights); + sample.setUKnot(FloatArraySample(knotsU)); + sample.setVKnot(FloatArraySample(knotsV)); + sample.setNu(nu->pntsu); + sample.setNv(nu->pntsv); + + /* TODO(kevin): to accommodate other software we should duplicate control + * points to indicate that a NURBS is cyclic. */ + OCompoundProperty user_props = m_nurbs_schema[count].getUserProperties(); + + if ((nu->flagu & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_ENDPOINT) != 0) { + OBoolProperty prop(user_props, "endpoint_v"); + prop.set(true); + } + + if ((nu->flagu & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_u"); + prop.set(true); + } + + if ((nu->flagv & CU_NURB_CYCLIC) != 0) { + OBoolProperty prop(user_props, "cyclic_v"); + prop.set(true); + } + + m_nurbs_schema[count].set(sample); + } +} diff --git a/source/blender/io/alembic/intern/abc_writer_nurbs.h b/source/blender/io/alembic/intern/abc_writer_nurbs.h new file mode 100644 index 00000000000..c6a3c399b66 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_nurbs.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_NURBS_H__ +#define __ABC_WRITER_NURBS_H__ + +#include "abc_writer_object.h" + +class AbcNurbsWriter : public AbcObjectWriter { + std::vector m_nurbs_schema; + bool m_is_animated; + + public: + AbcNurbsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings); + + private: + virtual void do_write(); + + bool isAnimated() const; +}; + +#endif /* __ABC_WRITER_NURBS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_object.cc b/source/blender/io/alembic/intern/abc_writer_object.cc new file mode 100644 index 00000000000..75dc93bd08e --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_object.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_object.h" + +extern "C" { +#include "DNA_object_types.h" + +#include "BKE_object.h" +} + +AbcObjectWriter::AbcObjectWriter(Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent) + : m_object(ob), m_settings(settings), m_time_sampling(time_sampling), m_first_frame(true) +{ + m_name = get_id_name(m_object) + "Shape"; + + if (parent) { + parent->addChild(this); + } +} + +AbcObjectWriter::~AbcObjectWriter() +{ +} + +void AbcObjectWriter::addChild(AbcObjectWriter *child) +{ + m_children.push_back(child); +} + +Imath::Box3d AbcObjectWriter::bounds() +{ + BoundBox *bb = BKE_object_boundbox_get(this->m_object); + + if (!bb) { + if (this->m_object->type != OB_CAMERA) { + ABC_LOG(m_settings.logger) << "Bounding box is null!\n"; + } + + return Imath::Box3d(); + } + + /* Convert Z-up to Y-up. This also changes which vector goes into which min/max property. */ + this->m_bounds.min.x = bb->vec[0][0]; + this->m_bounds.min.y = bb->vec[0][2]; + this->m_bounds.min.z = -bb->vec[6][1]; + + this->m_bounds.max.x = bb->vec[6][0]; + this->m_bounds.max.y = bb->vec[6][2]; + this->m_bounds.max.z = -bb->vec[0][1]; + + return this->m_bounds; +} + +void AbcObjectWriter::write() +{ + do_write(); + m_first_frame = false; +} diff --git a/source/blender/io/alembic/intern/abc_writer_object.h b/source/blender/io/alembic/intern/abc_writer_object.h new file mode 100644 index 00000000000..c3511566372 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_object.h @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_OBJECT_H__ +#define __ABC_WRITER_OBJECT_H__ + +#include +#include + +#include "abc_exporter.h" + +extern "C" { +#include "DNA_ID.h" +} + +class AbcTransformWriter; + +struct Main; +struct Object; + +class AbcObjectWriter { + protected: + Object *m_object; + ExportSettings &m_settings; + + uint32_t m_time_sampling; + + Imath::Box3d m_bounds; + std::vector m_children; + + std::vector> m_props; + + bool m_first_frame; + std::string m_name; + + public: + AbcObjectWriter(Object *ob, + uint32_t time_sampling, + ExportSettings &settings, + AbcObjectWriter *parent = NULL); + + virtual ~AbcObjectWriter(); + + void addChild(AbcObjectWriter *child); + + virtual Imath::Box3d bounds(); + + void write(); + + private: + virtual void do_write() = 0; +}; + +#endif /* __ABC_WRITER_OBJECT_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_points.cc b/source/blender/io/alembic/intern/abc_writer_points.cc new file mode 100644 index 00000000000..cc4abe8ec4b --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_points.cc @@ -0,0 +1,123 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_points.h" +#include "abc_writer_mesh.h" +#include "abc_writer_transform.h" +#include "abc_util.h" + +extern "C" { +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "BKE_lattice.h" +#include "BKE_particle.h" + +#include "BLI_math.h" + +#include "DEG_depsgraph_query.h" +} + +using Alembic::AbcGeom::kVertexScope; +using Alembic::AbcGeom::OPoints; +using Alembic::AbcGeom::OPointsSchema; + +/* ************************************************************************** */ + +AbcPointsWriter::AbcPointsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys) + : AbcObjectWriter(ob, time_sampling, settings, parent) +{ + m_psys = psys; + + OPoints points(parent->alembicXform(), psys->name, m_time_sampling); + m_schema = points.getSchema(); +} + +void AbcPointsWriter::do_write() +{ + if (!m_psys) { + return; + } + + std::vector points; + std::vector velocities; + std::vector widths; + std::vector ids; + + ParticleKey state; + + ParticleSimulationData sim; + sim.depsgraph = m_settings.depsgraph; + sim.scene = m_settings.scene; + sim.ob = m_object; + sim.psys = m_psys; + + m_psys->lattice_deform_data = psys_create_lattice_deform_data(&sim); + + uint64_t index = 0; + for (int p = 0; p < m_psys->totpart; p++) { + float pos[3], vel[3]; + + if (m_psys->particles[p].flag & (PARS_NO_DISP | PARS_UNEXIST)) { + continue; + } + + state.time = DEG_get_ctime(m_settings.depsgraph); + + if (psys_get_particle_state(&sim, p, &state, 0) == 0) { + continue; + } + + /* location */ + mul_v3_m4v3(pos, m_object->imat, state.co); + + /* velocity */ + sub_v3_v3v3(vel, state.co, m_psys->particles[p].prev_state.co); + + /* Convert Z-up to Y-up. */ + points.push_back(Imath::V3f(pos[0], pos[2], -pos[1])); + velocities.push_back(Imath::V3f(vel[0], vel[2], -vel[1])); + widths.push_back(m_psys->particles[p].size); + ids.push_back(index++); + } + + if (m_psys->lattice_deform_data) { + end_latt_deform(m_psys->lattice_deform_data); + m_psys->lattice_deform_data = NULL; + } + + Alembic::Abc::P3fArraySample psample(points); + Alembic::Abc::UInt64ArraySample idsample(ids); + Alembic::Abc::V3fArraySample vsample(velocities); + Alembic::Abc::FloatArraySample wsample_array(widths); + Alembic::AbcGeom::OFloatGeomParam::Sample wsample(wsample_array, kVertexScope); + + m_sample = OPointsSchema::Sample(psample, idsample, vsample, wsample); + m_sample.setSelfBounds(bounds()); + + m_schema.set(m_sample); +} diff --git a/source/blender/io/alembic/intern/abc_writer_points.h b/source/blender/io/alembic/intern/abc_writer_points.h new file mode 100644 index 00000000000..77dd10c4b26 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_points.h @@ -0,0 +1,49 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2016 Kévin Dietrich. + * All rights reserved. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_POINTS_H__ +#define __ABC_WRITER_POINTS_H__ + +#include "abc_writer_object.h" +#include "abc_customdata.h" + +struct ParticleSystem; + +/* ************************************************************************** */ + +class AbcPointsWriter : public AbcObjectWriter { + Alembic::AbcGeom::OPointsSchema m_schema; + Alembic::AbcGeom::OPointsSchema::Sample m_sample; + ParticleSystem *m_psys; + + public: + AbcPointsWriter(Object *ob, + AbcTransformWriter *parent, + uint32_t time_sampling, + ExportSettings &settings, + ParticleSystem *psys); + + void do_write(); +}; + +#endif /* __ABC_WRITER_POINTS_H__ */ diff --git a/source/blender/io/alembic/intern/abc_writer_transform.cc b/source/blender/io/alembic/intern/abc_writer_transform.cc new file mode 100644 index 00000000000..d7bcc46d96f --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_transform.cc @@ -0,0 +1,121 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "abc_writer_transform.h" +#include "abc_util.h" + +#include + +extern "C" { +#include "DNA_object_types.h" + +#include "BLI_math.h" + +#include "DEG_depsgraph_query.h" +} + +using Alembic::AbcGeom::OObject; +using Alembic::AbcGeom::OXform; + +AbcTransformWriter::AbcTransformWriter(Object *ob, + const OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings) + : AbcObjectWriter(ob, time_sampling, settings, parent), m_proxy_from(NULL) +{ + m_is_animated = hasAnimation(m_object); + + if (!m_is_animated) { + time_sampling = 0; + } + + m_xform = OXform(abc_parent, get_id_name(m_object), time_sampling); + m_schema = m_xform.getSchema(); + + /* Blender objects can't have a parent without inheriting the transform. */ + m_inherits_xform = parent != NULL; +} + +void AbcTransformWriter::do_write() +{ + Object *ob_eval = DEG_get_evaluated_object(m_settings.depsgraph, m_object); + + if (m_first_frame) { + m_visibility = Alembic::AbcGeom::CreateVisibilityProperty( + m_xform, m_xform.getSchema().getTimeSampling()); + } + + m_visibility.set(!(ob_eval->restrictflag & OB_RESTRICT_VIEWPORT)); + + if (!m_first_frame && !m_is_animated) { + return; + } + + float yup_mat[4][4]; + create_transform_matrix( + ob_eval, yup_mat, m_inherits_xform ? ABC_MATRIX_LOCAL : ABC_MATRIX_WORLD, m_proxy_from); + + /* If the parent is a camera, undo its to-Maya rotation (see below). */ + bool is_root_object = !m_inherits_xform || ob_eval->parent == nullptr; + if (!is_root_object && ob_eval->parent->type == OB_CAMERA) { + float rot_mat[4][4]; + axis_angle_to_mat4_single(rot_mat, 'X', M_PI_2); + mul_m4_m4m4(yup_mat, rot_mat, yup_mat); + } + + /* If the object is a camera, apply an extra rotation to Maya camera orientation. */ + if (ob_eval->type == OB_CAMERA) { + float rot_mat[4][4]; + axis_angle_to_mat4_single(rot_mat, 'X', -M_PI_2); + mul_m4_m4m4(yup_mat, yup_mat, rot_mat); + } + + if (is_root_object) { + /* Only apply scaling to root objects, parenting will propagate it. */ + float scale_mat[4][4]; + scale_m4_fl(scale_mat, m_settings.global_scale); + scale_mat[3][3] = m_settings.global_scale; /* also scale translation */ + mul_m4_m4m4(yup_mat, yup_mat, scale_mat); + yup_mat[3][3] /= m_settings.global_scale; /* normalise the homogeneous component */ + } + + m_matrix = convert_matrix_datatype(yup_mat); + m_sample.setMatrix(m_matrix); + m_sample.setInheritsXforms(m_inherits_xform); + m_schema.set(m_sample); +} + +Imath::Box3d AbcTransformWriter::bounds() +{ + Imath::Box3d bounds; + + for (int i = 0; i < m_children.size(); i++) { + Imath::Box3d box(m_children[i]->bounds()); + bounds.extendBy(box); + } + + return Imath::transform(bounds, m_matrix); +} + +bool AbcTransformWriter::hasAnimation(Object * /*ob*/) const +{ + return true; +} diff --git a/source/blender/io/alembic/intern/abc_writer_transform.h b/source/blender/io/alembic/intern/abc_writer_transform.h new file mode 100644 index 00000000000..4397b220761 --- /dev/null +++ b/source/blender/io/alembic/intern/abc_writer_transform.h @@ -0,0 +1,60 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#ifndef __ABC_WRITER_TRANSFORM_H__ +#define __ABC_WRITER_TRANSFORM_H__ + +#include "abc_writer_object.h" + +#include + +class AbcTransformWriter : public AbcObjectWriter { + Alembic::AbcGeom::OXform m_xform; + Alembic::AbcGeom::OXformSchema m_schema; + Alembic::AbcGeom::XformSample m_sample; + Alembic::AbcGeom::OVisibilityProperty m_visibility; + Alembic::Abc::M44d m_matrix; + + bool m_is_animated; + bool m_inherits_xform; + + public: + Object *m_proxy_from; + + public: + AbcTransformWriter(Object *ob, + const Alembic::AbcGeom::OObject &abc_parent, + AbcTransformWriter *parent, + unsigned int time_sampling, + ExportSettings &settings); + + Alembic::AbcGeom::OXform &alembicXform() + { + return m_xform; + } + virtual Imath::Box3d bounds(); + + private: + virtual void do_write(); + + bool hasAnimation(Object *ob) const; +}; + +#endif /* __ABC_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc new file mode 100644 index 00000000000..c6f9e284d53 --- /dev/null +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -0,0 +1,1052 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup balembic + */ + +#include "../ABC_alembic.h" + +#include + +#include "abc_reader_archive.h" +#include "abc_reader_camera.h" +#include "abc_reader_curves.h" +#include "abc_reader_mesh.h" +#include "abc_reader_nurbs.h" +#include "abc_reader_points.h" +#include "abc_reader_transform.h" +#include "abc_util.h" +#include "abc_writer_camera.h" +#include "abc_writer_curves.h" +#include "abc_writer_hair.h" +#include "abc_writer_mesh.h" +#include "abc_writer_nurbs.h" +#include "abc_writer_points.h" +#include "abc_writer_transform.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_cachefile_types.h" +#include "DNA_curve_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_cachefile.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +/* SpaceType struct has a member called 'new' which obviously conflicts with C++ + * so temporarily redefining the new keyword to make it compile. */ +#define new extern_new +#include "BKE_screen.h" +#undef new + +#include "BLI_fileops.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +using Alembic::Abc::Int32ArraySamplePtr; +using Alembic::Abc::ObjectHeader; + +using Alembic::AbcGeom::kWrapExisting; +using Alembic::AbcGeom::MetaData; +using Alembic::AbcGeom::P3fArraySamplePtr; + +using Alembic::AbcGeom::ICamera; +using Alembic::AbcGeom::ICompoundProperty; +using Alembic::AbcGeom::ICurves; +using Alembic::AbcGeom::ICurvesSchema; +using Alembic::AbcGeom::IFaceSet; +using Alembic::AbcGeom::ILight; +using Alembic::AbcGeom::IN3fArrayProperty; +using Alembic::AbcGeom::IN3fGeomParam; +using Alembic::AbcGeom::INuPatch; +using Alembic::AbcGeom::IObject; +using Alembic::AbcGeom::IPoints; +using Alembic::AbcGeom::IPointsSchema; +using Alembic::AbcGeom::IPolyMesh; +using Alembic::AbcGeom::IPolyMeshSchema; +using Alembic::AbcGeom::ISampleSelector; +using Alembic::AbcGeom::ISubD; +using Alembic::AbcGeom::IV2fGeomParam; +using Alembic::AbcGeom::IXform; +using Alembic::AbcGeom::IXformSchema; +using Alembic::AbcGeom::N3fArraySamplePtr; +using Alembic::AbcGeom::V3fArraySamplePtr; +using Alembic::AbcGeom::XformSample; + +using Alembic::AbcMaterial::IMaterial; + +struct AbcArchiveHandle { + int unused; +}; + +ABC_INLINE ArchiveReader *archive_from_handle(AbcArchiveHandle *handle) +{ + return reinterpret_cast(handle); +} + +ABC_INLINE AbcArchiveHandle *handle_from_archive(ArchiveReader *archive) +{ + return reinterpret_cast(archive); +} + +//#define USE_NURBS + +/* NOTE: this function is similar to visit_objects below, need to keep them in + * sync. */ +static bool gather_objects_paths(const IObject &object, ListBase *object_paths) +{ + if (!object.valid()) { + return false; + } + + size_t children_claiming_this_object = 0; + size_t num_children = object.getNumChildren(); + + for (size_t i = 0; i < num_children; i++) { + bool child_claims_this_object = gather_objects_paths(object.getChild(i), object_paths); + children_claiming_this_object += child_claims_this_object ? 1 : 0; + } + + const MetaData &md = object.getMetaData(); + bool get_path = false; + bool parent_is_part_of_this_object = false; + + if (!object.getParent()) { + /* The root itself is not an object we should import. */ + } + else if (IXform::matches(md)) { + if (has_property(object.getProperties(), "locator")) { + get_path = true; + } + else { + get_path = children_claiming_this_object == 0; + } + + /* Transforms are never "data" for their parent. */ + parent_is_part_of_this_object = false; + } + else { + /* These types are "data" for their parent. */ + get_path = IPolyMesh::matches(md) || ISubD::matches(md) || +#ifdef USE_NURBS + INuPatch::matches(md) || +#endif + ICamera::matches(md) || IPoints::matches(md) || ICurves::matches(md); + parent_is_part_of_this_object = get_path; + } + + if (get_path) { + void *abc_path_void = MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath"); + AlembicObjectPath *abc_path = static_cast(abc_path_void); + + BLI_strncpy(abc_path->path, object.getFullName().c_str(), sizeof(abc_path->path)); + BLI_addtail(object_paths, abc_path); + } + + return parent_is_part_of_this_object; +} + +AbcArchiveHandle *ABC_create_handle(struct Main *bmain, + const char *filename, + ListBase *object_paths) +{ + ArchiveReader *archive = new ArchiveReader(bmain, filename); + + if (!archive->valid()) { + delete archive; + return NULL; + } + + if (object_paths) { + gather_objects_paths(archive->getTop(), object_paths); + } + + return handle_from_archive(archive); +} + +void ABC_free_handle(AbcArchiveHandle *handle) +{ + delete archive_from_handle(handle); +} + +int ABC_get_version() +{ + return ALEMBIC_LIBRARY_VERSION; +} + +static void find_iobject(const IObject &object, IObject &ret, const std::string &path) +{ + if (!object.valid()) { + return; + } + + std::vector tokens; + split(path, '/', tokens); + + IObject tmp = object; + + std::vector::iterator iter; + for (iter = tokens.begin(); iter != tokens.end(); ++iter) { + IObject child = tmp.getChild(*iter); + tmp = child; + } + + ret = tmp; +} + +struct ExportJobData { + ViewLayer *view_layer; + Main *bmain; + wmWindowManager *wm; + + char filename[1024]; + ExportSettings settings; + + short *stop; + short *do_update; + float *progress; + + bool was_canceled; + bool export_ok; +}; + +static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ExportJobData *data = static_cast(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + /* XXX annoying hack: needed to prevent data corruption when changing + * scene frame in separate threads + */ + G.is_rendering = true; + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + DEG_graph_build_from_view_layer( + data->settings.depsgraph, data->bmain, data->settings.scene, data->view_layer); + BKE_scene_graph_update_tagged(data->settings.depsgraph, data->bmain); + + try { + AbcExporter exporter(data->bmain, data->filename, data->settings); + + Scene *scene = data->settings.scene; /* for the CFRA macro */ + const int orig_frame = CFRA; + + data->was_canceled = false; + exporter(do_update, progress, &data->was_canceled); + + if (CFRA != orig_frame) { + CFRA = orig_frame; + + BKE_scene_graph_update_for_newframe(data->settings.depsgraph, data->bmain); + } + + data->export_ok = !data->was_canceled; + } + catch (const std::exception &e) { + ABC_LOG(data->settings.logger) << "Abc Export error: " << e.what() << '\n'; + } + catch (...) { + ABC_LOG(data->settings.logger) << "Abc Export: unknown error...\n"; + } +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast(customdata); + + DEG_graph_free(data->settings.depsgraph); + + if (data->was_canceled && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + std::string log = data->settings.logger.str(); + if (!log.empty()) { + std::cerr << log; + WM_report(RPT_ERROR, "Errors occurred during the export, look in the console to know more..."); + } + + G.is_rendering = false; + WM_set_locked_interface(data->wm, false); +} + +bool ABC_export(Scene *scene, + bContext *C, + const char *filepath, + const struct AlembicExportParams *params, + bool as_background_job) +{ + ExportJobData *job = static_cast( + MEM_mallocN(sizeof(ExportJobData), "ExportJobData")); + + job->view_layer = CTX_data_view_layer(C); + job->bmain = CTX_data_main(C); + job->wm = CTX_wm_manager(C); + job->export_ok = false; + BLI_strncpy(job->filename, filepath, 1024); + + /* Alright, alright, alright.... + * + * ExportJobData contains an ExportSettings containing a SimpleLogger. + * + * Since ExportJobData is a C-style struct dynamically allocated with + * MEM_mallocN (see above), its constructor is never called, therefore the + * ExportSettings constructor is not called which implies that the + * SimpleLogger one is not called either. SimpleLogger in turn does not call + * the constructor of its data members which ultimately means that its + * std::ostringstream member has a NULL pointer. To be able to properly use + * the stream's operator<<, the pointer needs to be set, therefore we have + * to properly construct everything. And this is done using the placement + * new operator as here below. It seems hackish, but I'm too lazy to + * do bigger refactor and maybe there is a better way which does not involve + * hardcore refactoring. */ + new (&job->settings) ExportSettings(); + job->settings.scene = scene; + job->settings.depsgraph = DEG_graph_new(job->bmain, scene, job->view_layer, DAG_EVAL_RENDER); + + /* TODO(Sybren): for now we only export the active scene layer. + * Later in the 2.8 development process this may be replaced by using + * a specific collection for Alembic I/O, which can then be toggled + * between "real" objects and cached Alembic files. */ + job->settings.view_layer = job->view_layer; + + job->settings.frame_start = params->frame_start; + job->settings.frame_end = params->frame_end; + job->settings.frame_samples_xform = params->frame_samples_xform; + job->settings.frame_samples_shape = params->frame_samples_shape; + job->settings.shutter_open = params->shutter_open; + job->settings.shutter_close = params->shutter_close; + + /* TODO(Sybren): For now this is ignored, until we can get selection + * detection working through Base pointers (instead of ob->flags). */ + job->settings.selected_only = params->selected_only; + + job->settings.export_face_sets = params->face_sets; + job->settings.export_normals = params->normals; + job->settings.export_uvs = params->uvs; + job->settings.export_vcols = params->vcolors; + job->settings.export_hair = params->export_hair; + job->settings.export_particles = params->export_particles; + job->settings.apply_subdiv = params->apply_subdiv; + job->settings.curves_as_mesh = params->curves_as_mesh; + job->settings.flatten_hierarchy = params->flatten_hierarchy; + + /* TODO(Sybren): visible_layer & renderable only is ignored for now, + * to be replaced with collections later in the 2.8 dev process + * (also see note above). */ + job->settings.visible_objects_only = params->visible_objects_only; + job->settings.renderable_only = params->renderable_only; + + job->settings.use_subdiv_schema = params->use_subdiv_schema; + job->settings.export_ogawa = (params->compression_type == ABC_ARCHIVE_OGAWA); + job->settings.pack_uv = params->packuv; + job->settings.global_scale = params->global_scale; + job->settings.triangulate = params->triangulate; + job->settings.quad_method = params->quad_method; + job->settings.ngon_method = params->ngon_method; + + if (job->settings.frame_start > job->settings.frame_end) { + std::swap(job->settings.frame_start, job->settings.frame_end); + } + + bool export_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->settings.scene, + "Alembic Export", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, export_startjob, NULL, NULL, export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + export_startjob(job, &stop, &do_update, &progress); + export_endjob(job); + export_ok = job->export_ok; + + MEM_freeN(job); + } + + return export_ok; +} + +/* ********************** Import file ********************** */ + +/** + * Generates an AbcObjectReader for this Alembic object and its children. + * + * \param object: The Alembic IObject to visit. + * \param readers: The created AbcObjectReader * will be appended to this vector. + * \param settings: Import settings, not used directly but passed to the + * AbcObjectReader subclass constructors. + * \param r_assign_as_parent: Return parameter, contains a list of reader + * pointers, whose parent pointer should still be set. + * This is filled when this call to visit_object() didn't create + * a reader that should be the parent. + * \return A pair of boolean and reader pointer. The boolean indicates whether + * this IObject claims its parent as part of the same object + * (for example an IPolyMesh object would claim its parent, as the mesh + * is interpreted as the object's data, and the parent IXform as its + * Blender object). The pointer is the AbcObjectReader that represents + * the IObject parameter. + * + * NOTE: this function is similar to gather_object_paths above, need to keep + * them in sync. */ +static std::pair visit_object( + const IObject &object, + AbcObjectReader::ptr_vector &readers, + ImportSettings &settings, + AbcObjectReader::ptr_vector &r_assign_as_parent) +{ + const std::string &full_name = object.getFullName(); + + if (!object.valid()) { + std::cerr << " - " << full_name << ": object is invalid, skipping it and all its children.\n"; + return std::make_pair(false, static_cast(NULL)); + } + + /* The interpretation of data by the children determine the role of this + * object. This is especially important for Xform objects, as they can be + * either part of a Blender object or a Blender object (Empty) themselves. + */ + size_t children_claiming_this_object = 0; + size_t num_children = object.getNumChildren(); + AbcObjectReader::ptr_vector claiming_child_readers; + AbcObjectReader::ptr_vector nonclaiming_child_readers; + AbcObjectReader::ptr_vector assign_as_parent; + for (size_t i = 0; i < num_children; i++) { + const IObject ichild = object.getChild(i); + + /* TODO: When we only support C++11, use std::tie() instead. */ + std::pair child_result; + child_result = visit_object(ichild, readers, settings, assign_as_parent); + + bool child_claims_this_object = child_result.first; + AbcObjectReader *child_reader = child_result.second; + + if (child_reader == NULL) { + BLI_assert(!child_claims_this_object); + } + else { + if (child_claims_this_object) { + claiming_child_readers.push_back(child_reader); + } + else { + nonclaiming_child_readers.push_back(child_reader); + } + } + + children_claiming_this_object += child_claims_this_object ? 1 : 0; + } + BLI_assert(children_claiming_this_object == claiming_child_readers.size()); + + AbcObjectReader *reader = NULL; + const MetaData &md = object.getMetaData(); + bool parent_is_part_of_this_object = false; + + if (!object.getParent()) { + /* The root itself is not an object we should import. */ + } + else if (IXform::matches(md)) { + bool create_empty; + + /* An xform can either be a Blender Object (if it contains a mesh, for + * example), but it can also be an Empty. Its correct translation to + * Blender's data model depends on its children. */ + + /* Check whether or not this object is a Maya locator, which is + * similar to empties used as parent object in Blender. */ + if (has_property(object.getProperties(), "locator")) { + create_empty = true; + } + else { + create_empty = claiming_child_readers.empty(); + } + + if (create_empty) { + reader = new AbcEmptyReader(object, settings); + } + } + else if (IPolyMesh::matches(md)) { + reader = new AbcMeshReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (ISubD::matches(md)) { + reader = new AbcSubDReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (INuPatch::matches(md)) { +#ifdef USE_NURBS + /* TODO(kevin): importing cyclic NURBS from other software crashes + * at the moment. This is due to the fact that NURBS in other + * software have duplicated points which causes buffer overflows in + * Blender. Need to figure out exactly how these points are + * duplicated, in all cases (cyclic U, cyclic V, and cyclic UV). + * Until this is fixed, disabling NURBS reading. */ + reader = new AbcNurbsReader(object, settings); + parent_is_part_of_this_object = true; +#endif + } + else if (ICamera::matches(md)) { + reader = new AbcCameraReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (IPoints::matches(md)) { + reader = new AbcPointsReader(object, settings); + parent_is_part_of_this_object = true; + } + else if (IMaterial::matches(md)) { + /* Pass for now. */ + } + else if (ILight::matches(md)) { + /* Pass for now. */ + } + else if (IFaceSet::matches(md)) { + /* Pass, those are handled in the mesh reader. */ + } + else if (ICurves::matches(md)) { + reader = new AbcCurveReader(object, settings); + parent_is_part_of_this_object = true; + } + else { + std::cerr << "Alembic object " << full_name << " is of unsupported schema type '" + << object.getMetaData().get("schemaObjTitle") << "'" << std::endl; + } + + if (reader) { + /* We have created a reader, which should imply that this object is + * not claimed as part of any child Alembic object. */ + BLI_assert(claiming_child_readers.empty()); + + readers.push_back(reader); + reader->incref(); + + AlembicObjectPath *abc_path = static_cast( + MEM_callocN(sizeof(AlembicObjectPath), "AlembicObjectPath")); + BLI_strncpy(abc_path->path, full_name.c_str(), sizeof(abc_path->path)); + BLI_addtail(&settings.cache_file->object_paths, abc_path); + + /* We can now assign this reader as parent for our children. */ + if (nonclaiming_child_readers.size() + assign_as_parent.size() > 0) { + for (AbcObjectReader *child_reader : nonclaiming_child_readers) { + child_reader->parent_reader = reader; + } + for (AbcObjectReader *child_reader : assign_as_parent) { + child_reader->parent_reader = reader; + } + } + } + else if (object.getParent()) { + if (claiming_child_readers.size() > 0) { + /* The first claiming child will serve just fine as parent to + * our non-claiming children. Since all claiming children share + * the same XForm, it doesn't really matter which one we pick. */ + AbcObjectReader *claiming_child = claiming_child_readers[0]; + for (AbcObjectReader *child_reader : nonclaiming_child_readers) { + child_reader->parent_reader = claiming_child; + } + for (AbcObjectReader *child_reader : assign_as_parent) { + child_reader->parent_reader = claiming_child; + } + /* Claiming children should have our parent set as their parent. */ + for (AbcObjectReader *child_reader : claiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } + } + else { + /* This object isn't claimed by any child, and didn't produce + * a reader. Odd situation, could be the top Alembic object, or + * an unsupported Alembic schema. Delegate to our parent. */ + for (AbcObjectReader *child_reader : claiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } + for (AbcObjectReader *child_reader : nonclaiming_child_readers) { + r_assign_as_parent.push_back(child_reader); + } + for (AbcObjectReader *child_reader : assign_as_parent) { + r_assign_as_parent.push_back(child_reader); + } + } + } + + return std::make_pair(parent_is_part_of_this_object, reader); +} + +enum { + ABC_NO_ERROR = 0, + ABC_ARCHIVE_FAIL, + ABC_UNSUPPORTED_HDF5, +}; + +struct ImportJobData { + Main *bmain; + Scene *scene; + ViewLayer *view_layer; + wmWindowManager *wm; + + char filename[1024]; + ImportSettings settings; + + ArchiveReader *archive; + std::vector readers; + + short *stop; + short *do_update; + float *progress; + + char error_code; + bool was_cancelled; + bool import_ok; +}; + +static void import_startjob(void *user_data, short *stop, short *do_update, float *progress) +{ + SCOPE_TIMER("Alembic import, objects reading and creation"); + + ImportJobData *data = static_cast(user_data); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + + WM_set_locked_interface(data->wm, true); + + ArchiveReader *archive = new ArchiveReader(data->bmain, data->filename); + + if (!archive->valid()) { +#ifndef WITH_ALEMBIC_HDF5 + data->error_code = archive->is_hdf5() ? ABC_UNSUPPORTED_HDF5 : ABC_ARCHIVE_FAIL; +#else + data->error_code = ABC_ARCHIVE_FAIL; +#endif + delete archive; + return; + } + + CacheFile *cache_file = static_cast( + BKE_cachefile_add(data->bmain, BLI_path_basename(data->filename))); + + /* Decrement the ID ref-count because it is going to be incremented for each + * modifier and constraint that it will be attached to, so since currently + * it is not used by anyone, its use count will off by one. */ + id_us_min(&cache_file->id); + + cache_file->is_sequence = data->settings.is_sequence; + cache_file->scale = data->settings.scale; + STRNCPY(cache_file->filepath, data->filename); + + data->archive = archive; + data->settings.cache_file = cache_file; + + *data->do_update = true; + *data->progress = 0.05f; + + /* Parse Alembic Archive. */ + AbcObjectReader::ptr_vector assign_as_parent; + visit_object(archive->getTop(), data->readers, data->settings, assign_as_parent); + + /* There shouldn't be any orphans. */ + BLI_assert(assign_as_parent.size() == 0); + + if (G.is_break) { + data->was_cancelled = true; + return; + } + + *data->do_update = true; + *data->progress = 0.1f; + + /* Create objects and set scene frame range. */ + + const float size = static_cast(data->readers.size()); + size_t i = 0; + + chrono_t min_time = std::numeric_limits::max(); + chrono_t max_time = std::numeric_limits::min(); + + ISampleSelector sample_sel(0.0f); + std::vector::iterator iter; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + + if (reader->valid()) { + reader->readObjectData(data->bmain, sample_sel); + + min_time = std::min(min_time, reader->minTime()); + max_time = std::max(max_time, reader->maxTime()); + } + else { + std::cerr << "Object " << reader->name() << " in Alembic file " << data->filename + << " is invalid.\n"; + } + + *data->progress = 0.1f + 0.3f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } + + if (data->settings.set_frame_range) { + Scene *scene = data->scene; + + if (data->settings.is_sequence) { + SFRA = data->settings.sequence_offset; + EFRA = SFRA + (data->settings.sequence_len - 1); + CFRA = SFRA; + } + else if (min_time < max_time) { + SFRA = static_cast(round(min_time * FPS)); + EFRA = static_cast(round(max_time * FPS)); + CFRA = SFRA; + } + } + + /* Setup parenthood. */ + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + const AbcObjectReader *reader = *iter; + const AbcObjectReader *parent_reader = reader->parent_reader; + Object *ob = reader->object(); + + if (parent_reader == NULL || !reader->inherits_xform()) { + ob->parent = NULL; + } + else { + ob->parent = parent_reader->object(); + } + } + + /* Setup transformations and constraints. */ + i = 0; + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + reader->setupObjectTransform(0.0f); + + *data->progress = 0.7f + 0.3f * (++i / size); + *data->do_update = true; + + if (G.is_break) { + data->was_cancelled = true; + return; + } + } +} + +static void import_endjob(void *user_data) +{ + SCOPE_TIMER("Alembic import, cleanup"); + + ImportJobData *data = static_cast(user_data); + + std::vector::iterator iter; + + /* Delete objects on cancellation. */ + if (data->was_cancelled) { + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + + /* It's possible that cancellation occurred between the creation of + * the reader and the creation of the Blender object. */ + if (ob == NULL) { + continue; + } + + BKE_id_free_us(data->bmain, ob); + } + } + else { + /* Add object to scene. */ + Base *base; + LayerCollection *lc; + ViewLayer *view_layer = data->view_layer; + + BKE_view_layer_base_deselect_all(view_layer); + + lc = BKE_layer_collection_get_active(view_layer); + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + Object *ob = (*iter)->object(); + + BKE_collection_object_add(data->bmain, lc->collection, ob); + + base = BKE_view_layer_base_find(view_layer, ob); + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + + DEG_id_tag_update(&lc->collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update_ex(data->bmain, + &ob->id, + ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION | + ID_RECALC_BASE_FLAGS); + } + + DEG_id_tag_update(&data->scene->id, ID_RECALC_BASE_FLAGS); + DEG_relations_tag_update(data->bmain); + } + + for (iter = data->readers.begin(); iter != data->readers.end(); ++iter) { + AbcObjectReader *reader = *iter; + reader->decref(); + + if (reader->refcount() == 0) { + delete reader; + } + } + + WM_set_locked_interface(data->wm, false); + + switch (data->error_code) { + default: + case ABC_NO_ERROR: + data->import_ok = !data->was_cancelled; + break; + case ABC_ARCHIVE_FAIL: + WM_report(RPT_ERROR, "Could not open Alembic archive for reading! See console for detail."); + break; + case ABC_UNSUPPORTED_HDF5: + WM_report(RPT_ERROR, "Alembic archive in obsolete HDF5 format is not supported."); + break; + } + + WM_main_add_notifier(NC_SCENE | ND_FRAME, data->scene); +} + +static void import_freejob(void *user_data) +{ + ImportJobData *data = static_cast(user_data); + delete data->archive; + delete data; +} + +bool ABC_import(bContext *C, + const char *filepath, + float scale, + bool is_sequence, + bool set_frame_range, + int sequence_len, + int offset, + bool validate_meshes, + bool as_background_job) +{ + /* Using new here since MEM_* funcs do not call ctor to properly initialize + * data. */ + ImportJobData *job = new ImportJobData(); + job->bmain = CTX_data_main(C); + job->scene = CTX_data_scene(C); + job->view_layer = CTX_data_view_layer(C); + job->wm = CTX_wm_manager(C); + job->import_ok = false; + BLI_strncpy(job->filename, filepath, 1024); + + job->settings.scale = scale; + job->settings.is_sequence = is_sequence; + job->settings.set_frame_range = set_frame_range; + job->settings.sequence_len = sequence_len; + job->settings.sequence_offset = offset; + job->settings.validate_meshes = validate_meshes; + job->error_code = ABC_NO_ERROR; + job->was_cancelled = false; + job->archive = NULL; + + G.is_break = false; + + bool import_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + job->scene, + "Alembic Import", + WM_JOB_PROGRESS, + WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, import_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, import_startjob, NULL, NULL, import_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while importing. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + import_startjob(job, &stop, &do_update, &progress); + import_endjob(job); + import_ok = job->import_ok; + + import_freejob(job); + } + + return import_ok; +} + +/* ************************************************************************** */ + +void ABC_get_transform(CacheReader *reader, float r_mat[4][4], float time, float scale) +{ + if (!reader) { + return; + } + + AbcObjectReader *abc_reader = reinterpret_cast(reader); + + bool is_constant = false; + abc_reader->read_matrix(r_mat, time, scale, is_constant); +} + +/* ************************************************************************** */ + +static AbcObjectReader *get_abc_reader(CacheReader *reader, Object *ob, const char **err_str) +{ + AbcObjectReader *abc_reader = reinterpret_cast(reader); + IObject iobject = abc_reader->iobject(); + + if (!iobject.valid()) { + *err_str = "Invalid object: verify object path"; + return NULL; + } + + const ObjectHeader &header = iobject.getHeader(); + if (!abc_reader->accepts_object_type(header, ob, err_str)) { + /* err_str is set by acceptsObjectType() */ + return NULL; + } + + return abc_reader; +} + +static ISampleSelector sample_selector_for_time(float time) +{ + /* kFloorIndex is used to be compatible with non-interpolating + * properties; they use the floor. */ + return ISampleSelector(time, ISampleSelector::kFloorIndex); +} + +Mesh *ABC_read_mesh(CacheReader *reader, + Object *ob, + Mesh *existing_mesh, + const float time, + const char **err_str, + int read_flag) +{ + AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); + if (abc_reader == NULL) { + return NULL; + } + + ISampleSelector sample_sel = sample_selector_for_time(time); + return abc_reader->read_mesh(existing_mesh, sample_sel, read_flag, err_str); +} + +bool ABC_mesh_topology_changed( + CacheReader *reader, Object *ob, Mesh *existing_mesh, const float time, const char **err_str) +{ + AbcObjectReader *abc_reader = get_abc_reader(reader, ob, err_str); + if (abc_reader == NULL) { + return false; + } + + ISampleSelector sample_sel = sample_selector_for_time(time); + return abc_reader->topology_changed(existing_mesh, sample_sel); +} + +/* ************************************************************************** */ + +void CacheReader_free(CacheReader *reader) +{ + AbcObjectReader *abc_reader = reinterpret_cast(reader); + abc_reader->decref(); + + if (abc_reader->refcount() == 0) { + delete abc_reader; + } +} + +void CacheReader_incref(CacheReader *reader) +{ + AbcObjectReader *abc_reader = reinterpret_cast(reader); + abc_reader->incref(); +} + +CacheReader *CacheReader_open_alembic_object(AbcArchiveHandle *handle, + CacheReader *reader, + Object *object, + const char *object_path) +{ + if (object_path[0] == '\0') { + return reader; + } + + ArchiveReader *archive = archive_from_handle(handle); + + if (!archive || !archive->valid()) { + return reader; + } + + IObject iobject; + find_iobject(archive->getTop(), iobject, object_path); + + if (reader) { + CacheReader_free(reader); + } + + ImportSettings settings; + AbcObjectReader *abc_reader = create_reader(iobject, settings); + if (abc_reader == NULL) { + /* This object is not supported */ + return NULL; + } + abc_reader->object(object); + abc_reader->incref(); + + return reinterpret_cast(abc_reader); +} diff --git a/source/blender/io/avi/AVI_avi.h b/source/blender/io/avi/AVI_avi.h new file mode 100644 index 00000000000..4f3aa720da3 --- /dev/null +++ b/source/blender/io/avi/AVI_avi.h @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * \section avi_about About the AVI module + * + * This is external code. It provides avi file import/export and + * conversions. It has been adapted to make use of Blender memory + * management functions, and because of this it needs module + * blenlib. You need to provide this lib when linking with libavi.a . + * + * \subsection avi_issues Known issues with AVI + * + * - avi uses #MEM_mallocN, #MEM_freeN from blenlib. + * - Not all functions that are used externally are properly + * prototyped. + * + * This header has not been split, since it interleaves type defines + * and functions. You would need the types to be able to include the + * function headers anyway. And, after all, it is someone else's + * code. So we keep it like this. + */ + +#ifndef __AVI_AVI_H__ +#define __AVI_AVI_H__ + +#include "BLI_sys_types.h" +#include /* for FILE */ + +typedef struct _AviChunk { + int fcc; + int size; +} AviChunk; + +typedef struct _AviList { + int fcc; + int size; + int ids; +} AviList; + +typedef struct _AviMainHeader { + int fcc; + int size; + int MicroSecPerFrame; /* MicroSecPerFrame - timing between frames */ + int MaxBytesPerSec; /* MaxBytesPerSec - approx bps system must handle */ + int PaddingGranularity; + int Flags; + + /** had idx1 chunk */ +#define AVIF_HASINDEX 0x00000010 + /** must use idx1 chunk to determine order */ +#define AVIF_MUSTUSEINDEX 0x00000020 + /** AVI file is interleaved */ +#define AVIF_ISINTERLEAVED 0x00000100 +#define AVIF_TRUSTCKTYPE 0x00000800 + /** specially allocated used for capturing real time video */ +#define AVIF_WASCAPTUREFILE 0x00010000 + /** contains copyrighted data */ +#define AVIF_COPYRIGHTED 0x00020000 + + int TotalFrames; + int InitialFrames; /* InitialFrames - initial frame before interleaving */ + int Streams; + int SuggestedBufferSize; + int Width; + int Height; + int Reserved[4]; +} AviMainHeader; + +typedef struct _AviStreamHeader { + int fcc; + int size; + int Type; +#define AVIST_VIDEO FCC("vids") +#define AVIST_AUDIO FCC("auds") +#define AVIST_MIDI FCC("mids") +#define AVIST_TEXT FCC("txts") + + int Handler; + int Flags; +#define AVISF_DISABLED 0x00000001 +#define AVISF_VIDEO_PALCHANGES 0x00010000 + + short Priority; + short Language; + int InitialFrames; + int Scale; + int Rate; + int Start; + int Length; + int SuggestedBufferSize; + int Quality; + int SampleSize; + short left; + short top; + short right; + short bottom; +} AviStreamHeader; + +typedef struct _AviBitmapInfoHeader { + int fcc; + int size; + int Size; + int Width; + int Height; + short Planes; + short BitCount; + int Compression; + int SizeImage; + int XPelsPerMeter; + int YPelsPerMeter; + int ClrUsed; + int ClrImportant; +} AviBitmapInfoHeader; + +typedef struct _AviMJPEGUnknown { + int a; + int b; + int c; + int d; + int e; + int f; + int g; +} AviMJPEGUnknown; + +typedef struct _AviIndexEntry { + int ChunkId; + int Flags; +#define AVIIF_LIST 0x00000001 +#define AVIIF_KEYFRAME 0x00000010 +#define AVIIF_NO_TIME 0x00000100 +#define AVIIF_COMPRESSOR 0x0FFF0000 + int Offset; + int Size; +} AviIndexEntry; + +typedef struct _AviIndex { + int fcc; + int size; + AviIndexEntry *entrys; +} AviIndex; + +typedef enum { + /** The most basic of forms, 3 bytes per pixel, 1 per r, g, b. */ + AVI_FORMAT_RGB24, + /** The second most basic of forms, 4 bytes per pixel, 1 per r, g, b, alpha. */ + AVI_FORMAT_RGB32, + /** Same as above, but is in the weird AVI order (bottom to top, left to right). */ + AVI_FORMAT_AVI_RGB, + /** Motion-JPEG. */ + AVI_FORMAT_MJPEG, +} AviFormat; + +typedef struct _AviStreamRec { + AviStreamHeader sh; + void *sf; + int sf_size; + AviFormat format; +} AviStreamRec; + +typedef struct _AviMovie { + FILE *fp; + + int type; +#define AVI_MOVIE_READ 0 +#define AVI_MOVIE_WRITE 1 + + int64_t size; + + AviMainHeader *header; + AviStreamRec *streams; + AviIndexEntry *entries; + int index_entries; + + int64_t movi_offset; + int64_t read_offset; + int64_t *offset_table; + + /* Local data goes here */ + int interlace; + int odd_fields; +} AviMovie; + +typedef enum { + AVI_ERROR_NONE = 0, + AVI_ERROR_COMPRESSION, + AVI_ERROR_OPEN, + AVI_ERROR_READING, + AVI_ERROR_WRITING, + AVI_ERROR_FORMAT, + AVI_ERROR_ALLOC, + AVI_ERROR_FOUND, + AVI_ERROR_OPTION, +} AviError; + +/* belongs to the option-setting function. */ +typedef enum { + AVI_OPTION_WIDTH = 0, + AVI_OPTION_HEIGHT, + AVI_OPTION_QUALITY, + AVI_OPTION_FRAMERATE, +} AviOption; + +/* The offsets that will always stay the same in AVI files we + * write... used to seek around to the places where we need to write + * the sizes */ + +#define AVI_RIFF_SOFF 4L +#define AVI_HDRL_SOFF 16L + +/** + * This is a sort of MAKE_ID thing. Used in imbuf :( It is used + * through options in the AVI header (AviStreamHeader). */ +#define FCC(ch4) (ch4[0] | ch4[1] << 8 | ch4[2] << 16 | ch4[3] << 24) + +/** + * Test whether this is an avi-format. + */ +bool AVI_is_avi(const char *name); + +/** + * Open a compressed file, decompress it into memory. + */ +AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...); + +/** + * Finalize a compressed output stream. + */ +AviError AVI_close_compress(AviMovie *movie); + +/** + * Choose a compression option for \. Possible options are + * AVI_OPTION_TYPE_MAIN, AVI_OPTION_TYPE_STRH, AVI_OPTION_TYPE_STRF + */ +AviError AVI_set_compress_option( + AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data); +/* Hmmm... there should be some explanation about what these mean */ +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_MAIN 0 +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_STRH 1 +/** + * Compression option, for use in avi_set_compress_option + */ +#define AVI_OPTION_TYPE_STRF 2 + +/** + * Direct the streams \ to \. Redirect \ + * streams. + */ +int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num); + +/** + * Open a movie stream from file. + */ +AviError AVI_open_movie(const char *name, AviMovie *movie); + +/** + * Read a frame from a movie stream. + */ +void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream); +/** + * Close an open movie stream. + */ +AviError AVI_close(AviMovie *movie); + +/** + * Write frames to a movie stream. + */ +AviError AVI_write_frame(AviMovie *movie, int frame_num, ...); + +/** + * Unused but still external + */ +AviError AVI_print_error(AviError error); + +#endif /* __AVI_AVI_H__ */ diff --git a/source/blender/io/avi/CMakeLists.txt b/source/blender/io/avi/CMakeLists.txt new file mode 100644 index 00000000000..76c90353673 --- /dev/null +++ b/source/blender/io/avi/CMakeLists.txt @@ -0,0 +1,53 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + ../../blenlib + ../../imbuf + ../../../../intern/guardedalloc +) + +set(INC_SYS + ${JPEG_INCLUDE_DIR} +) + +set(SRC + intern/avi.c + intern/avi_codecs.c + intern/avi_endian.c + intern/avi_mjpeg.c + intern/avi_options.c + intern/avi_rgb.c + intern/avi_rgb32.c + + AVI_avi.h + intern/avi_endian.h + intern/avi_intern.h + intern/avi_mjpeg.h + intern/avi_rgb.h + intern/avi_rgb32.h +) + +set(LIB + ${JPEG_LIBRARIES} +) + +blender_add_lib(bf_avi "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/avi/intern/avi.c b/source/blender/io/avi/intern/avi.c new file mode 100644 index 00000000000..22eb0be0cc0 --- /dev/null +++ b/source/blender/io/avi/intern/avi.c @@ -0,0 +1,1056 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. + */ + +#include +#include +#include +#include +#include + +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" +#include "BLI_utildefines.h" +#include "BLI_fileops.h" + +#include "AVI_avi.h" +#include "avi_intern.h" + +#include "avi_endian.h" + +static int AVI_DEBUG = 0; +static char DEBUG_FCC[4]; + +#define DEBUG_PRINT(x) \ + if (AVI_DEBUG) { \ + printf("AVI DEBUG: " x); \ + } \ + (void)0 + +/* local functions */ +char *fcc_to_char(unsigned int fcc); +char *tcc_to_char(unsigned int tcc); + +/* implementation */ + +unsigned int GET_FCC(FILE *fp) +{ + unsigned char tmp[4]; + + tmp[0] = getc(fp); + tmp[1] = getc(fp); + tmp[2] = getc(fp); + tmp[3] = getc(fp); + + return FCC(tmp); +} + +unsigned int GET_TCC(FILE *fp) +{ + char tmp[5]; + + tmp[0] = getc(fp); + tmp[1] = getc(fp); + tmp[2] = 0; + tmp[3] = 0; + + return FCC(tmp); +} + +char *fcc_to_char(unsigned int fcc) +{ + DEBUG_FCC[0] = (fcc)&127; + DEBUG_FCC[1] = (fcc >> 8) & 127; + DEBUG_FCC[2] = (fcc >> 16) & 127; + DEBUG_FCC[3] = (fcc >> 24) & 127; + + return DEBUG_FCC; +} + +char *tcc_to_char(unsigned int tcc) +{ + DEBUG_FCC[0] = (tcc)&127; + DEBUG_FCC[1] = (tcc >> 8) & 127; + DEBUG_FCC[2] = 0; + DEBUG_FCC[3] = 0; + + return DEBUG_FCC; +} + +int AVI_get_stream(AviMovie *movie, int avist_type, int stream_num) +{ + int cur_stream; + + if (movie == NULL) { + return -AVI_ERROR_OPTION; + } + + for (cur_stream = 0; cur_stream < movie->header->Streams; cur_stream++) { + if (movie->streams[cur_stream].sh.Type == avist_type) { + if (stream_num == 0) { + return cur_stream; + } + else { + stream_num--; + } + } + } + + return -AVI_ERROR_FOUND; +} + +static int fcc_get_stream(int fcc) +{ + char fccs[4]; + + fccs[0] = fcc; + fccs[1] = fcc >> 8; + fccs[2] = fcc >> 16; + fccs[3] = fcc >> 24; + + return 10 * (fccs[0] - '0') + (fccs[1] - '0'); +} + +static bool fcc_is_data(int fcc) +{ + char fccs[4]; + + fccs[0] = fcc; + fccs[1] = fcc >> 8; + fccs[2] = fcc >> 16; + fccs[3] = fcc >> 24; + + if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w')) { + return 0; + } + if (fccs[3] != 'b' && fccs[3] != 'c') { + return 0; + } + + return 1; +} + +AviError AVI_print_error(AviError in_error) +{ + int error; + + if ((int)in_error < 0) { + error = -in_error; + } + else { + error = in_error; + } + + switch (error) { + case AVI_ERROR_NONE: + break; + case AVI_ERROR_COMPRESSION: + printf("AVI ERROR: compressed in an unsupported format\n"); + break; + case AVI_ERROR_OPEN: + printf("AVI ERROR: could not open file\n"); + break; + case AVI_ERROR_READING: + printf("AVI ERROR: could not read from file\n"); + break; + case AVI_ERROR_WRITING: + printf("AVI ERROR: could not write to file\n"); + break; + case AVI_ERROR_FORMAT: + printf("AVI ERROR: file is in an illegal or unrecognized format\n"); + break; + case AVI_ERROR_ALLOC: + printf("AVI ERROR: error encountered while allocating memory\n"); + break; + case AVI_ERROR_OPTION: + printf("AVI ERROR: program made illegal request\n"); + break; + case AVI_ERROR_FOUND: + printf("AVI ERROR: movie did not contain expected item\n"); + break; + default: + break; + } + + return in_error; +} + +bool AVI_is_avi(const char *name) +{ + int temp, fcca, j; + AviMovie movie = {NULL}; + AviMainHeader header; + AviBitmapInfoHeader bheader; + int movie_tracks = 0; + + DEBUG_PRINT("opening movie\n"); + + movie.type = AVI_MOVIE_READ; + movie.fp = BLI_fopen(name, "rb"); + movie.offset_table = NULL; + + if (movie.fp == NULL) { + return 0; + } + + if (GET_FCC(movie.fp) != FCC("RIFF") || !(movie.size = GET_FCC(movie.fp))) { + fclose(movie.fp); + return 0; + } + + movie.header = &header; + + if (GET_FCC(movie.fp) != FCC("AVI ") || GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || + GET_FCC(movie.fp) != FCC("hdrl") || (movie.header->fcc = GET_FCC(movie.fp)) != FCC("avih") || + !(movie.header->size = GET_FCC(movie.fp))) { + DEBUG_PRINT("bad initial header info\n"); + fclose(movie.fp); + return 0; + } + + movie.header->MicroSecPerFrame = GET_FCC(movie.fp); + movie.header->MaxBytesPerSec = GET_FCC(movie.fp); + movie.header->PaddingGranularity = GET_FCC(movie.fp); + movie.header->Flags = GET_FCC(movie.fp); + movie.header->TotalFrames = GET_FCC(movie.fp); + movie.header->InitialFrames = GET_FCC(movie.fp); + movie.header->Streams = GET_FCC(movie.fp); + movie.header->SuggestedBufferSize = GET_FCC(movie.fp); + movie.header->Width = GET_FCC(movie.fp); + movie.header->Height = GET_FCC(movie.fp); + movie.header->Reserved[0] = GET_FCC(movie.fp); + movie.header->Reserved[1] = GET_FCC(movie.fp); + movie.header->Reserved[2] = GET_FCC(movie.fp); + movie.header->Reserved[3] = GET_FCC(movie.fp); + + fseek(movie.fp, movie.header->size - 14 * 4, SEEK_CUR); + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie.header->Streams < 1 || movie.header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 1-65536\n"); + fclose(movie.fp); + return 0; + } + + movie.streams = (AviStreamRec *)MEM_calloc_arrayN( + movie.header->Streams, sizeof(AviStreamRec), "moviestreams"); + + for (temp = 0; temp < movie.header->Streams; temp++) { + + if (GET_FCC(movie.fp) != FCC("LIST") || !GET_FCC(movie.fp) || + GET_FCC(movie.fp) != FCC("strl") || + (movie.streams[temp].sh.fcc = GET_FCC(movie.fp)) != FCC("strh") || + !(movie.streams[temp].sh.size = GET_FCC(movie.fp))) { + DEBUG_PRINT("bad stream header information\n"); + + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + + movie.streams[temp].sh.Type = GET_FCC(movie.fp); + movie.streams[temp].sh.Handler = GET_FCC(movie.fp); + + fcca = movie.streams[temp].sh.Handler; + + if (movie.streams[temp].sh.Type == FCC("vids")) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + movie.streams[temp].format = AVI_FORMAT_AVI_RGB; + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie.streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + movie_tracks++; + } + + movie.streams[temp].sh.Flags = GET_FCC(movie.fp); + movie.streams[temp].sh.Priority = GET_TCC(movie.fp); + movie.streams[temp].sh.Language = GET_TCC(movie.fp); + movie.streams[temp].sh.InitialFrames = GET_FCC(movie.fp); + movie.streams[temp].sh.Scale = GET_FCC(movie.fp); + movie.streams[temp].sh.Rate = GET_FCC(movie.fp); + movie.streams[temp].sh.Start = GET_FCC(movie.fp); + movie.streams[temp].sh.Length = GET_FCC(movie.fp); + movie.streams[temp].sh.SuggestedBufferSize = GET_FCC(movie.fp); + movie.streams[temp].sh.Quality = GET_FCC(movie.fp); + movie.streams[temp].sh.SampleSize = GET_FCC(movie.fp); + movie.streams[temp].sh.left = GET_TCC(movie.fp); + movie.streams[temp].sh.top = GET_TCC(movie.fp); + movie.streams[temp].sh.right = GET_TCC(movie.fp); + movie.streams[temp].sh.bottom = GET_TCC(movie.fp); + + fseek(movie.fp, movie.streams[temp].sh.size - 14 * 4, SEEK_CUR); + + if (GET_FCC(movie.fp) != FCC("strf")) { + DEBUG_PRINT("no stream format information\n"); + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + + movie.streams[temp].sf_size = GET_FCC(movie.fp); + if (movie.streams[temp].sh.Type == FCC("vids")) { + j = movie.streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); + if (j >= 0) { + AviBitmapInfoHeader *bi; + + movie.streams[temp].sf = &bheader; + bi = (AviBitmapInfoHeader *)movie.streams[temp].sf; + + bi->fcc = FCC("strf"); + bi->size = movie.streams[temp].sf_size; + bi->Size = GET_FCC(movie.fp); + bi->Width = GET_FCC(movie.fp); + bi->Height = GET_FCC(movie.fp); + bi->Planes = GET_TCC(movie.fp); + bi->BitCount = GET_TCC(movie.fp); + bi->Compression = GET_FCC(movie.fp); + bi->SizeImage = GET_FCC(movie.fp); + bi->XPelsPerMeter = GET_FCC(movie.fp); + bi->YPelsPerMeter = GET_FCC(movie.fp); + bi->ClrUsed = GET_FCC(movie.fp); + bi->ClrImportant = GET_FCC(movie.fp); + + fcca = bi->Compression; + + if (movie.streams[temp].format == AVI_FORMAT_AVI_RGB) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + /* pass */ + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie.streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + } + } + if (j > 0) { + fseek(movie.fp, j, SEEK_CUR); + } + } + else { + fseek(movie.fp, movie.streams[temp].sf_size, SEEK_CUR); + } + + /* Walk to the next LIST */ + while (GET_FCC(movie.fp) != FCC("LIST")) { + temp = GET_FCC(movie.fp); + if (temp < 0 || ftell(movie.fp) > movie.size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + + MEM_freeN(movie.streams); + fclose(movie.fp); + return 0; + } + fseek(movie.fp, temp, SEEK_CUR); + } + + fseek(movie.fp, -4L, SEEK_CUR); + } + + MEM_freeN(movie.streams); + fclose(movie.fp); + + /* at least one video track is needed */ + return (movie_tracks != 0); +} + +AviError AVI_open_movie(const char *name, AviMovie *movie) +{ + int temp, fcca, size, j; + + DEBUG_PRINT("opening movie\n"); + + memset(movie, 0, sizeof(AviMovie)); + + movie->type = AVI_MOVIE_READ; + movie->fp = BLI_fopen(name, "rb"); + movie->offset_table = NULL; + + if (movie->fp == NULL) { + return AVI_ERROR_OPEN; + } + + if (GET_FCC(movie->fp) != FCC("RIFF") || !(movie->size = GET_FCC(movie->fp))) { + return AVI_ERROR_FORMAT; + } + + movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); + + if (GET_FCC(movie->fp) != FCC("AVI ") || GET_FCC(movie->fp) != FCC("LIST") || + !GET_FCC(movie->fp) || GET_FCC(movie->fp) != FCC("hdrl") || + (movie->header->fcc = GET_FCC(movie->fp)) != FCC("avih") || + !(movie->header->size = GET_FCC(movie->fp))) { + DEBUG_PRINT("bad initial header info\n"); + return AVI_ERROR_FORMAT; + } + + movie->header->MicroSecPerFrame = GET_FCC(movie->fp); + movie->header->MaxBytesPerSec = GET_FCC(movie->fp); + movie->header->PaddingGranularity = GET_FCC(movie->fp); + movie->header->Flags = GET_FCC(movie->fp); + movie->header->TotalFrames = GET_FCC(movie->fp); + movie->header->InitialFrames = GET_FCC(movie->fp); + movie->header->Streams = GET_FCC(movie->fp); + movie->header->SuggestedBufferSize = GET_FCC(movie->fp); + movie->header->Width = GET_FCC(movie->fp); + movie->header->Height = GET_FCC(movie->fp); + movie->header->Reserved[0] = GET_FCC(movie->fp); + movie->header->Reserved[1] = GET_FCC(movie->fp); + movie->header->Reserved[2] = GET_FCC(movie->fp); + movie->header->Reserved[3] = GET_FCC(movie->fp); + + fseek(movie->fp, movie->header->size - 14 * 4, SEEK_CUR); + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie->header->Streams < 1 || movie->header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 1-65536\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams = (AviStreamRec *)MEM_calloc_arrayN( + movie->header->Streams, sizeof(AviStreamRec), "moviestreams"); + + for (temp = 0; temp < movie->header->Streams; temp++) { + + if (GET_FCC(movie->fp) != FCC("LIST") || !GET_FCC(movie->fp) || + GET_FCC(movie->fp) != FCC("strl") || + (movie->streams[temp].sh.fcc = GET_FCC(movie->fp)) != FCC("strh") || + !(movie->streams[temp].sh.size = GET_FCC(movie->fp))) { + DEBUG_PRINT("bad stream header information\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams[temp].sh.Type = GET_FCC(movie->fp); + movie->streams[temp].sh.Handler = GET_FCC(movie->fp); + + fcca = movie->streams[temp].sh.Handler; + + if (movie->streams[temp].sh.Type == FCC("vids")) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + movie->streams[temp].format = AVI_FORMAT_AVI_RGB; + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie->streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + return AVI_ERROR_COMPRESSION; + } + } + + movie->streams[temp].sh.Flags = GET_FCC(movie->fp); + movie->streams[temp].sh.Priority = GET_TCC(movie->fp); + movie->streams[temp].sh.Language = GET_TCC(movie->fp); + movie->streams[temp].sh.InitialFrames = GET_FCC(movie->fp); + movie->streams[temp].sh.Scale = GET_FCC(movie->fp); + movie->streams[temp].sh.Rate = GET_FCC(movie->fp); + movie->streams[temp].sh.Start = GET_FCC(movie->fp); + movie->streams[temp].sh.Length = GET_FCC(movie->fp); + movie->streams[temp].sh.SuggestedBufferSize = GET_FCC(movie->fp); + movie->streams[temp].sh.Quality = GET_FCC(movie->fp); + movie->streams[temp].sh.SampleSize = GET_FCC(movie->fp); + movie->streams[temp].sh.left = GET_TCC(movie->fp); + movie->streams[temp].sh.top = GET_TCC(movie->fp); + movie->streams[temp].sh.right = GET_TCC(movie->fp); + movie->streams[temp].sh.bottom = GET_TCC(movie->fp); + + fseek(movie->fp, movie->streams[temp].sh.size - 14 * 4, SEEK_CUR); + + if (GET_FCC(movie->fp) != FCC("strf")) { + DEBUG_PRINT("no stream format information\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams[temp].sf_size = GET_FCC(movie->fp); + if (movie->streams[temp].sh.Type == FCC("vids")) { + j = movie->streams[temp].sf_size - (sizeof(AviBitmapInfoHeader) - 8); + if (j >= 0) { + AviBitmapInfoHeader *bi; + + movie->streams[temp].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "streamformat"); + + bi = (AviBitmapInfoHeader *)movie->streams[temp].sf; + + bi->fcc = FCC("strf"); + bi->size = movie->streams[temp].sf_size; + bi->Size = GET_FCC(movie->fp); + bi->Width = GET_FCC(movie->fp); + bi->Height = GET_FCC(movie->fp); + bi->Planes = GET_TCC(movie->fp); + bi->BitCount = GET_TCC(movie->fp); + bi->Compression = GET_FCC(movie->fp); + bi->SizeImage = GET_FCC(movie->fp); + bi->XPelsPerMeter = GET_FCC(movie->fp); + bi->YPelsPerMeter = GET_FCC(movie->fp); + bi->ClrUsed = GET_FCC(movie->fp); + bi->ClrImportant = GET_FCC(movie->fp); + + fcca = bi->Compression; + + if (movie->streams[temp].format == AVI_FORMAT_AVI_RGB) { + if (fcca == FCC("DIB ") || fcca == FCC("RGB ") || fcca == FCC("rgb ") || + fcca == FCC("RAW ") || fcca == 0) { + /* pass */ + } + else if (fcca == FCC("mjpg") || fcca == FCC("MJPG")) { + movie->streams[temp].format = AVI_FORMAT_MJPEG; + } + else { + return AVI_ERROR_COMPRESSION; + } + } + } + if (j > 0) { + fseek(movie->fp, j, SEEK_CUR); + } + } + else { + fseek(movie->fp, movie->streams[temp].sf_size, SEEK_CUR); + } + + /* Walk to the next LIST */ + while (GET_FCC(movie->fp) != FCC("LIST")) { + temp = GET_FCC(movie->fp); + if (temp < 0 || ftell(movie->fp) > movie->size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + return AVI_ERROR_FORMAT; + } + fseek(movie->fp, temp, SEEK_CUR); + } + + fseek(movie->fp, -4L, SEEK_CUR); + } + + while (1) { + temp = GET_FCC(movie->fp); + size = GET_FCC(movie->fp); + + if (size == 0) { + break; + } + + if (temp == FCC("LIST")) { + if (GET_FCC(movie->fp) == FCC("movi")) { + break; + } + else { + fseek(movie->fp, size - 4, SEEK_CUR); + } + } + else { + fseek(movie->fp, size, SEEK_CUR); + } + if (ftell(movie->fp) > movie->size) { + DEBUG_PRINT("incorrect size in header or error in AVI\n"); + return AVI_ERROR_FORMAT; + } + } + + movie->movi_offset = ftell(movie->fp); + movie->read_offset = movie->movi_offset; + + /* Read in the index if the file has one, otherwise create one */ + if (movie->header->Flags & AVIF_HASINDEX) { + fseek(movie->fp, size - 4, SEEK_CUR); + + if (GET_FCC(movie->fp) != FCC("idx1")) { + DEBUG_PRINT("bad index informatio\n"); + return AVI_ERROR_FORMAT; + } + + movie->index_entries = GET_FCC(movie->fp) / sizeof(AviIndexEntry); + if (movie->index_entries == 0) { + DEBUG_PRINT("no index entries\n"); + return AVI_ERROR_FORMAT; + } + + movie->entries = (AviIndexEntry *)MEM_mallocN(movie->index_entries * sizeof(AviIndexEntry), + "movieentries"); + + for (temp = 0; temp < movie->index_entries; temp++) { + movie->entries[temp].ChunkId = GET_FCC(movie->fp); + movie->entries[temp].Flags = GET_FCC(movie->fp); + movie->entries[temp].Offset = GET_FCC(movie->fp); + movie->entries[temp].Size = GET_FCC(movie->fp); + + if (AVI_DEBUG) { + printf("Index entry %04d: ChunkId:%s Flags:%d Offset:%d Size:%d\n", + temp, + fcc_to_char(movie->entries[temp].ChunkId), + movie->entries[temp].Flags, + movie->entries[temp].Offset, + movie->entries[temp].Size); + } + } + + /* Some AVI's have offset entries in absolute coordinates + * instead of an offset from the movie beginning... this is... + * wacky, but we need to handle it. The wacky offset always + * starts at movi_offset it seems... so we'll check that. + * Note the offset needs an extra 4 bytes for some + * undetermined reason */ + + if (movie->entries[0].Offset == movie->movi_offset) { + movie->read_offset = 4; + } + } + + DEBUG_PRINT("movie successfully opened\n"); + return AVI_ERROR_NONE; +} + +void *AVI_read_frame(AviMovie *movie, AviFormat format, int frame, int stream) +{ + int cur_frame = -1, i = 0, rewind = 1; + void *buffer; + + /* Retrieve the record number of the desired frame in the index + * If a chunk has Size 0 we need to rewind to previous frame */ + while (rewind && frame > -1) { + i = 0; + cur_frame = -1; + rewind = 0; + + while (cur_frame < frame && i < movie->index_entries) { + if (fcc_is_data(movie->entries[i].ChunkId) && + fcc_get_stream(movie->entries[i].ChunkId) == stream) { + if ((cur_frame == frame - 1) && (movie->entries[i].Size == 0)) { + rewind = 1; + frame = frame - 1; + } + else { + cur_frame++; + } + } + i++; + } + } + + if (cur_frame != frame) { + return NULL; + } + + fseek(movie->fp, movie->read_offset + movie->entries[i - 1].Offset, SEEK_SET); + + size_t size = GET_FCC(movie->fp); + buffer = MEM_mallocN(size, "readbuffer"); + + if (fread(buffer, 1, size, movie->fp) != size) { + MEM_freeN(buffer); + + return NULL; + } + + buffer = avi_format_convert(movie, stream, buffer, movie->streams[stream].format, format, &size); + + return buffer; +} + +AviError AVI_close(AviMovie *movie) +{ + int i; + + fclose(movie->fp); + + for (i = 0; i < movie->header->Streams; i++) { + if (movie->streams[i].sf != NULL) { + MEM_freeN(movie->streams[i].sf); + } + } + + MEM_freeN(movie->header); + MEM_freeN(movie->streams); + + if (movie->entries != NULL) { + MEM_freeN(movie->entries); + } + if (movie->offset_table != NULL) { + MEM_freeN(movie->offset_table); + } + + return AVI_ERROR_NONE; +} + +AviError AVI_open_compress(char *name, AviMovie *movie, int streams, ...) +{ + va_list ap; + AviList list; + AviChunk chunk; + int i; + int64_t header_pos1, header_pos2; + int64_t stream_pos1, stream_pos2; + int64_t junk_pos; + + movie->type = AVI_MOVIE_WRITE; + movie->fp = BLI_fopen(name, "wb"); + + movie->index_entries = 0; + + if (movie->fp == NULL) { + return AVI_ERROR_OPEN; + } + + movie->offset_table = (int64_t *)MEM_mallocN((1 + streams * 2) * sizeof(int64_t), "offsettable"); + + for (i = 0; i < 1 + streams * 2; i++) { + movie->offset_table[i] = -1L; + } + + movie->entries = NULL; + + movie->header = (AviMainHeader *)MEM_mallocN(sizeof(AviMainHeader), "movieheader"); + + movie->header->fcc = FCC("avih"); + movie->header->size = 56; + movie->header->MicroSecPerFrame = 66667; + movie->header->MaxBytesPerSec = 0; + movie->header->PaddingGranularity = 0; + movie->header->Flags = AVIF_HASINDEX | AVIF_MUSTUSEINDEX; + movie->header->TotalFrames = 0; + movie->header->InitialFrames = 0; + movie->header->Streams = streams; + movie->header->SuggestedBufferSize = 0; + movie->header->Width = 0; + movie->header->Height = 0; + movie->header->Reserved[0] = 0; + movie->header->Reserved[1] = 0; + movie->header->Reserved[2] = 0; + movie->header->Reserved[3] = 0; + + /* Limit number of streams to some reasonable amount to prevent + * buffer overflow vulnerabilities. */ + if (movie->header->Streams < 0 || movie->header->Streams > 65536) { + DEBUG_PRINT("Number of streams should be in range 0-65536\n"); + return AVI_ERROR_FORMAT; + } + + movie->streams = (AviStreamRec *)MEM_mallocN(sizeof(AviStreamRec) * movie->header->Streams, + "moviestreams"); + + va_start(ap, streams); + + for (i = 0; i < movie->header->Streams; i++) { + movie->streams[i].format = va_arg(ap, AviFormat); + + movie->streams[i].sh.fcc = FCC("strh"); + movie->streams[i].sh.size = 56; + movie->streams[i].sh.Type = avi_get_format_type(movie->streams[i].format); + if (movie->streams[i].sh.Type == 0) { + va_end(ap); + return AVI_ERROR_FORMAT; + } + + movie->streams[i].sh.Handler = avi_get_format_fcc(movie->streams[i].format); + if (movie->streams[i].sh.Handler == 0) { + va_end(ap); + return AVI_ERROR_FORMAT; + } + + movie->streams[i].sh.Flags = 0; + movie->streams[i].sh.Priority = 0; + movie->streams[i].sh.Language = 0; + movie->streams[i].sh.InitialFrames = 0; + movie->streams[i].sh.Scale = 66667; + movie->streams[i].sh.Rate = 1000000; + movie->streams[i].sh.Start = 0; + movie->streams[i].sh.Length = 0; + movie->streams[i].sh.SuggestedBufferSize = 0; + movie->streams[i].sh.Quality = 10000; + movie->streams[i].sh.SampleSize = 0; + movie->streams[i].sh.left = 0; + movie->streams[i].sh.top = 0; + movie->streams[i].sh.right = 0; + movie->streams[i].sh.bottom = 0; + + if (movie->streams[i].sh.Type == FCC("vids")) { + movie->streams[i].sf = MEM_mallocN(sizeof(AviBitmapInfoHeader), "moviestreamformatS"); + movie->streams[i].sf_size = sizeof(AviBitmapInfoHeader); + + ((AviBitmapInfoHeader *)movie->streams[i].sf)->fcc = FCC("strf"); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->size = movie->streams[i].sf_size - 8; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Size = movie->streams[i].sf_size - 8; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Planes = 1; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->BitCount = 24; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Compression = avi_get_format_compression( + movie->streams[i].format); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->XPelsPerMeter = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->YPelsPerMeter = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrUsed = 0; + ((AviBitmapInfoHeader *)movie->streams[i].sf)->ClrImportant = 0; + } + } + + list.fcc = FCC("RIFF"); + list.size = 0; + list.ids = FCC("AVI "); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("hdrl"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + header_pos1 = ftell(movie->fp); + + movie->offset_table[0] = ftell(movie->fp); + + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + for (i = 0; i < movie->header->Streams; i++) { + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("strl"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + stream_pos1 = ftell(movie->fp); + + movie->offset_table[1 + i * 2] = ftell(movie->fp); + awrite(movie, &movie->streams[i].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); + + movie->offset_table[1 + i * 2 + 1] = ftell(movie->fp); + awrite(movie, movie->streams[i].sf, 1, movie->streams[i].sf_size, movie->fp, AVI_BITMAPH); + + stream_pos2 = ftell(movie->fp); + + fseek(movie->fp, stream_pos1 - 8, SEEK_SET); + + PUT_FCCN((stream_pos2 - stream_pos1 + 4L), movie->fp); + + fseek(movie->fp, stream_pos2, SEEK_SET); + } + + junk_pos = ftell(movie->fp); + + if (junk_pos < 2024 - 8) { + chunk.fcc = FCC("JUNK"); + chunk.size = 2024 - 8 - (int)junk_pos; + + awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); + + for (i = 0; i < chunk.size; i++) { + putc(0, movie->fp); + } + } + + header_pos2 = ftell(movie->fp); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("movi"); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + movie->movi_offset = ftell(movie->fp) - 8L; + + fseek(movie->fp, AVI_HDRL_SOFF, SEEK_SET); + + PUT_FCCN((header_pos2 - header_pos1 + 4L), movie->fp); + + va_end(ap); + + return AVI_ERROR_NONE; +} + +AviError AVI_write_frame(AviMovie *movie, int frame_num, ...) +{ + AviList list; + AviChunk chunk; + va_list ap; + int stream; + int64_t rec_off; + AviFormat format; + void *buffer; + + if (frame_num < 0) { + return AVI_ERROR_OPTION; + } + + /* Allocate the new memory for the index entry */ + + if (frame_num >= movie->index_entries) { + const size_t entry_size = (movie->header->Streams + 1) * sizeof(AviIndexEntry); + movie->entries = (AviIndexEntry *)MEM_recallocN(movie->entries, (frame_num + 1) * entry_size); + movie->index_entries = frame_num + 1; + } + + /* Slap a new record entry onto the end of the file */ + + fseek(movie->fp, 0L, SEEK_END); + + list.fcc = FCC("LIST"); + list.size = 0; + list.ids = FCC("rec "); + + awrite(movie, &list, 1, sizeof(AviList), movie->fp, AVI_LIST); + + rec_off = ftell(movie->fp) - 8L; + + /* Write a frame for every stream */ + + va_start(ap, frame_num); + + for (stream = 0; stream < movie->header->Streams; stream++) { + unsigned int tbuf = 0; + + format = va_arg(ap, AviFormat); + buffer = va_arg(ap, void *); + size_t size = va_arg(ap, int); + + /* Convert the buffer into the output format */ + buffer = avi_format_convert( + movie, stream, buffer, format, movie->streams[stream].format, &size); + + /* Write the header info for this data chunk */ + + fseek(movie->fp, 0L, SEEK_END); + + chunk.fcc = avi_get_data_id(format, stream); + chunk.size = size; + + if (size % 4) { + chunk.size += 4 - size % 4; + } + + awrite(movie, &chunk, 1, sizeof(AviChunk), movie->fp, AVI_CHUNK); + + /* Write the index entry for this data chunk */ + + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].ChunkId = chunk.fcc; + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Flags = AVIIF_KEYFRAME; + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Offset = + (int)(ftell(movie->fp) - 12L - movie->movi_offset); + movie->entries[frame_num * (movie->header->Streams + 1) + stream + 1].Size = chunk.size; + + /* Write the chunk */ + awrite(movie, buffer, 1, size, movie->fp, AVI_RAW); + MEM_freeN(buffer); + + if (size % 4) { + awrite(movie, &tbuf, 1, 4 - size % 4, movie->fp, AVI_RAW); + } + + /* Update the stream headers length field */ + movie->streams[stream].sh.Length++; + fseek(movie->fp, movie->offset_table[1 + stream * 2], SEEK_SET); + awrite(movie, &movie->streams[stream].sh, 1, sizeof(AviStreamHeader), movie->fp, AVI_STREAMH); + } + va_end(ap); + + /* Record the entry for the new record */ + + fseek(movie->fp, 0L, SEEK_END); + + movie->entries[frame_num * (movie->header->Streams + 1)].ChunkId = FCC("rec "); + movie->entries[frame_num * (movie->header->Streams + 1)].Flags = AVIIF_LIST; + movie->entries[frame_num * (movie->header->Streams + 1)].Offset = (int)(rec_off - 8L - + movie->movi_offset); + movie->entries[frame_num * (movie->header->Streams + 1)].Size = (int)(ftell(movie->fp) - + (rec_off + 4L)); + + /* Update the record size */ + fseek(movie->fp, rec_off, SEEK_SET); + PUT_FCCN(movie->entries[frame_num * (movie->header->Streams + 1)].Size, movie->fp); + + /* Update the main header information in the file */ + movie->header->TotalFrames++; + fseek(movie->fp, movie->offset_table[0], SEEK_SET); + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + return AVI_ERROR_NONE; +} + +AviError AVI_close_compress(AviMovie *movie) +{ + int temp, movi_size, i; + + if (movie->fp == NULL) { + /* none of the allocations below were done if the file failed to open */ + return AVI_ERROR_FOUND; + } + + fseek(movie->fp, 0L, SEEK_END); + movi_size = (int)ftell(movie->fp); + + PUT_FCC("idx1", movie->fp); + PUT_FCCN((movie->index_entries * (movie->header->Streams + 1) * 16), movie->fp); + + for (temp = 0; temp < movie->index_entries * (movie->header->Streams + 1); temp++) { + awrite(movie, &movie->entries[temp], 1, sizeof(AviIndexEntry), movie->fp, AVI_INDEXE); + } + + temp = (int)ftell(movie->fp); + + fseek(movie->fp, AVI_RIFF_SOFF, SEEK_SET); + + PUT_FCCN((temp - 8L), movie->fp); + + fseek(movie->fp, movie->movi_offset, SEEK_SET); + + PUT_FCCN((movi_size - (movie->movi_offset + 4L)), movie->fp); + + fclose(movie->fp); + + for (i = 0; i < movie->header->Streams; i++) { + if (movie->streams && (movie->streams[i].sf != NULL)) { + MEM_freeN(movie->streams[i].sf); + } + } + + MEM_freeN(movie->header); + + if (movie->entries != NULL) { + MEM_freeN(movie->entries); + } + if (movie->streams != NULL) { + MEM_freeN(movie->streams); + } + if (movie->offset_table != NULL) { + MEM_freeN(movie->offset_table); + } + return AVI_ERROR_NONE; +} diff --git a/source/blender/io/avi/intern/avi_codecs.c b/source/blender/io/avi/intern/avi_codecs.c new file mode 100644 index 00000000000..15f498ac653 --- /dev/null +++ b/source/blender/io/avi/intern/avi_codecs.c @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Identify and convert different avi-files. + */ + +#include "AVI_avi.h" +#include "avi_intern.h" + +#include "avi_rgb.h" +#include "avi_mjpeg.h" +#include "avi_rgb32.h" + +void *avi_format_convert( + AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size) +{ + if (from == to) { + return buffer; + } + + if (from != AVI_FORMAT_RGB24 && to != AVI_FORMAT_RGB24) { + return avi_format_convert( + movie, + stream, + avi_format_convert(movie, stream, buffer, from, AVI_FORMAT_RGB24, size), + AVI_FORMAT_RGB24, + to, + size); + } + + switch (to) { + case AVI_FORMAT_RGB24: + switch (from) { + case AVI_FORMAT_AVI_RGB: + buffer = avi_converter_from_avi_rgb(movie, stream, buffer, size); + break; + case AVI_FORMAT_MJPEG: + buffer = avi_converter_from_mjpeg(movie, stream, buffer, size); + break; + case AVI_FORMAT_RGB32: + buffer = avi_converter_from_rgb32(movie, stream, buffer, size); + break; + default: + break; + } + break; + case AVI_FORMAT_AVI_RGB: + buffer = avi_converter_to_avi_rgb(movie, stream, buffer, size); + break; + case AVI_FORMAT_MJPEG: + buffer = avi_converter_to_mjpeg(movie, stream, buffer, size); + break; + case AVI_FORMAT_RGB32: + buffer = avi_converter_to_rgb32(movie, stream, buffer, size); + break; + default: + break; + } + + return buffer; +} + +int avi_get_data_id(AviFormat format, int stream) +{ + char fcc[5]; + + if (avi_get_format_type(format) == FCC("vids")) { + sprintf(fcc, "%2.2ddc", stream); + } + else if (avi_get_format_type(format) == FCC("auds")) { + sprintf(fcc, "%2.2ddc", stream); + } + else { + return 0; + } + + return FCC(fcc); +} + +int avi_get_format_type(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + case AVI_FORMAT_MJPEG: + return FCC("vids"); + default: + return 0; + } +} + +int avi_get_format_fcc(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + return FCC("DIB "); + case AVI_FORMAT_MJPEG: + return FCC("MJPG"); + default: + return 0; + } +} + +int avi_get_format_compression(AviFormat format) +{ + switch (format) { + case AVI_FORMAT_RGB24: + case AVI_FORMAT_RGB32: + case AVI_FORMAT_AVI_RGB: + return 0; + case AVI_FORMAT_MJPEG: + return FCC("MJPG"); + default: + return 0; + } +} diff --git a/source/blender/io/avi/intern/avi_endian.c b/source/blender/io/avi/intern/avi_endian.c new file mode 100644 index 00000000000..56474e9e329 --- /dev/null +++ b/source/blender/io/avi/intern/avi_endian.c @@ -0,0 +1,203 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Streams bytes to output depending on the + * endianness of the system. + */ + +#include +#include +#include + +#include "AVI_avi.h" +#include "avi_endian.h" +#include "avi_intern.h" + +#ifdef __BIG_ENDIAN__ +# include "MEM_guardedalloc.h" +#endif + +#ifdef __BIG_ENDIAN__ + +/* copied from BLI_endian_switch_inline.h */ +static void invert(int *val) +{ + int tval = *val; + *val = ((tval >> 24)) | ((tval << 8) & 0x00ff0000) | ((tval >> 8) & 0x0000ff00) | ((tval << 24)); +} + +static void sinvert(short int *val) +{ + short tval = *val; + *val = (tval >> 8) | (tval << 8); +} + +static void Ichunk(AviChunk *chunk) +{ + invert(&chunk->fcc); + invert(&chunk->size); +} +#endif + +#ifdef __BIG_ENDIAN__ +static void Ilist(AviList *list) +{ + invert(&list->fcc); + invert(&list->size); + invert(&list->ids); +} + +static void Imainh(AviMainHeader *mainh) +{ + invert(&mainh->fcc); + invert(&mainh->size); + invert(&mainh->MicroSecPerFrame); + invert(&mainh->MaxBytesPerSec); + invert(&mainh->PaddingGranularity); + invert(&mainh->Flags); + invert(&mainh->TotalFrames); + invert(&mainh->InitialFrames); + invert(&mainh->Streams); + invert(&mainh->SuggestedBufferSize); + invert(&mainh->Width); + invert(&mainh->Height); + invert(&mainh->Reserved[0]); + invert(&mainh->Reserved[1]); + invert(&mainh->Reserved[2]); + invert(&mainh->Reserved[3]); +} + +static void Istreamh(AviStreamHeader *streamh) +{ + invert(&streamh->fcc); + invert(&streamh->size); + invert(&streamh->Type); + invert(&streamh->Handler); + invert(&streamh->Flags); + sinvert(&streamh->Priority); + sinvert(&streamh->Language); + invert(&streamh->InitialFrames); + invert(&streamh->Scale); + invert(&streamh->Rate); + invert(&streamh->Start); + invert(&streamh->Length); + invert(&streamh->SuggestedBufferSize); + invert(&streamh->Quality); + invert(&streamh->SampleSize); + sinvert(&streamh->left); + sinvert(&streamh->right); + sinvert(&streamh->top); + sinvert(&streamh->bottom); +} + +static void Ibitmaph(AviBitmapInfoHeader *bitmaph) +{ + invert(&bitmaph->fcc); + invert(&bitmaph->size); + invert(&bitmaph->Size); + invert(&bitmaph->Width); + invert(&bitmaph->Height); + sinvert(&bitmaph->Planes); + sinvert(&bitmaph->BitCount); + invert(&bitmaph->Compression); + invert(&bitmaph->SizeImage); + invert(&bitmaph->XPelsPerMeter); + invert(&bitmaph->YPelsPerMeter); + invert(&bitmaph->ClrUsed); + invert(&bitmaph->ClrImportant); +} + +static void Imjpegu(AviMJPEGUnknown *mjpgu) +{ + invert(&mjpgu->a); + invert(&mjpgu->b); + invert(&mjpgu->c); + invert(&mjpgu->d); + invert(&mjpgu->e); + invert(&mjpgu->f); + invert(&mjpgu->g); +} + +static void Iindexe(AviIndexEntry *indexe) +{ + invert(&indexe->ChunkId); + invert(&indexe->Flags); + invert(&indexe->Offset); + invert(&indexe->Size); +} +#endif /* __BIG_ENDIAN__ */ + +void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type) +{ +#ifdef __BIG_ENDIAN__ + void *data; + + data = MEM_mallocN(size, "avi endian"); + + memcpy(data, datain, size); + + switch (type) { + case AVI_RAW: + fwrite(data, block, size, fp); + break; + case AVI_CHUNK: + Ichunk((AviChunk *)data); + fwrite(data, block, size, fp); + break; + case AVI_LIST: + Ilist((AviList *)data); + fwrite(data, block, size, fp); + break; + case AVI_MAINH: + Imainh((AviMainHeader *)data); + fwrite(data, block, size, fp); + break; + case AVI_STREAMH: + Istreamh((AviStreamHeader *)data); + fwrite(data, block, size, fp); + break; + case AVI_BITMAPH: + Ibitmaph((AviBitmapInfoHeader *)data); + if (size == sizeof(AviBitmapInfoHeader) + sizeof(AviMJPEGUnknown)) { + Imjpegu((AviMJPEGUnknown *)((char *)data + sizeof(AviBitmapInfoHeader))); + } + fwrite(data, block, size, fp); + break; + case AVI_MJPEGU: + Imjpegu((AviMJPEGUnknown *)data); + fwrite(data, block, size, fp); + break; + case AVI_INDEXE: + Iindexe((AviIndexEntry *)data); + fwrite(data, block, size, fp); + break; + default: + break; + } + + MEM_freeN(data); +#else /* __BIG_ENDIAN__ */ + (void)movie; /* unused */ + (void)type; /* unused */ + fwrite(datain, block, size, fp); +#endif /* __BIG_ENDIAN__ */ +} diff --git a/source/blender/io/avi/intern/avi_endian.h b/source/blender/io/avi/intern/avi_endian.h new file mode 100644 index 00000000000..d1253f488e7 --- /dev/null +++ b/source/blender/io/avi/intern/avi_endian.h @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. + */ + +#ifndef __AVI_ENDIAN_H__ +#define __AVI_ENDIAN_H__ + +#define AVI_RAW 0 +#define AVI_CHUNK 1 +#define AVI_LIST 2 +#define AVI_MAINH 3 +#define AVI_STREAMH 4 +#define AVI_BITMAPH 5 +#define AVI_INDEXE 6 +#define AVI_MJPEGU 7 + +void awrite(AviMovie *movie, void *datain, int block, int size, FILE *fp, int type); + +#endif /* __AVI_ENDIAN_H__ */ diff --git a/source/blender/io/avi/intern/avi_intern.h b/source/blender/io/avi/intern/avi_intern.h new file mode 100644 index 00000000000..6ce91ce7f70 --- /dev/null +++ b/source/blender/io/avi/intern/avi_intern.h @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_INTERN_H__ +#define __AVI_INTERN_H__ + +#include /* for FILE */ + +unsigned int GET_FCC(FILE *fp); +unsigned int GET_TCC(FILE *fp); + +#define PUT_FCC(ch4, fp) \ + { \ + putc(ch4[0], fp); \ + putc(ch4[1], fp); \ + putc(ch4[2], fp); \ + putc(ch4[3], fp); \ + } \ + (void)0 + +#define PUT_FCCN(num, fp) \ + { \ + putc((num >> 0) & 0377, fp); \ + putc((num >> 8) & 0377, fp); \ + putc((num >> 16) & 0377, fp); \ + putc((num >> 24) & 0377, fp); \ + } \ + (void)0 + +#define PUT_TCC(ch2, fp) \ + { \ + putc(ch2[0], fp); \ + putc(ch2[1], fp); \ + } \ + (void)0 + +void *avi_format_convert( + AviMovie *movie, int stream, void *buffer, AviFormat from, AviFormat to, size_t *size); + +int avi_get_data_id(AviFormat format, int stream); +int avi_get_format_type(AviFormat format); +int avi_get_format_fcc(AviFormat format); +int avi_get_format_compression(AviFormat format); + +#endif diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c new file mode 100644 index 00000000000..d4c7378964e --- /dev/null +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -0,0 +1,547 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts between avi and mpeg/jpeg. + */ + +#include +#include + +#include "AVI_avi.h" + +#include "MEM_guardedalloc.h" + +#include "IMB_imbuf.h" + +#include "jpeglib.h" +#include "jerror.h" + +#include "avi_mjpeg.h" + +static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize); +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize); + +static size_t numbytes; + +static void add_huff_table(j_decompress_ptr dinfo, + JHUFF_TBL **htblptr, + const UINT8 *bits, + const UINT8 *val) +{ + if (*htblptr == NULL) { + *htblptr = jpeg_alloc_huff_table((j_common_ptr)dinfo); + } + + memcpy((*htblptr)->bits, bits, sizeof((*htblptr)->bits)); + memcpy((*htblptr)->huffval, val, sizeof((*htblptr)->huffval)); + + /* Initialize sent_table false so table will be written to JPEG file. */ + (*htblptr)->sent_table = false; +} + +/* Set up the standard Huffman tables (cf. JPEG standard section K.3) */ +/* IMPORTANT: these are only valid for 8-bit data precision! */ + +static void std_huff_tables(j_decompress_ptr dinfo) +{ + static const UINT8 bits_dc_luminance[17] = { + /* 0-base */ + 0, + 0, + 1, + 5, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + }; + static const UINT8 val_dc_luminance[] = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + }; + + static const UINT8 bits_dc_chrominance[17] = { + /* 0-base */ + 0, + 0, + 3, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 0, + 0, + 0, + 0, + 0, + }; + static const UINT8 val_dc_chrominance[] = { + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + }; + + static const UINT8 bits_ac_luminance[17] = { + /* 0-base */ + 0, + 0, + 2, + 1, + 3, + 3, + 2, + 4, + 3, + 5, + 5, + 4, + 4, + 0, + 0, + 1, + 0x7d, + }; + static const UINT8 val_ac_luminance[] = { + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, + 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, + 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, + 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, + 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, + 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, + 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + }; + static const UINT8 bits_ac_chrominance[17] = { + /* 0-base */ + 0, + 0, + 2, + 1, + 2, + 4, + 4, + 3, + 4, + 7, + 5, + 4, + 4, + 0, + 1, + 2, + 0x77, + }; + static const UINT8 val_ac_chrominance[] = { + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, + 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, + 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, + 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, + 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, + 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, + 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, + 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, + }; + + add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[0], bits_dc_luminance, val_dc_luminance); + add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[0], bits_ac_luminance, val_ac_luminance); + add_huff_table(dinfo, &dinfo->dc_huff_tbl_ptrs[1], bits_dc_chrominance, val_dc_chrominance); + add_huff_table(dinfo, &dinfo->ac_huff_tbl_ptrs[1], bits_ac_chrominance, val_ac_chrominance); +} + +static int Decode_JPEG(unsigned char *inBuffer, + unsigned char *outBuffer, + unsigned int width, + unsigned int height, + size_t bufsize) +{ + struct jpeg_decompress_struct dinfo; + struct jpeg_error_mgr jerr; + + (void)width; /* unused */ + + numbytes = 0; + + dinfo.err = jpeg_std_error(&jerr); + jpeg_create_decompress(&dinfo); + jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize); + jpeg_read_header(&dinfo, true); + if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { + std_huff_tables(&dinfo); + } + dinfo.out_color_space = JCS_RGB; + dinfo.dct_method = JDCT_IFAST; + + jpeg_start_decompress(&dinfo); + + size_t rowstride = dinfo.output_width * dinfo.output_components; + for (size_t y = 0; y < dinfo.output_height; y++) { + jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); + outBuffer += rowstride; + } + jpeg_finish_decompress(&dinfo); + + if (dinfo.output_height >= height) { + return 0; + } + + inBuffer += numbytes; + jpegmemsrcmgr_build(&dinfo, inBuffer, bufsize - numbytes); + + numbytes = 0; + jpeg_read_header(&dinfo, true); + if (dinfo.dc_huff_tbl_ptrs[0] == NULL) { + std_huff_tables(&dinfo); + } + + jpeg_start_decompress(&dinfo); + rowstride = dinfo.output_width * dinfo.output_components; + for (size_t y = 0; y < dinfo.output_height; y++) { + jpeg_read_scanlines(&dinfo, (JSAMPARRAY)&outBuffer, 1); + outBuffer += rowstride; + } + jpeg_finish_decompress(&dinfo); + jpeg_destroy_decompress(&dinfo); + + return 1; +} + +static void Compress_JPEG(int quality, + unsigned char *outbuffer, + const unsigned char *inBuffer, + int width, + int height, + size_t bufsize) +{ + struct jpeg_compress_struct cinfo; + struct jpeg_error_mgr jerr; + unsigned char marker[60]; + + cinfo.err = jpeg_std_error(&jerr); + jpeg_create_compress(&cinfo); + jpegmemdestmgr_build(&cinfo, outbuffer, bufsize); + + cinfo.image_width = width; + cinfo.image_height = height; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + + jpeg_set_defaults(&cinfo); + jpeg_set_colorspace(&cinfo, JCS_YCbCr); + + jpeg_set_quality(&cinfo, quality, true); + + cinfo.dc_huff_tbl_ptrs[0]->sent_table = true; + cinfo.dc_huff_tbl_ptrs[1]->sent_table = true; + cinfo.ac_huff_tbl_ptrs[0]->sent_table = true; + cinfo.ac_huff_tbl_ptrs[1]->sent_table = true; + + cinfo.comp_info[0].component_id = 0; + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[1].component_id = 1; + cinfo.comp_info[2].component_id = 2; + + cinfo.write_JFIF_header = false; + + jpeg_start_compress(&cinfo, false); + + int i = 0; + marker[i++] = 'A'; + marker[i++] = 'V'; + marker[i++] = 'I'; + marker[i++] = '1'; + marker[i++] = 0; + while (i < 60) { + marker[i++] = 32; + } + + jpeg_write_marker(&cinfo, JPEG_APP0, marker, 60); + + i = 0; + while (i < 60) { + marker[i++] = 0; + } + + jpeg_write_marker(&cinfo, JPEG_COM, marker, 60); + + size_t rowstride = cinfo.image_width * cinfo.input_components; + for (size_t y = 0; y < cinfo.image_height; y++) { + jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&inBuffer, 1); + inBuffer += rowstride; + } + jpeg_finish_compress(&cinfo); + jpeg_destroy_compress(&cinfo); +} + +static void interlace(unsigned char *to, unsigned char *from, int width, int height) +{ + size_t i, rowstride = width * 3; + + for (i = 0; i < height; i++) { + if (i & 1) { + memcpy(&to[i * rowstride], &from[(i / 2 + height / 2) * rowstride], rowstride); + } + else { + memcpy(&to[i * rowstride], &from[(i / 2) * rowstride], rowstride); + } + } +} + +static void deinterlace(int odd, unsigned char *to, unsigned char *from, int width, int height) +{ + size_t i, rowstride = width * 3; + + for (i = 0; i < height; i++) { + if ((i & 1) == odd) { + memcpy(&to[(i / 2 + height / 2) * rowstride], &from[i * rowstride], rowstride); + } + else { + memcpy(&to[(i / 2) * rowstride], &from[i * rowstride], rowstride); + } + } +} + +void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + int deint; + unsigned char *buf; + + (void)stream; /* unused */ + + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_from_mjpeg 1"); + if (!buf) { + return NULL; + } + + deint = Decode_JPEG(buffer, buf, movie->header->Width, movie->header->Height, *size); + + MEM_freeN(buffer); + + if (deint) { + buffer = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_from_mjpeg 2"); + if (buffer) { + interlace(buffer, buf, movie->header->Width, movie->header->Height); + } + MEM_freeN(buf); + + buf = buffer; + } + + return buf; +} + +void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + size_t bufsize = *size; + + numbytes = 0; + *size = 0; + + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_to_mjpeg 1"); + if (!buf) { + return NULL; + } + + if (!movie->interlace) { + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf, + buffer, + movie->header->Width, + movie->header->Height, + bufsize); + *size += numbytes; + } + else { + deinterlace(movie->odd_fields, buf, buffer, movie->header->Width, movie->header->Height); + MEM_freeN(buffer); + + buffer = buf; + buf = imb_alloc_pixels(movie->header->Height, + movie->header->Width, + 3, + sizeof(unsigned char), + "avi.avi_converter_to_mjpeg 1"); + + if (buf) { + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf, + buffer, + movie->header->Width, + movie->header->Height / 2, + bufsize / 2); + *size += numbytes; + numbytes = 0; + Compress_JPEG(movie->streams[stream].sh.Quality / 100, + buf + *size, + buffer + + (size_t)(movie->header->Height / 2) * (size_t)movie->header->Width * 3, + movie->header->Width, + movie->header->Height / 2, + bufsize / 2); + *size += numbytes; + } + } + + MEM_freeN(buffer); + return buf; +} + +/* Compression from memory */ + +static void jpegmemdestmgr_init_destination(j_compress_ptr cinfo) +{ + (void)cinfo; /* unused */ +} + +static boolean jpegmemdestmgr_empty_output_buffer(j_compress_ptr cinfo) +{ + (void)cinfo; /* unused */ + return true; +} + +static void jpegmemdestmgr_term_destination(j_compress_ptr cinfo) +{ + numbytes -= cinfo->dest->free_in_buffer; + + MEM_freeN(cinfo->dest); +} + +static void jpegmemdestmgr_build(j_compress_ptr cinfo, unsigned char *buffer, size_t bufsize) +{ + cinfo->dest = MEM_mallocN(sizeof(*(cinfo->dest)), "avi.jpegmemdestmgr_build"); + + cinfo->dest->init_destination = jpegmemdestmgr_init_destination; + cinfo->dest->empty_output_buffer = jpegmemdestmgr_empty_output_buffer; + cinfo->dest->term_destination = jpegmemdestmgr_term_destination; + + cinfo->dest->next_output_byte = buffer; + cinfo->dest->free_in_buffer = bufsize; + + numbytes = bufsize; +} + +/* Decompression from memory */ + +static void jpegmemsrcmgr_init_source(j_decompress_ptr dinfo) +{ + (void)dinfo; +} + +static boolean jpegmemsrcmgr_fill_input_buffer(j_decompress_ptr dinfo) +{ + unsigned char *buf = (unsigned char *)dinfo->src->next_input_byte - 2; + + /* if we get called, must have run out of data */ + WARNMS(dinfo, JWRN_JPEG_EOF); + + buf[0] = (JOCTET)0xFF; + buf[1] = (JOCTET)JPEG_EOI; + + dinfo->src->next_input_byte = buf; + dinfo->src->bytes_in_buffer = 2; + + return true; +} + +static void jpegmemsrcmgr_skip_input_data(j_decompress_ptr dinfo, long skipcnt) +{ + if (dinfo->src->bytes_in_buffer < skipcnt) { + skipcnt = dinfo->src->bytes_in_buffer; + } + + dinfo->src->next_input_byte += skipcnt; + dinfo->src->bytes_in_buffer -= skipcnt; +} + +static void jpegmemsrcmgr_term_source(j_decompress_ptr dinfo) +{ + numbytes -= dinfo->src->bytes_in_buffer; + + MEM_freeN(dinfo->src); +} + +static void jpegmemsrcmgr_build(j_decompress_ptr dinfo, unsigned char *buffer, size_t bufsize) +{ + dinfo->src = MEM_mallocN(sizeof(*(dinfo->src)), "avi.jpegmemsrcmgr_build"); + + dinfo->src->init_source = jpegmemsrcmgr_init_source; + dinfo->src->fill_input_buffer = jpegmemsrcmgr_fill_input_buffer; + dinfo->src->skip_input_data = jpegmemsrcmgr_skip_input_data; + dinfo->src->resync_to_restart = jpeg_resync_to_restart; + dinfo->src->term_source = jpegmemsrcmgr_term_source; + + dinfo->src->bytes_in_buffer = bufsize; + dinfo->src->next_input_byte = buffer; + + numbytes = bufsize; +} diff --git a/source/blender/io/avi/intern/avi_mjpeg.h b/source/blender/io/avi/intern/avi_mjpeg.h new file mode 100644 index 00000000000..30e46bf1d0c --- /dev/null +++ b/source/blender/io/avi/intern/avi_mjpeg.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_MJPEG_H__ +#define __AVI_MJPEG_H__ + +void *avi_converter_from_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_mjpeg(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_MJPEG_H__ */ diff --git a/source/blender/io/avi/intern/avi_options.c b/source/blender/io/avi/intern/avi_options.c new file mode 100644 index 00000000000..65db8c19397 --- /dev/null +++ b/source/blender/io/avi/intern/avi_options.c @@ -0,0 +1,148 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Sets some compression related options + * (width, height quality, framerate). + */ + +#include "AVI_avi.h" +#include "avi_intern.h" +#include "avi_endian.h" + +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +/* avi_set_compress_options gets its own file... now don't WE feel important? */ + +AviError AVI_set_compress_option( + AviMovie *movie, int option_type, int stream, AviOption option, void *opt_data) +{ + int i; + int useconds; + + (void)stream; /* unused */ + + if (movie->header->TotalFrames != 0) { + /* Can't change params after we have already started writing frames. */ + return AVI_ERROR_OPTION; + } + + switch (option_type) { + case AVI_OPTION_TYPE_MAIN: + switch (option) { + case AVI_OPTION_WIDTH: + movie->header->Width = *((int *)opt_data); + movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Width = *((int *)opt_data); + movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; + movie->streams[i].sh.right = *((int *)opt_data); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = + movie->header->SuggestedBufferSize; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + + break; + + case AVI_OPTION_HEIGHT: + movie->header->Height = *((int *)opt_data); + movie->header->SuggestedBufferSize = movie->header->Width * movie->header->Height * 3; + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + ((AviBitmapInfoHeader *)movie->streams[i].sf)->Height = *((int *)opt_data); + movie->streams[i].sh.SuggestedBufferSize = movie->header->SuggestedBufferSize; + movie->streams[i].sh.bottom = *((int *)opt_data); + ((AviBitmapInfoHeader *)movie->streams[i].sf)->SizeImage = + movie->header->SuggestedBufferSize; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + + break; + + case AVI_OPTION_QUALITY: + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + movie->streams[i].sh.Quality = (*((int *)opt_data)) * 100; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + break; + + case AVI_OPTION_FRAMERATE: + useconds = (int)(1000000 / (*((double *)opt_data))); + if (useconds) { + movie->header->MicroSecPerFrame = useconds; + } + + for (i = 0; i < movie->header->Streams; i++) { + if (avi_get_format_type(movie->streams[i].format) == FCC("vids")) { + movie->streams[i].sh.Scale = movie->header->MicroSecPerFrame; + fseek(movie->fp, movie->offset_table[1 + i * 2 + 1], SEEK_SET); + awrite(movie, + movie->streams[i].sf, + 1, + movie->streams[i].sf_size, + movie->fp, + AVI_BITMAPH); + } + } + break; + } + + fseek(movie->fp, movie->offset_table[0], SEEK_SET); + awrite(movie, movie->header, 1, sizeof(AviMainHeader), movie->fp, AVI_MAINH); + + break; + case AVI_OPTION_TYPE_STRH: + break; + case AVI_OPTION_TYPE_STRF: + break; + default: + return AVI_ERROR_OPTION; + } + + return AVI_ERROR_NONE; +} diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c new file mode 100644 index 00000000000..d449556e79b --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb.c @@ -0,0 +1,154 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts rgb-type avi-s. + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "AVI_avi.h" +#include "avi_rgb.h" + +#include "IMB_imbuf.h" + +/* implementation */ + +void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + AviBitmapInfoHeader *bi; + short bits = 32; + + (void)size; /* unused */ + + bi = (AviBitmapInfoHeader *)movie->streams[stream].sf; + if (bi) { + bits = bi->BitCount; + } + + if (bits == 16) { + unsigned short *pxl; + unsigned char *to; +#ifdef __BIG_ENDIAN__ + unsigned char *pxla; +#endif + + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); + + if (buf) { + size_t y = movie->header->Height; + to = buf; + + while (y--) { + pxl = (unsigned short *)(buffer + y * movie->header->Width * 2); + +#ifdef __BIG_ENDIAN__ + pxla = (unsigned char *)pxl; +#endif + + size_t x = movie->header->Width; + while (x--) { +#ifdef __BIG_ENDIAN__ + int i = pxla[0]; + pxla[0] = pxla[1]; + pxla[1] = i; + + pxla += 2; +#endif + + *(to++) = ((*pxl >> 10) & 0x1f) * 8; + *(to++) = ((*pxl >> 5) & 0x1f) * 8; + *(to++) = (*pxl & 0x1f) * 8; + pxl++; + } + } + } + + MEM_freeN(buffer); + + return buf; + } + else { + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromavirgbbuf"); + + if (buf) { + size_t rowstride = movie->header->Width * 3; + if ((bits != 16) && (movie->header->Width % 2)) { + rowstride++; + } + + for (size_t y = 0; y < movie->header->Height; y++) { + memcpy(&buf[y * movie->header->Width * 3], + &buffer[((movie->header->Height - 1) - y) * rowstride], + movie->header->Width * 3); + } + + for (size_t y = 0; y < (size_t)movie->header->Height * (size_t)movie->header->Width * 3; + y += 3) { + int i = buf[y]; + buf[y] = buf[y + 2]; + buf[y + 2] = i; + } + } + + MEM_freeN(buffer); + + return buf; + } +} + +void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + + (void)stream; /* unused */ + + size_t rowstride = movie->header->Width * 3; + /* AVI files has uncompressed lines 4-byte aligned */ + rowstride = (rowstride + 3) & ~3; + + *size = movie->header->Height * rowstride; + buf = MEM_mallocN(*size, "toavirgbbuf"); + + for (size_t y = 0; y < movie->header->Height; y++) { + memcpy(&buf[y * rowstride], + &buffer[((movie->header->Height - 1) - y) * movie->header->Width * 3], + movie->header->Width * 3); + } + + for (size_t y = 0; y < movie->header->Height; y++) { + for (size_t x = 0; x < movie->header->Width * 3; x += 3) { + int i = buf[y * rowstride + x]; + buf[y * rowstride + x] = buf[y * rowstride + x + 2]; + buf[y * rowstride + x + 2] = i; + } + } + + MEM_freeN(buffer); + + return buf; +} diff --git a/source/blender/io/avi/intern/avi_rgb.h b/source/blender/io/avi/intern/avi_rgb.h new file mode 100644 index 00000000000..7c8ce590d27 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_RGB_H__ +#define __AVI_RGB_H__ + +void *avi_converter_from_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_avi_rgb(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_RGB_H__ */ diff --git a/source/blender/io/avi/intern/avi_rgb32.c b/source/blender/io/avi/intern/avi_rgb32.c new file mode 100644 index 00000000000..3efa4814c70 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb32.c @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + * + * This is external code. Converts between rgb32 and avi. + */ + +#include +#include + +#include "MEM_guardedalloc.h" + +#include "IMB_imbuf.h" + +#include "AVI_avi.h" +#include "avi_rgb32.h" + +void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + + (void)stream; /* unused */ + + *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 3; + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 3, sizeof(unsigned char), "fromrgb32buf"); + if (!buf) { + return NULL; + } + + size_t rowstridea = movie->header->Width * 3; + size_t rowstrideb = movie->header->Width * 4; + + for (size_t y = 0; y < movie->header->Height; y++) { + for (size_t x = 0; x < movie->header->Width; x++) { + buf[y * rowstridea + x * 3 + 0] = buffer[y * rowstrideb + x * 4 + 3]; + buf[y * rowstridea + x * 3 + 1] = buffer[y * rowstrideb + x * 4 + 2]; + buf[y * rowstridea + x * 3 + 2] = buffer[y * rowstrideb + x * 4 + 1]; + } + } + + MEM_freeN(buffer); + + return buf; +} + +void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size) +{ + unsigned char *buf; + unsigned char *to, *from; + + (void)stream; /* unused */ + + *size = (size_t)movie->header->Height * (size_t)movie->header->Width * 4; + buf = imb_alloc_pixels( + movie->header->Height, movie->header->Width, 4, sizeof(unsigned char), "torgb32buf"); + if (!buf) { + return NULL; + } + + memset(buf, 255, *size); + + to = buf; + from = buffer; + size_t i = (size_t)movie->header->Height * (size_t)movie->header->Width; + + while (i--) { + memcpy(to, from, 3); + to += 4; + from += 3; + } + + MEM_freeN(buffer); + + return buf; +} diff --git a/source/blender/io/avi/intern/avi_rgb32.h b/source/blender/io/avi/intern/avi_rgb32.h new file mode 100644 index 00000000000..eb4b9ca4e21 --- /dev/null +++ b/source/blender/io/avi/intern/avi_rgb32.h @@ -0,0 +1,30 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + */ + +/** \file + * \ingroup avi + */ + +#ifndef __AVI_RGB32_H__ +#define __AVI_RGB32_H__ + +void *avi_converter_from_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); +void *avi_converter_to_rgb32(AviMovie *movie, int stream, unsigned char *buffer, size_t *size); + +#endif /* __AVI_RGB32_H__ */ diff --git a/source/blender/io/collada/AnimationClipExporter.cpp b/source/blender/io/collada/AnimationClipExporter.cpp new file mode 100644 index 00000000000..5868c24e6cd --- /dev/null +++ b/source/blender/io/collada/AnimationClipExporter.cpp @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GeometryExporter.h" +#include "AnimationClipExporter.h" +#include "MaterialExporter.h" + +void AnimationClipExporter::exportAnimationClips(Scene *sce) +{ + openLibrary(); + std::map clips; + + std::vector>::iterator anim_meta_entry; + for (anim_meta_entry = anim_meta.begin(); anim_meta_entry != anim_meta.end(); + ++anim_meta_entry) { + std::vector entry = *anim_meta_entry; + std::string action_id = entry[0]; + std::string action_name = entry[1]; + + std::map::iterator it = clips.find( + action_name); + if (it == clips.end()) { + COLLADASW::ColladaAnimationClip *clip = new COLLADASW::ColladaAnimationClip(action_name); + clips[action_name] = clip; + } + COLLADASW::ColladaAnimationClip *clip = clips[action_name]; + clip->setInstancedAnimation(action_id); + } + + std::map::iterator clips_it; + for (clips_it = clips.begin(); clips_it != clips.end(); clips_it++) { + COLLADASW::ColladaAnimationClip *clip = (COLLADASW::ColladaAnimationClip *)clips_it->second; + addAnimationClip(*clip); + } + + closeLibrary(); +} diff --git a/source/blender/io/collada/AnimationClipExporter.h b/source/blender/io/collada/AnimationClipExporter.h new file mode 100644 index 00000000000..25c69fe6b93 --- /dev/null +++ b/source/blender/io/collada/AnimationClipExporter.h @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ANIMATIONCLIPEXPORTER_H__ +#define __ANIMATIONCLIPEXPORTER_H__ + +#include +#include +#include + +#include "COLLADASWLibraryAnimationClips.h" + +class AnimationClipExporter : COLLADASW::LibraryAnimationClips { + private: + Depsgraph *depsgraph; + Scene *scene; + COLLADASW::StreamWriter *sw; + BCExportSettings &export_settings; + std::vector> anim_meta; + + public: + AnimationClipExporter(Depsgraph *depsgraph, + COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings, + std::vector> anim_meta) + : COLLADASW::LibraryAnimationClips(sw), + depsgraph(depsgraph), + scene(nullptr), + sw(sw), + export_settings(export_settings), + anim_meta(anim_meta) + { + } + + void exportAnimationClips(Scene *sce); +}; + +#endif /* __ANIMATIONCLIPEXPORTER_H__ */ diff --git a/source/blender/io/collada/AnimationExporter.cpp b/source/blender/io/collada/AnimationExporter.cpp new file mode 100644 index 00000000000..cd4319e3101 --- /dev/null +++ b/source/blender/io/collada/AnimationExporter.cpp @@ -0,0 +1,877 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "GeometryExporter.h" +#include "AnimationExporter.h" +#include "AnimationClipExporter.h" +#include "BCAnimationSampler.h" +#include "MaterialExporter.h" +#include "collada_utils.h" + +std::string EMPTY_STRING; + +std::string AnimationExporter::get_axis_name(std::string channel_type, int id) +{ + + static std::map> BC_COLLADA_AXIS_FROM_TYPE = { + {"color", {"R", "G", "B"}}, + {"specular_color", {"R", "G", "B"}}, + {"diffuse_color", {"R", "G", "B"}}, + {"alpha", {"R", "G", "B"}}, + {"scale", {"X", "Y", "Z"}}, + {"location", {"X", "Y", "Z"}}, + {"rotation_euler", {"X", "Y", "Z"}}}; + + std::map>::const_iterator it; + + it = BC_COLLADA_AXIS_FROM_TYPE.find(channel_type); + if (it == BC_COLLADA_AXIS_FROM_TYPE.end()) { + return ""; + } + + const std::vector &subchannel = it->second; + if (id >= subchannel.size()) { + return ""; + } + return subchannel[id]; +} + +bool AnimationExporter::open_animation_container(bool has_container, Object *ob) +{ + if (!has_container) { + char anim_id[200]; + sprintf(anim_id, "action_container-%s", translate_id(id_name(ob)).c_str()); + openAnimation(anim_id, encode_xml(id_name(ob))); + } + return true; +} + +void AnimationExporter::openAnimationWithClip(std::string action_id, std::string action_name) +{ + std::vector anim_meta_entry; + anim_meta_entry.push_back(translate_id(action_id)); + anim_meta_entry.push_back(action_name); + anim_meta.push_back(anim_meta_entry); + + openAnimation(translate_id(action_id), action_name); +} + +void AnimationExporter::close_animation_container(bool has_container) +{ + if (has_container) { + closeAnimation(); + } +} + +bool AnimationExporter::exportAnimations() +{ + Scene *sce = export_settings.get_scene(); + + LinkNode *export_set = this->export_settings.get_export_set(); + bool has_anim_data = bc_has_animations(sce, export_set); + int animation_count = 0; + if (has_anim_data) { + + BCObjectSet animated_subset; + BCAnimationSampler::get_animated_from_export_set(animated_subset, *export_set); + animation_count = animated_subset.size(); + BCAnimationSampler animation_sampler(export_settings, animated_subset); + + try { + animation_sampler.sample_scene(export_settings, /*keyframe_at_end = */ true); + + openLibrary(); + + BCObjectSet::iterator it; + for (it = animated_subset.begin(); it != animated_subset.end(); ++it) { + Object *ob = *it; + exportAnimation(ob, animation_sampler); + } + } + catch (std::invalid_argument &iae) { + fprintf(stderr, "Animation export interrupted"); + fprintf(stderr, "Exception was: %s", iae.what()); + } + + closeLibrary(); + +#if 0 + /* TODO: If all actions shall be exported, we need to call the + * AnimationClipExporter which will figure out which actions + * need to be exported for which objects + */ + if (this->export_settings->include_all_actions) { + AnimationClipExporter ace(eval_ctx, sw, export_settings, anim_meta); + ace.exportAnimationClips(sce); + } +#endif + } + return animation_count; +} + +/* called for each exported object */ +void AnimationExporter::exportAnimation(Object *ob, BCAnimationSampler &sampler) +{ + bool container_is_open = false; + + /* Transform animations (trans, rot, scale). */ + container_is_open = open_animation_container(container_is_open, ob); + + /* Now take care of the Object Animations + * Note: For Armatures the skeletal animation has already been exported (see above) + * However Armatures also can have Object animation. + */ + bool export_as_matrix = this->export_settings.get_animation_transformation_type() == + BC_TRANSFORMATION_TYPE_MATRIX; + + if (export_as_matrix) { + /* export all transform_curves as one single matrix animation */ + export_matrix_animation(ob, sampler); + } + + export_curve_animation_set(ob, sampler, export_as_matrix); + + if (ob->type == OB_ARMATURE && export_as_matrix) { + +#ifdef WITH_MORPH_ANIMATION + /* TODO: This needs to be handled by extra profiles, postponed for now */ + export_morph_animation(ob); +#endif + + /* Export skeletal animation (if any) */ + bArmature *arm = (bArmature *)ob->data; + for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next) { + export_bone_animations_recursive(ob, root_bone, sampler); + } + } + + close_animation_container(container_is_open); +} + +/* + * Export all animation FCurves of an Object. + * + * Note: This uses the keyframes as sample points, + * and exports "baked keyframes" while keeping the tangent information + * of the FCurves intact. This works for simple cases, but breaks + * especially when negative scales are involved in the animation. + * And when parent inverse matrices are involved (when exporting + * object hierarchies) + */ +void AnimationExporter::export_curve_animation_set(Object *ob, + BCAnimationSampler &sampler, + bool export_as_matrix) +{ + BCAnimationCurveMap *curves = sampler.get_curves(ob); + bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); + + BCAnimationCurveMap::iterator it; + for (it = curves->begin(); it != curves->end(); ++it) { + BCAnimationCurve &curve = *it->second; + std::string channel_type = curve.get_channel_type(); + if (channel_type == "rotation_quaternion") { + /* Can not export Quaternion animation in Collada as far as i know) + * Maybe automatically convert to euler rotation? + * Discard for now. */ + continue; + } + + if (export_as_matrix && curve.is_transform_curve()) { + /* All Transform curves will be exported within a single matrix animation, + * see export_matrix_animation() + * No need to export the curves here again. + */ + continue; + } + + if (!keep_flat_curves && !curve.is_animated()) { + continue; + } + + BCAnimationCurve *mcurve = get_modified_export_curve(ob, curve, *curves); + if (mcurve) { + export_curve_animation(ob, *mcurve); + delete mcurve; + } + else { + export_curve_animation(ob, curve); + } + } +} + +void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &sampler) +{ + bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); + + std::vector frames; + sampler.get_object_frames(frames, ob); + if (frames.size() > 0) { + BCMatrixSampleMap samples; + bool is_animated = sampler.get_object_samples(samples, ob); + if (keep_flat_curves || is_animated) { + bAction *action = bc_getSceneObjectAction(ob); + std::string name = encode_xml(id_name(ob)); + std::string action_name = (action == NULL) ? name + "-action" : id_name(action); + std::string channel_type = "transform"; + std::string axis = ""; + std::string id = bc_get_action_id(action_name, name, channel_type, axis); + + std::string target = translate_id(name) + '/' + channel_type; + + BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); + export_collada_matrix_animation( + id, name, target, frames, samples, global_rotation_type, ob->parentinv); + } + } +} + +BC_global_rotation_type AnimationExporter::get_global_rotation_type(Object *ob) +{ + bool is_export_root = this->export_settings.is_export_root(ob); + if (!is_export_root) { + return BC_NO_ROTATION; + } + + bool apply_global_rotation = this->export_settings.get_apply_global_orientation(); + + return (apply_global_rotation) ? BC_DATA_ROTATION : BC_OBJECT_ROTATION; +} + +/* Write bone animations in transform matrix sources. */ +void AnimationExporter::export_bone_animations_recursive(Object *ob, + Bone *bone, + BCAnimationSampler &sampler) +{ + bool keep_flat_curves = this->export_settings.get_keep_flat_curves(); + + std::vector frames; + sampler.get_bone_frames(frames, ob, bone); + + if (frames.size()) { + BCMatrixSampleMap samples; + bool is_animated = sampler.get_bone_samples(samples, ob, bone); + if (keep_flat_curves || is_animated) { + export_bone_animation(ob, bone, frames, samples); + } + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + export_bone_animations_recursive(ob, child, sampler); + } +} + +/** + * In some special cases the exported Curve needs to be replaced + * by a modified curve (for collada purposes) + * This method checks if a conversion is necessary and if applicable + * returns a pointer to the modified BCAnimationCurve. + * IMPORTANT: the modified curve must be deleted by the caller when no longer needed + * if no conversion is needed this method returns a NULL; + */ +BCAnimationCurve *AnimationExporter::get_modified_export_curve(Object *ob, + BCAnimationCurve &curve, + BCAnimationCurveMap &curves) +{ + std::string channel_type = curve.get_channel_type(); + BCAnimationCurve *mcurve = NULL; + if (channel_type == "lens") { + + /* Create an xfov curve */ + + BCCurveKey key(BC_ANIMATION_TYPE_CAMERA, "xfov", 0); + mcurve = new BCAnimationCurve(key, ob); + + /* now tricky part: transform the fcurve */ + BCValueMap lens_values; + curve.get_value_map(lens_values); + + BCAnimationCurve *sensor_curve = NULL; + BCCurveKey sensor_key(BC_ANIMATION_TYPE_CAMERA, "sensor_width", 0); + BCAnimationCurveMap::iterator cit = curves.find(sensor_key); + if (cit != curves.end()) { + sensor_curve = cit->second; + } + + BCValueMap::const_iterator vit; + for (vit = lens_values.begin(); vit != lens_values.end(); ++vit) { + int frame = vit->first; + float lens_value = vit->second; + + float sensor_value; + if (sensor_curve) { + sensor_value = sensor_curve->get_value(frame); + } + else { + sensor_value = ((Camera *)ob->data)->sensor_x; + } + float value = RAD2DEGF(focallength_to_fov(lens_value, sensor_value)); + mcurve->add_value(value, frame); + } + /* to reset the handles */ + mcurve->clean_handles(); + } + return mcurve; +} + +void AnimationExporter::export_curve_animation(Object *ob, BCAnimationCurve &curve) +{ + std::string channel_target = curve.get_channel_target(); + + /* + * Some curves can not be exported as is and need some conversion + * For more information see implementation of get_modified_export_curve() + * note: if mcurve is not NULL then it must be deleted at end of this method; + */ + + int channel_index = curve.get_channel_index(); + /* RGB or XYZ or "" */ + std::string channel_type = curve.get_channel_type(); + std::string axis = get_axis_name(channel_type, channel_index); + + std::string action_name; + bAction *action = bc_getSceneObjectAction(ob); + action_name = (action) ? id_name(action) : "constraint_anim"; + + const std::string curve_name = encode_xml(curve.get_animation_name(ob)); + std::string id = bc_get_action_id(action_name, curve_name, channel_target, axis, "."); + + std::string collada_target = translate_id(curve_name); + + if (curve.is_of_animation_type(BC_ANIMATION_TYPE_MATERIAL)) { + int material_index = curve.get_subindex(); + Material *ma = BKE_object_material_get(ob, material_index + 1); + if (ma) { + collada_target = translate_id(id_name(ma)) + "-effect/common/" + + get_collada_sid(curve, axis); + } + } + else { + collada_target += "/" + get_collada_sid(curve, axis); + } + + BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); + export_collada_curve_animation( + id, curve_name, collada_target, axis, curve, global_rotation_type); +} + +void AnimationExporter::export_bone_animation(Object *ob, + Bone *bone, + BCFrames &frames, + BCMatrixSampleMap &samples) +{ + bAction *action = bc_getSceneObjectAction(ob); + std::string bone_name(bone->name); + std::string name = encode_xml(id_name(ob)); + std::string id = bc_get_action_id(id_name(action), name, bone_name, "pose_matrix"); + std::string target = translate_id(id_name(ob) + "_" + bone_name) + "/transform"; + + BC_global_rotation_type global_rotation_type = get_global_rotation_type(ob); + export_collada_matrix_animation( + id, name, target, frames, samples, global_rotation_type, ob->parentinv); +} + +bool AnimationExporter::is_bone_deform_group(Bone *bone) +{ + bool is_def; + /* Check if current bone is deform */ + if ((bone->flag & BONE_NO_DEFORM) == 0) { + return true; + } + /* Check child bones */ + else { + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + /* loop through all the children until deform bone is found, and then return */ + is_def = is_bone_deform_group(child); + if (is_def) { + return true; + } + } + } + /* no deform bone found in children also */ + return false; +} + +void AnimationExporter::export_collada_curve_animation( + std::string id, + std::string name, + std::string collada_target, + std::string axis, + BCAnimationCurve &curve, + BC_global_rotation_type global_rotation_type) +{ + BCFrames frames; + BCValues values; + curve.get_frames(frames); + curve.get_values(values); + std::string channel_target = curve.get_channel_target(); + + fprintf( + stdout, "Export animation curve %s (%d control points)\n", id.c_str(), int(frames.size())); + openAnimation(id, name); + BC_animation_source_type source_type = (curve.is_rotation_curve()) ? BC_SOURCE_TYPE_ANGLE : + BC_SOURCE_TYPE_VALUE; + + std::string input_id = collada_source_from_values( + BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, axis); + std::string output_id = collada_source_from_values( + source_type, COLLADASW::InputSemantic::OUTPUT, values, id, axis); + + bool has_tangents = false; + std::string interpolation_id; + if (this->export_settings.get_keep_smooth_curves()) { + interpolation_id = collada_interpolation_source(curve, id, axis, &has_tangents); + } + else { + interpolation_id = collada_linear_interpolation_source(frames.size(), id); + } + + std::string intangent_id; + std::string outtangent_id; + if (has_tangents) { + intangent_id = collada_tangent_from_curve( + COLLADASW::InputSemantic::IN_TANGENT, curve, id, axis); + outtangent_id = collada_tangent_from_curve( + COLLADASW::InputSemantic::OUT_TANGENT, curve, id, axis); + } + + std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX; + + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); + + sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id)); + sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id)); + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, + COLLADABU::URI(EMPTY_STRING, interpolation_id)); + + if (has_tangents) { + sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT, + COLLADABU::URI(EMPTY_STRING, intangent_id)); + sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT, + COLLADABU::URI(EMPTY_STRING, outtangent_id)); + } + + addSampler(sampler); + addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), collada_target); + + closeAnimation(); +} + +void AnimationExporter::export_collada_matrix_animation( + std::string id, + std::string name, + std::string target, + BCFrames &frames, + BCMatrixSampleMap &samples, + BC_global_rotation_type global_rotation_type, + Matrix &parentinv) +{ + fprintf( + stdout, "Export animation matrix %s (%d control points)\n", id.c_str(), int(frames.size())); + + openAnimationWithClip(id, name); + + std::string input_id = collada_source_from_values( + BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, ""); + std::string output_id = collada_source_from_values(samples, id, global_rotation_type, parentinv); + std::string interpolation_id = collada_linear_interpolation_source(frames.size(), id); + + std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX; + COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id); + + sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id)); + sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id)); + sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, + COLLADABU::URI(EMPTY_STRING, interpolation_id)); + + /* Matrix animation has no tangents */ + + addSampler(sampler); + addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), target); + + closeAnimation(); +} + +std::string AnimationExporter::get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic) +{ + switch (semantic) { + case COLLADASW::InputSemantic::INPUT: + return INPUT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::OUTPUT: + return OUTPUT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::INTERPOLATION: + return INTERPOLATION_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::IN_TANGENT: + return INTANGENT_SOURCE_ID_SUFFIX; + case COLLADASW::InputSemantic::OUT_TANGENT: + return OUTTANGENT_SOURCE_ID_SUFFIX; + default: + break; + } + return ""; +} + +void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList ¶m, + COLLADASW::InputSemantic::Semantics semantic, + bool is_rot, + const std::string axis, + bool transform) +{ + switch (semantic) { + case COLLADASW::InputSemantic::INPUT: + param.push_back("TIME"); + break; + case COLLADASW::InputSemantic::OUTPUT: + if (is_rot) { + param.push_back("ANGLE"); + } + else { + if (axis != "") { + param.push_back(axis); + } + else if (transform) { + param.push_back("TRANSFORM"); + } + else { + /* assumes if axis isn't specified all axises are added */ + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + } + } + break; + case COLLADASW::InputSemantic::IN_TANGENT: + case COLLADASW::InputSemantic::OUT_TANGENT: + param.push_back("X"); + param.push_back("Y"); + break; + default: + break; + } +} + +std::string AnimationExporter::collada_tangent_from_curve( + COLLADASW::InputSemantic::Semantics semantic, + BCAnimationCurve &curve, + const std::string &anim_id, + std::string axis_name) +{ + Scene *scene = this->export_settings.get_scene(); + + std::string channel = curve.get_channel_target(); + + const std::string source_id = anim_id + get_semantic_suffix(semantic); + + bool is_angle = (bc_startswith(channel, "rotation") || channel == "spot_size"); + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(curve.sample_count()); + source.setAccessorStride(2); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, is_angle, axis_name, false); + + source.prepareToAppendValues(); + + const FCurve *fcu = curve.get_fcurve(); + int tangent = (semantic == COLLADASW::InputSemantic::IN_TANGENT) ? 0 : 2; + + for (int i = 0; i < fcu->totvert; i++) { + BezTriple &bezt = fcu->bezt[i]; + + float sampled_time = bezt.vec[tangent][0]; + float sampled_val = bezt.vec[tangent][1]; + + if (is_angle) { + sampled_val = RAD2DEGF(sampled_val); + } + + source.appendValues(FRA2TIME(sampled_time)); + source.appendValues(sampled_val); + } + source.finish(); + return source_id; +} + +std::string AnimationExporter::collada_source_from_values( + BC_animation_source_type source_type, + COLLADASW::InputSemantic::Semantics semantic, + std::vector &values, + const std::string &anim_id, + const std::string axis_name) +{ + BlenderContext &blender_context = this->export_settings.get_blender_context(); + Scene *scene = blender_context.get_scene(); + /* T can be float, int or double */ + + int stride = 1; + int entry_count = values.size() / stride; + std::string source_id = anim_id + get_semantic_suffix(semantic); + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(entry_count); + source.setAccessorStride(stride); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, source_type == BC_SOURCE_TYPE_ANGLE, axis_name, false); + + source.prepareToAppendValues(); + + for (int i = 0; i < entry_count; i++) { + float val = values[i]; + switch (source_type) { + case BC_SOURCE_TYPE_TIMEFRAME: + val = FRA2TIME(val); + break; + case BC_SOURCE_TYPE_ANGLE: + val = RAD2DEGF(val); + break; + default: + break; + } + source.appendValues(val); + } + + source.finish(); + + return source_id; +} + +/* + * Create a collada matrix source for a set of samples + */ +std::string AnimationExporter::collada_source_from_values( + BCMatrixSampleMap &samples, + const std::string &anim_id, + BC_global_rotation_type global_rotation_type, + Matrix &parentinv) +{ + COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT; + std::string source_id = anim_id + get_semantic_suffix(semantic); + + COLLADASW::Float4x4Source source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(samples.size()); + source.setAccessorStride(16); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + add_source_parameters(param, semantic, false, "", true); + + source.prepareToAppendValues(); + + BCMatrixSampleMap::iterator it; + /* could be made configurable */ + int precision = (this->export_settings.get_limit_precision()) ? 6 : -1; + for (it = samples.begin(); it != samples.end(); it++) { + BCMatrix sample = BCMatrix(*it->second); + BCMatrix global_transform = this->export_settings.get_global_transform(); + DMatrix daemat; + if (this->export_settings.get_apply_global_orientation()) { + sample.apply_transform(global_transform); + } + else { + sample.add_transform(global_transform); + } + sample.get_matrix(daemat, true, precision); + source.appendValues(daemat); + } + + source.finish(); + return source_id; +} + +std::string AnimationExporter::collada_interpolation_source(const BCAnimationCurve &curve, + const std::string &anim_id, + const std::string axis, + bool *has_tangents) +{ + std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); + + COLLADASW::NameSource source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(curve.sample_count()); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("INTERPOLATION"); + + source.prepareToAppendValues(); + + *has_tangents = false; + + std::vector frames; + curve.get_frames(frames); + + for (unsigned int i = 0; i < curve.sample_count(); i++) { + float frame = frames[i]; + int ipo = curve.get_interpolation_type(frame); + if (ipo == BEZT_IPO_BEZ) { + source.appendValues(BEZIER_NAME); + *has_tangents = true; + } + else if (ipo == BEZT_IPO_CONST) { + source.appendValues(STEP_NAME); + } + else { + /* BEZT_IPO_LIN */ + source.appendValues(LINEAR_NAME); + } + } + /* unsupported? -- HERMITE, CARDINAL, BSPLINE, NURBS */ + + source.finish(); + + return source_id; +} + +std::string AnimationExporter::collada_linear_interpolation_source(int tot, + const std::string &anim_id) +{ + std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION); + + COLLADASW::NameSource source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(tot); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("INTERPOLATION"); + + source.prepareToAppendValues(); + + for (int i = 0; i < tot; i++) { + source.appendValues(LINEAR_NAME); + } + + source.finish(); + + return source_id; +} + +const std::string AnimationExporter::get_collada_name(std::string channel_type) const +{ + /* + * Translation table to map FCurve animation types to Collada animation. + * Todo: Maybe we can keep the names from the fcurves here instead of + * mapping. However this is what i found in the old code. So keep + * this map for now. + */ + static std::map BC_CHANNEL_BLENDER_TO_COLLADA = { + {"rotation", "rotation"}, + {"rotation_euler", "rotation"}, + {"rotation_quaternion", "rotation"}, + {"scale", "scale"}, + {"location", "location"}, + + /* Materials */ + {"specular_color", "specular"}, + {"diffuse_color", "diffuse"}, + {"ior", "index_of_refraction"}, + {"specular_hardness", "specular_hardness"}, + {"alpha", "alpha"}, + + /* Lights */ + {"color", "color"}, + {"fall_off_angle", "falloff_angle"}, + {"spot_size", "falloff_angle"}, + {"fall_off_exponent", "falloff_exponent"}, + {"spot_blend", "falloff_exponent"}, + /* Special blender profile (todo: make this more elegant). */ + {"blender/blender_dist", "blender/blender_dist"}, + /* Special blender profile (todo: make this more elegant). */ + {"distance", "blender/blender_dist"}, + + /* Cameras */ + {"lens", "xfov"}, + {"xfov", "xfov"}, + {"xmag", "xmag"}, + {"zfar", "zfar"}, + {"znear", "znear"}, + {"ortho_scale", "xmag"}, + {"clip_end", "zfar"}, + {"clip_start", "znear"}}; + + std::map::iterator name_it = BC_CHANNEL_BLENDER_TO_COLLADA.find( + channel_type); + if (name_it == BC_CHANNEL_BLENDER_TO_COLLADA.end()) { + return ""; + } + std::string tm_name = name_it->second; + return tm_name; +} + +/* + * Assign sid of the animated parameter or transform for rotation, + * axis name is always appended and the value of append_axis is ignored + */ +std::string AnimationExporter::get_collada_sid(const BCAnimationCurve &curve, + const std::string axis_name) +{ + std::string channel_target = curve.get_channel_target(); + std::string channel_type = curve.get_channel_type(); + std::string tm_name = get_collada_name(channel_type); + + bool is_angle = curve.is_rotation_curve(); + + if (tm_name.size()) { + if (is_angle) { + return tm_name + std::string(axis_name) + ".ANGLE"; + } + else if (axis_name != "") { + return tm_name + "." + std::string(axis_name); + } + else { + return tm_name; + } + } + + return tm_name; +} + +#ifdef WITH_MORPH_ANIMATION +/* TODO: This function needs to be implemented similar to the material animation export + * So we have to update BCSample for this to work. */ +void AnimationExporter::export_morph_animation(Object *ob, BCAnimationSampler &sampler) +{ + FCurve *fcu; + Key *key = BKE_key_from_object(ob); + if (!key) { + return; + } + + if (key->adt && key->adt->action) { + fcu = (FCurve *)key->adt->action->curves.first; + + while (fcu) { + BC_animation_transform_type tm_type = get_transform_type(fcu->rna_path); + + create_keyframed_animation(ob, fcu, tm_type, true, sampler); + + fcu = fcu->next; + } + } +} +#endif diff --git a/source/blender/io/collada/AnimationExporter.h b/source/blender/io/collada/AnimationExporter.h new file mode 100644 index 00000000000..64751ec5327 --- /dev/null +++ b/source/blender/io/collada/AnimationExporter.h @@ -0,0 +1,266 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __ANIMATIONEXPORTER_H__ +#define __ANIMATIONEXPORTER_H__ + +#include +#include +#include + +#include "BCAnimationCurve.h" + +extern "C" { +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_light_types.h" +#include "DNA_camera_types.h" +#include "DNA_armature_types.h" +#include "DNA_material_types.h" +#include "DNA_constraint_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "BKE_fcurve.h" +#include "BKE_animsys.h" +#include "BKE_scene.h" +#include "BKE_action.h" // pose functions +#include "BKE_armature.h" +#include "BKE_object.h" +#include "BKE_constraint.h" +#include "BIK_api.h" +#include "ED_object.h" +} + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "COLLADASWSource.h" +#include "COLLADASWInstanceGeometry.h" +#include "COLLADASWInputList.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWVertices.h" +#include "COLLADASWLibraryAnimations.h" +#include "COLLADASWParamTemplate.h" +#include "COLLADASWParamBase.h" +#include "COLLADASWSampler.h" +#include "COLLADASWConstants.h" +#include "COLLADASWBaseInputElement.h" + +#include "EffectExporter.h" +#include "BCAnimationSampler.h" +#include "collada_internal.h" + +#include "IK_solver.h" + +#include +#include +#include // std::find + +typedef enum BC_animation_source_type { + BC_SOURCE_TYPE_VALUE, + BC_SOURCE_TYPE_ANGLE, + BC_SOURCE_TYPE_TIMEFRAME, +} BC_animation_source_type; + +typedef enum BC_global_rotation_type { + BC_NO_ROTATION, + BC_OBJECT_ROTATION, + BC_DATA_ROTATION +} BC_global_rotation_type; + +class AnimationExporter : COLLADASW::LibraryAnimations { + private: + COLLADASW::StreamWriter *sw; + BCExportSettings &export_settings; + + BC_global_rotation_type get_global_rotation_type(Object *ob); + + public: + AnimationExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) + : COLLADASW::LibraryAnimations(sw), sw(sw), export_settings(export_settings) + { + } + + bool exportAnimations(); + + // called for each exported object + void operator()(Object *ob); + + protected: + void export_object_constraint_animation(Object *ob); + + void export_morph_animation(Object *ob); + + void write_bone_animation_matrix(Object *ob_arm, Bone *bone); + + void write_bone_animation(Object *ob_arm, Bone *bone); + + void sample_and_write_bone_animation(Object *ob_arm, Bone *bone, int transform_type); + + void sample_and_write_bone_animation_matrix(Object *ob_arm, Bone *bone); + + void sample_animation(float *v, + std::vector &frames, + int type, + Bone *bone, + Object *ob_arm, + bPoseChannel *pChan); + + void sample_animation(std::vector &mats, + std::vector &frames, + Bone *bone, + Object *ob_arm, + bPoseChannel *pChan); + + // dae_bone_animation -> add_bone_animation + // (blend this into dae_bone_animation) + void dae_bone_animation(std::vector &fra, + float *v, + int tm_type, + int axis, + std::string ob_name, + std::string bone_name); + + void dae_baked_animation(std::vector &fra, Object *ob_arm, Bone *bone); + + void dae_baked_object_animation(std::vector &fra, Object *ob); + + float convert_time(float frame); + + float convert_angle(float angle); + + std::vector> anim_meta; + + /* Main entry point into Animation export (called for each exported object) */ + void exportAnimation(Object *ob, BCAnimationSampler &sampler); + + /* export animation as separate trans/rot/scale curves */ + void export_curve_animation_set(Object *ob, BCAnimationSampler &sampler, bool export_tm_curves); + + /* export one single curve */ + void export_curve_animation(Object *ob, BCAnimationCurve &curve); + + /* export animation as matrix data */ + void export_matrix_animation(Object *ob, BCAnimationSampler &sampler); + + /* step through the bone hierarchy */ + void export_bone_animations_recursive(Object *ob_arm, Bone *bone, BCAnimationSampler &sampler); + + /* Export for one bone */ + void export_bone_animation(Object *ob, Bone *bone, BCFrames &frames, BCMatrixSampleMap &outmats); + + /* call to the low level collada exporter */ + void export_collada_curve_animation(std::string id, + std::string name, + std::string target, + std::string axis, + BCAnimationCurve &curve, + BC_global_rotation_type global_rotation_type); + + /* call to the low level collada exporter */ + void export_collada_matrix_animation(std::string id, + std::string name, + std::string target, + BCFrames &frames, + BCMatrixSampleMap &outmats, + BC_global_rotation_type global_rotation_type, + Matrix &parentinv); + + BCAnimationCurve *get_modified_export_curve(Object *ob, + BCAnimationCurve &curve, + BCAnimationCurveMap &curves); + + /* Helper functions */ + void openAnimationWithClip(std::string id, std::string name); + bool open_animation_container(bool has_container, Object *ob); + void close_animation_container(bool has_container); + + /* Input and Output sources (single valued) */ + std::string collada_source_from_values(BC_animation_source_type tm_channel, + COLLADASW::InputSemantic::Semantics semantic, + std::vector &values, + const std::string &anim_id, + const std::string axis_name); + + /* Output sources (matrix data) */ + std::string collada_source_from_values(BCMatrixSampleMap &samples, + const std::string &anim_id, + BC_global_rotation_type global_rotation_type, + Matrix &parentinv); + + /* Interpolation sources */ + std::string collada_linear_interpolation_source(int tot, const std::string &anim_id); + + /* source ID = animation_name + semantic_suffix */ + + std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic); + + void add_source_parameters(COLLADASW::SourceBase::ParameterNameList ¶m, + COLLADASW::InputSemantic::Semantics semantic, + bool is_rot, + const std::string axis, + bool transform); + + int get_point_in_curve(BCBezTriple &bezt, + COLLADASW::InputSemantic::Semantics semantic, + bool is_angle, + float *values); + int get_point_in_curve(const BCAnimationCurve &curve, + float sample_frame, + COLLADASW::InputSemantic::Semantics semantic, + bool is_angle, + float *values); + + std::string collada_tangent_from_curve(COLLADASW::InputSemantic::Semantics semantic, + BCAnimationCurve &curve, + const std::string &anim_id, + const std::string axis_name); + + std::string collada_interpolation_source(const BCAnimationCurve &curve, + const std::string &anim_id, + std::string axis_name, + bool *has_tangents); + + std::string get_axis_name(std::string channel, int id); + const std::string get_collada_name(std::string channel_target) const; + std::string get_collada_sid(const BCAnimationCurve &curve, const std::string axis_name); + + /* ===================================== */ + /* Currently unused or not (yet?) needed */ + /* ===================================== */ + + bool is_bone_deform_group(Bone *bone); + +#if 0 + BC_animation_transform_type _get_transform_type(const std::string path); + void get_eul_source_for_quat(std::vector &cache, Object *ob); +#endif + +#ifdef WITH_MORPH_ANIMATION + void export_morph_animation(Object *ob, BCAnimationSampler &sampler); +#endif +}; + +#endif /* __ANIMATIONEXPORTER_H__ */ diff --git a/source/blender/io/collada/AnimationImporter.cpp b/source/blender/io/collada/AnimationImporter.cpp new file mode 100644 index 00000000000..715cd9e1a12 --- /dev/null +++ b/source/blender/io/collada/AnimationImporter.cpp @@ -0,0 +1,2232 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "DNA_armature_types.h" + +#include "ED_keyframing.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_string_utils.h" + +#include "BLT_translation.h" + +#include "BKE_action.h" +#include "BKE_armature.h" +#include "BKE_fcurve.h" +#include "BKE_object.h" + +#include "MEM_guardedalloc.h" + +#include "collada_utils.h" +#include "AnimationImporter.h" +#include "ArmatureImporter.h" +#include "MaterialExporter.h" + +#include + +/* first try node name, if not available (since is optional), fall back to original id */ +template static const char *bc_get_joint_name(T *node) +{ + const std::string &id = node->getName(); + return id.size() ? id.c_str() : node->getOriginalId().c_str(); +} + +FCurve *AnimationImporter::create_fcurve(int array_index, const char *rna_path) +{ + FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); + fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); + fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + fcu->array_index = array_index; + return fcu; +} + +void AnimationImporter::add_bezt(FCurve *fcu, + float frame, + float value, + eBezTriple_Interpolation ipo) +{ + // float fps = (float)FPS; + BezTriple bez; + memset(&bez, 0, sizeof(BezTriple)); + bez.vec[1][0] = frame; + bez.vec[1][1] = value; + bez.ipo = ipo; /* use default interpolation mode here... */ + bez.f1 = bez.f2 = bez.f3 = SELECT; + bez.h1 = bez.h2 = HD_AUTO; + insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); + calchandles_fcurve(fcu); +} + +/* create one or several fcurves depending on the number of parameters being animated */ +void AnimationImporter::animation_to_fcurves(COLLADAFW::AnimationCurve *curve) +{ + COLLADAFW::FloatOrDoubleArray &input = curve->getInputValues(); + COLLADAFW::FloatOrDoubleArray &output = curve->getOutputValues(); + + float fps = (float)FPS; + size_t dim = curve->getOutDimension(); + unsigned int i; + + std::vector &fcurves = curve_map[curve->getUniqueId()]; + + switch (dim) { + case 1: /* X, Y, Z or angle */ + case 3: /* XYZ */ + case 4: + case 16: /* matrix */ + { + for (i = 0; i < dim; i++) { + FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); + + fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); + fcu->array_index = 0; + fcu->auto_smoothing = U.auto_smoothing_new; + + for (unsigned int j = 0; j < curve->getKeyCount(); j++) { + BezTriple bez; + memset(&bez, 0, sizeof(BezTriple)); + + /* input, output */ + bez.vec[1][0] = bc_get_float_value(input, j) * fps; + bez.vec[1][1] = bc_get_float_value(output, j * dim + i); + bez.h1 = bez.h2 = HD_AUTO; + + if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER || + curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_STEP) { + COLLADAFW::FloatOrDoubleArray &intan = curve->getInTangentValues(); + COLLADAFW::FloatOrDoubleArray &outtan = curve->getOutTangentValues(); + + /* intangent */ + unsigned int index = 2 * (j * dim + i); + bez.vec[0][0] = bc_get_float_value(intan, index) * fps; + bez.vec[0][1] = bc_get_float_value(intan, index + 1); + + /* outtangent */ + bez.vec[2][0] = bc_get_float_value(outtan, index) * fps; + bez.vec[2][1] = bc_get_float_value(outtan, index + 1); + if (curve->getInterpolationType() == COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER) { + bez.ipo = BEZT_IPO_BEZ; + bez.h1 = bez.h2 = HD_AUTO_ANIM; + } + else { + bez.ipo = BEZT_IPO_CONST; + } + } + else { + bez.ipo = BEZT_IPO_LIN; + } +#if 0 + bez.ipo = U.ipo_new; /* use default interpolation mode here... */ +#endif + bez.f1 = bez.f2 = bez.f3 = SELECT; + + insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); + } + + calchandles_fcurve(fcu); + + fcurves.push_back(fcu); + unused_curves.push_back(fcu); + } + } break; + default: + fprintf(stderr, + "Output dimension of %d is not yet supported (animation id = %s)\n", + (int)dim, + curve->getOriginalId().c_str()); + } +} + +void AnimationImporter::fcurve_deg_to_rad(FCurve *cu) +{ + for (unsigned int i = 0; i < cu->totvert; i++) { + /* TODO convert handles too */ + cu->bezt[i].vec[1][1] *= DEG2RADF(1.0f); + cu->bezt[i].vec[0][1] *= DEG2RADF(1.0f); + cu->bezt[i].vec[2][1] *= DEG2RADF(1.0f); + } +} + +void AnimationImporter::fcurve_scale(FCurve *cu, int scale) +{ + for (unsigned int i = 0; i < cu->totvert; i++) { + /* TODO convert handles too */ + cu->bezt[i].vec[1][1] *= scale; + cu->bezt[i].vec[0][1] *= scale; + cu->bezt[i].vec[2][1] *= scale; + } +} + +void AnimationImporter::fcurve_is_used(FCurve *fcu) +{ + unused_curves.erase(std::remove(unused_curves.begin(), unused_curves.end(), fcu), + unused_curves.end()); +} + +void AnimationImporter::add_fcurves_to_object(Main *bmain, + Object *ob, + std::vector &curves, + char *rna_path, + int array_index, + Animation *animated) +{ + bAction *act; + + if (!ob->adt || !ob->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&ob->id); + } + else { + act = ob->adt->action; + } + + std::vector::iterator it; + int i; + +#if 0 + char *p = strstr(rna_path, "rotation_euler"); + bool is_rotation = p && *(p + strlen("rotation_euler")) == '\0'; + + /* convert degrees to radians for rotation */ + if (is_rotation) { + fcurve_deg_to_rad(fcu); + } +#endif + + for (it = curves.begin(), i = 0; it != curves.end(); it++, i++) { + FCurve *fcu = *it; + fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + + if (array_index == -1) { + fcu->array_index = i; + } + else { + fcu->array_index = array_index; + } + + if (ob->type == OB_ARMATURE) { + bActionGroup *grp = NULL; + const char *bone_name = bc_get_joint_name(animated->node); + + if (bone_name) { + /* try to find group */ + grp = BKE_action_group_find_name(act, bone_name); + + /* no matching groups, so add one */ + if (grp == NULL) { + /* Add a new group, and make it active */ + grp = (bActionGroup *)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); + + grp->flag = AGRP_SELECTED; + BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); + + BLI_addtail(&act->groups, grp); + BLI_uniquename(&act->groups, + grp, + CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), + '.', + offsetof(bActionGroup, name), + 64); + } + + /* add F-Curve to group */ + action_groups_add_channel(act, grp, fcu); + fcurve_is_used(fcu); + } +#if 0 + if (is_rotation) { + fcurves_actionGroup_map[grp].push_back(fcu); + } +#endif + } + else { + BLI_addtail(&act->curves, fcu); + fcurve_is_used(fcu); + } + } +} + +AnimationImporter::~AnimationImporter() +{ + /* free unused FCurves */ + for (std::vector::iterator it = unused_curves.begin(); it != unused_curves.end(); + it++) { + free_fcurve(*it); + } + + if (unused_curves.size()) { + fprintf(stderr, "removed %d unused curves\n", (int)unused_curves.size()); + } +} + +bool AnimationImporter::write_animation(const COLLADAFW::Animation *anim) +{ + if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) { + COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve *)anim; + + /* XXX Don't know if it's necessary + * Should we check outPhysicalDimension? */ + if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) { + fprintf(stderr, "Inputs physical dimension is not time.\n"); + return true; + } + + /* a curve can have mixed interpolation type, + * in this case curve->getInterpolationTypes returns a list of interpolation types per key */ + COLLADAFW::AnimationCurve::InterpolationType interp = curve->getInterpolationType(); + + if (interp != COLLADAFW::AnimationCurve::INTERPOLATION_MIXED) { + switch (interp) { + case COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR: + case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER: + case COLLADAFW::AnimationCurve::INTERPOLATION_STEP: + animation_to_fcurves(curve); + break; + default: + /* TODO there're also CARDINAL, HERMITE, BSPLINE and STEP types */ + fprintf(stderr, + "CARDINAL, HERMITE and BSPLINE anim interpolation types not supported yet.\n"); + break; + } + } + else { + /* not supported yet */ + fprintf(stderr, "MIXED anim interpolation type is not supported yet.\n"); + } + } + else { + fprintf(stderr, "FORMULA animation type is not supported yet.\n"); + } + + return true; +} + +/* called on post-process stage after writeVisualScenes */ +bool AnimationImporter::write_animation_list(const COLLADAFW::AnimationList *animlist) +{ + const COLLADAFW::UniqueId &animlist_id = animlist->getUniqueId(); + animlist_map[animlist_id] = animlist; + +#if 0 + + /* should not happen */ + if (uid_animated_map.find(animlist_id) == uid_animated_map.end()) { + return true; + } + + /* for bones rna_path is like: pose.bones["bone-name"].rotation */ + +#endif + + return true; +} + +/* \todo refactor read_node_transform to not automatically apply anything, + * but rather return the transform matrix, so caller can do with it what is + * necessary. Same for \ref get_node_mat */ +void AnimationImporter::read_node_transform(COLLADAFW::Node *node, Object *ob) +{ + float mat[4][4]; + TransformReader::get_node_mat(mat, node, &uid_animated_map, ob); + if (ob) { + copy_m4_m4(ob->obmat, mat); + BKE_object_apply_mat4(ob, ob->obmat, 0, 0); + } +} + +#if 0 +virtual void AnimationImporter::change_eul_to_quat(Object *ob, bAction *act) +{ + bActionGroup *grp; + int i; + + for (grp = (bActionGroup *)act->groups.first; grp; grp = grp->next) { + + FCurve *eulcu[3] = {NULL, NULL, NULL}; + + if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) { + continue; + } + + std::vector &rot_fcurves = fcurves_actionGroup_map[grp]; + + if (rot_fcurves.size() > 3) { + continue; + } + + for (i = 0; i < rot_fcurves.size(); i++) { + eulcu[rot_fcurves[i]->array_index] = rot_fcurves[i]; + } + + char joint_path[100]; + char rna_path[100]; + + BLI_snprintf(joint_path, sizeof(joint_path), "pose.bones[\"%s\"]", grp->name); + BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_quaternion", joint_path); + + FCurve *quatcu[4] = { + create_fcurve(0, rna_path), + create_fcurve(1, rna_path), + create_fcurve(2, rna_path), + create_fcurve(3, rna_path), + }; + + bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, grp->name); + + float m4[4][4], irest[3][3]; + invert_m4_m4(m4, chan->bone->arm_mat); + copy_m3_m4(irest, m4); + + for (i = 0; i < 3; i++) { + + FCurve *cu = eulcu[i]; + + if (!cu) { + continue; + } + + for (int j = 0; j < cu->totvert; j++) { + float frame = cu->bezt[j].vec[1][0]; + + float eul[3] = { + eulcu[0] ? evaluate_fcurve(eulcu[0], frame) : 0.0f, + eulcu[1] ? evaluate_fcurve(eulcu[1], frame) : 0.0f, + eulcu[2] ? evaluate_fcurve(eulcu[2], frame) : 0.0f, + }; + + /* make eul relative to bone rest pose */ + float rot[3][3], rel[3][3], quat[4]; + +# if 0 + eul_to_mat3(rot, eul); + mul_m3_m3m3(rel, irest, rot); + mat3_to_quat(quat, rel); +# endif + + eul_to_quat(quat, eul); + + for (int k = 0; k < 4; k++) { + create_bezt(quatcu[k], frame, quat[k], U.ipo_new); + } + } + } + + /* now replace old Euler curves */ + + for (i = 0; i < 3; i++) { + if (!eulcu[i]) { + continue; + } + + action_groups_remove_channel(act, eulcu[i]); + free_fcurve(eulcu[i]); + } + + chan->rotmode = ROT_MODE_QUAT; + + for (i = 0; i < 4; i++) { + action_groups_add_channel(act, grp, quatcu[i]); + } + } + + bPoseChannel *pchan; + for (pchan = (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan = pchan->next) { + pchan->rotmode = ROT_MODE_QUAT; + } +} +#endif + +/* sets the rna_path and array index to curve */ +void AnimationImporter::modify_fcurve(std::vector *curves, + const char *rna_path, + int array_index, + int scale) +{ + std::vector::iterator it; + int i; + for (it = curves->begin(), i = 0; it != curves->end(); it++, i++) { + FCurve *fcu = *it; + fcu->rna_path = BLI_strdup(rna_path); + + if (array_index == -1) { + fcu->array_index = i; + } + else { + fcu->array_index = array_index; + } + + if (scale != 1) { + fcurve_scale(fcu, scale); + } + + fcurve_is_used(fcu); + } +} + +void AnimationImporter::unused_fcurve(std::vector *curves) +{ + /* when an error happens and we can't actually use curve remove it from unused_curves */ + std::vector::iterator it; + for (it = curves->begin(); it != curves->end(); it++) { + FCurve *fcu = *it; + fcurve_is_used(fcu); + } +} + +void AnimationImporter::find_frames(std::vector *frames, std::vector *curves) +{ + std::vector::iterator iter; + for (iter = curves->begin(); iter != curves->end(); iter++) { + FCurve *fcu = *iter; + + for (unsigned int k = 0; k < fcu->totvert; k++) { + /* get frame value from bezTriple */ + float fra = fcu->bezt[k].vec[1][0]; + /* if frame already not added add frame to frames */ + if (std::find(frames->begin(), frames->end(), fra) == frames->end()) { + frames->push_back(fra); + } + } + } +} + +static int get_animation_axis_index(const COLLADABU::Math::Vector3 &axis) +{ + int index; + if (COLLADABU::Math::Vector3::UNIT_X == axis) { + index = 0; + } + else if (COLLADABU::Math::Vector3::UNIT_Y == axis) { + index = 1; + } + else if (COLLADABU::Math::Vector3::UNIT_Z == axis) { + index = 2; + } + else { + index = -1; + } + return index; +} + +/* creates the rna_paths and array indices of fcurves from animations using transformation and + * bound animation class of each animation. */ +void AnimationImporter::Assign_transform_animations( + COLLADAFW::Transformation *transform, + const COLLADAFW::AnimationList::AnimationBinding *binding, + std::vector *curves, + bool is_joint, + char *joint_path) +{ + COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); + bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; + bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; + + /* to check if the no of curves are valid */ + bool xyz = ((tm_type == COLLADAFW::Transformation::TRANSLATE || + tm_type == COLLADAFW::Transformation::SCALE) && + binding->animationClass == COLLADAFW::AnimationList::POSITION_XYZ); + + if (!((!xyz && curves->size() == 1) || (xyz && curves->size() == 3) || is_matrix)) { + fprintf(stderr, "expected %d curves, got %d\n", xyz ? 3 : 1, (int)curves->size()); + return; + } + + char rna_path[100]; + + switch (tm_type) { + case COLLADAFW::Transformation::TRANSLATE: + case COLLADAFW::Transformation::SCALE: { + bool loc = tm_type == COLLADAFW::Transformation::TRANSLATE; + if (is_joint) { + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, loc ? "location" : "scale"); + } + else { + BLI_strncpy(rna_path, loc ? "location" : "scale", sizeof(rna_path)); + } + + switch (binding->animationClass) { + case COLLADAFW::AnimationList::POSITION_X: + modify_fcurve(curves, rna_path, 0); + break; + case COLLADAFW::AnimationList::POSITION_Y: + modify_fcurve(curves, rna_path, 1); + break; + case COLLADAFW::AnimationList::POSITION_Z: + modify_fcurve(curves, rna_path, 2); + break; + case COLLADAFW::AnimationList::POSITION_XYZ: + modify_fcurve(curves, rna_path, -1); + break; + default: + unused_fcurve(curves); + fprintf(stderr, + "AnimationClass %d is not supported for %s.\n", + binding->animationClass, + loc ? "TRANSLATE" : "SCALE"); + } + break; + } + + case COLLADAFW::Transformation::ROTATE: { + if (is_joint) { + BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation_euler", joint_path); + } + else { + BLI_strncpy(rna_path, "rotation_euler", sizeof(rna_path)); + } + std::vector::iterator iter; + for (iter = curves->begin(); iter != curves->end(); iter++) { + FCurve *fcu = *iter; + + /* if transform is rotation the fcurves values must be turned in to radian. */ + if (is_rotation) { + fcurve_deg_to_rad(fcu); + } + } + COLLADAFW::Rotate *rot = (COLLADAFW::Rotate *)transform; + COLLADABU::Math::Vector3 &axis = rot->getRotationAxis(); + + switch (binding->animationClass) { + case COLLADAFW::AnimationList::ANGLE: { + int axis_index = get_animation_axis_index(axis); + if (axis_index >= 0) { + modify_fcurve(curves, rna_path, axis_index); + } + else { + unused_fcurve(curves); + } + } break; + case COLLADAFW::AnimationList::AXISANGLE: + /* TODO convert axis-angle to quat? or XYZ? */ + default: + unused_fcurve(curves); + fprintf(stderr, + "AnimationClass %d is not supported for ROTATE transformation.\n", + binding->animationClass); + } + break; + } + + case COLLADAFW::Transformation::MATRIX: +#if 0 + { + COLLADAFW::Matrix *mat = (COLLADAFW::Matrix *)transform; + COLLADABU::Math::Matrix4 mat4 = mat->getMatrix(); + switch (binding->animationClass) { + case COLLADAFW::AnimationList::TRANSFORM: + } + } +#endif + unused_fcurve(curves); + break; + case COLLADAFW::Transformation::SKEW: + case COLLADAFW::Transformation::LOOKAT: + unused_fcurve(curves); + fprintf(stderr, "Animation of SKEW and LOOKAT transformations is not supported yet.\n"); + break; + } +} + +/* creates the rna_paths and array indices of fcurves from animations using color and bound + * animation class of each animation. */ +void AnimationImporter::Assign_color_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const char *anim_type) +{ + char rna_path[100]; + BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); + + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + if (animlist == NULL) { + fprintf(stderr, + "Collada: No animlist found for ID: %s of type %s\n", + listid.toAscii().c_str(), + anim_type); + return; + } + + const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); + /* all the curves belonging to the current binding */ + std::vector animcurves; + for (unsigned int j = 0; j < bindings.getCount(); j++) { + animcurves = curve_map[bindings[j].animation]; + + switch (bindings[j].animationClass) { + case COLLADAFW::AnimationList::COLOR_R: + modify_fcurve(&animcurves, rna_path, 0); + break; + case COLLADAFW::AnimationList::COLOR_G: + modify_fcurve(&animcurves, rna_path, 1); + break; + case COLLADAFW::AnimationList::COLOR_B: + modify_fcurve(&animcurves, rna_path, 2); + break; + case COLLADAFW::AnimationList::COLOR_RGB: + case COLLADAFW::AnimationList::COLOR_RGBA: /* to do-> set intensity */ + modify_fcurve(&animcurves, rna_path, -1); + break; + + default: + unused_fcurve(&animcurves); + fprintf(stderr, + "AnimationClass %d is not supported for %s.\n", + bindings[j].animationClass, + "COLOR"); + } + + std::vector::iterator iter; + /* Add the curves of the current animation to the object */ + for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { + FCurve *fcu = *iter; + BLI_addtail(AnimCurves, fcu); + fcurve_is_used(fcu); + } + } +} + +void AnimationImporter::Assign_float_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const char *anim_type) +{ + char rna_path[100]; + if (animlist_map.find(listid) == animlist_map.end()) { + return; + } + else { + /* anim_type has animations */ + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); + /* all the curves belonging to the current binding */ + std::vector animcurves; + for (unsigned int j = 0; j < bindings.getCount(); j++) { + animcurves = curve_map[bindings[j].animation]; + + BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); + modify_fcurve(&animcurves, rna_path, 0); + std::vector::iterator iter; + /* Add the curves of the current animation to the object */ + for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { + FCurve *fcu = *iter; + /* All anim_types whose values are to be converted from Degree to Radians can be ORed here + */ + if (STREQ("spot_size", anim_type)) { + /* NOTE: Do NOT convert if imported file was made by blender <= 2.69.10 + * Reason: old blender versions stored spot_size in radians (was a bug) + */ + if (this->import_from_version == "" || + BLI_strcasecmp_natural(this->import_from_version.c_str(), "2.69.10") != -1) { + fcurve_deg_to_rad(fcu); + } + } + /** XXX What About animtype "rotation" ? */ + + BLI_addtail(AnimCurves, fcu); + fcurve_is_used(fcu); + } + } + } +} + +float AnimationImporter::convert_to_focal_length(float in_xfov, + int fov_type, + float aspect, + float sensorx) +{ + /* NOTE: Needs more testing (As we currently have no official test data for this) */ + float xfov = (fov_type == CAMERA_YFOV) ? + (2.0f * atanf(aspect * tanf(DEG2RADF(in_xfov) * 0.5f))) : + DEG2RADF(in_xfov); + return fov_to_focallength(xfov, sensorx); +} + +/* + * Lens animations must be stored in COLLADA by using FOV, + * while blender internally uses focal length. + * The imported animation curves must be converted appropriately. + */ +void AnimationImporter::Assign_lens_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const double aspect, + Camera *cam, + const char *anim_type, + int fov_type) +{ + char rna_path[100]; + if (animlist_map.find(listid) == animlist_map.end()) { + return; + } + else { + /* anim_type has animations */ + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); + /* all the curves belonging to the current binding */ + std::vector animcurves; + for (unsigned int j = 0; j < bindings.getCount(); j++) { + animcurves = curve_map[bindings[j].animation]; + + BLI_strncpy(rna_path, anim_type, sizeof(rna_path)); + + modify_fcurve(&animcurves, rna_path, 0); + std::vector::iterator iter; + /* Add the curves of the current animation to the object */ + for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { + FCurve *fcu = *iter; + + for (unsigned int i = 0; i < fcu->totvert; i++) { + fcu->bezt[i].vec[0][1] = convert_to_focal_length( + fcu->bezt[i].vec[0][1], fov_type, aspect, cam->sensor_x); + fcu->bezt[i].vec[1][1] = convert_to_focal_length( + fcu->bezt[i].vec[1][1], fov_type, aspect, cam->sensor_x); + fcu->bezt[i].vec[2][1] = convert_to_focal_length( + fcu->bezt[i].vec[2][1], fov_type, aspect, cam->sensor_x); + } + + BLI_addtail(AnimCurves, fcu); + fcurve_is_used(fcu); + } + } + } +} + +void AnimationImporter::apply_matrix_curves(Object *ob, + std::vector &animcurves, + COLLADAFW::Node *root, + COLLADAFW::Node *node, + COLLADAFW::Transformation *tm) +{ + bool is_joint = node->getType() == COLLADAFW::Node::JOINT; + const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; + char joint_path[200]; + if (is_joint) { + armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); + } + + std::vector frames; + find_frames(&frames, &animcurves); + + float irest_dae[4][4]; + float rest[4][4], irest[4][4]; + + if (is_joint) { + get_joint_rest_mat(irest_dae, root, node); + invert_m4(irest_dae); + + Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); + if (!bone) { + fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); + return; + } + + unit_m4(rest); + copy_m4_m4(rest, bone->arm_mat); + invert_m4_m4(irest, rest); + } + /* new curves to assign matrix transform animation */ + FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale */ + unsigned int totcu = 10; + const char *tm_str = NULL; + char rna_path[200]; + for (int i = 0; i < totcu; i++) { + + int axis = i; + + if (i < 4) { + tm_str = "rotation_quaternion"; + axis = i; + } + else if (i < 7) { + tm_str = "location"; + axis = i - 4; + } + else { + tm_str = "scale"; + axis = i - 7; + } + + if (is_joint) { + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); + } + else { + BLI_strncpy(rna_path, tm_str, sizeof(rna_path)); + } + newcu[i] = create_fcurve(axis, rna_path); + newcu[i]->totvert = frames.size(); + } + + if (frames.size() == 0) { + return; + } + + std::sort(frames.begin(), frames.end()); + + std::vector::iterator it; + +#if 0 + float qref[4]; + unit_qt(qref); +#endif + + /* sample values at each frame */ + for (it = frames.begin(); it != frames.end(); it++) { + float fra = *it; + + float mat[4][4]; + float matfra[4][4]; + + unit_m4(matfra); + + /* calc object-space mat */ + evaluate_transform_at_frame(matfra, node, fra); + + /* for joints, we need a special matrix */ + if (is_joint) { + /* special matrix: iR * M * iR_dae * R + * where R, iR are bone rest and inverse rest mats in world space (Blender bones), + * iR_dae is joint inverse rest matrix (DAE) + * and M is an evaluated joint world-space matrix (DAE) */ + float temp[4][4], par[4][4]; + + /* calc M */ + calc_joint_parent_mat_rest(par, NULL, root, node); + mul_m4_m4m4(temp, par, matfra); + +#if 0 + evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); +#endif + + /* calc special matrix */ + mul_m4_series(mat, irest, temp, irest_dae, rest); + } + else { + copy_m4_m4(mat, matfra); + } + + float rot[4], loc[3], scale[3]; + mat4_decompose(loc, rot, scale, mat); + + /* add keys */ + for (int i = 0; i < totcu; i++) { + if (i < 4) { + add_bezt(newcu[i], fra, rot[i]); + } + else if (i < 7) { + add_bezt(newcu[i], fra, loc[i - 4]); + } + else { + add_bezt(newcu[i], fra, scale[i - 7]); + } + } + } + Main *bmain = CTX_data_main(mContext); + ED_id_action_ensure(bmain, (ID *)&ob->id); + + ListBase *curves = &ob->adt->action->curves; + + /* add curves */ + for (int i = 0; i < totcu; i++) { + if (is_joint) { + add_bone_fcurve(ob, node, newcu[i]); + } + else { + BLI_addtail(curves, newcu[i]); + } +#if 0 + fcurve_is_used(newcu[i]); /* never added to unused */ +#endif + } + + if (is_joint) { + bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); + chan->rotmode = ROT_MODE_QUAT; + } + else { + ob->rotmode = ROT_MODE_QUAT; + } + + return; +} + +/* + * This function returns the aspect ration from the Collada camera. + * + * Note:COLLADA allows to specify either XFov, or YFov alone. + * In that case the aspect ratio can be determined from + * the viewport aspect ratio (which is 1:1 ?) + * XXX: check this: its probably wrong! + * If both values are specified, then the aspect ration is simply xfov/yfov + * and if aspect ratio is efined, then .. well then its that one. + */ +static const double get_aspect_ratio(const COLLADAFW::Camera *camera) +{ + double aspect = camera->getAspectRatio().getValue(); + + if (aspect == 0) { + const double yfov = camera->getYFov().getValue(); + + if (yfov == 0) { + aspect = 1; /* assume yfov and xfov are equal */ + } + else { + const double xfov = camera->getXFov().getValue(); + if (xfov == 0) { + aspect = 1; + } + else { + aspect = xfov / yfov; + } + } + } + return aspect; +} + +static ListBase &get_animation_curves(Main *bmain, Material *ma) +{ + bAction *act; + if (!ma->adt || !ma->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&ma->id); + } + else { + act = ma->adt->action; + } + + return act->curves; +} + +void AnimationImporter::translate_Animations( + COLLADAFW::Node *node, + std::map &root_map, + std::multimap &object_map, + std::map FW_object_map, + std::map uid_material_map) +{ + bool is_joint = node->getType() == COLLADAFW::Node::JOINT; + COLLADAFW::UniqueId uid = node->getUniqueId(); + COLLADAFW::Node *root = root_map.find(uid) == root_map.end() ? node : root_map[uid]; + + Object *ob; + if (is_joint) { + ob = armature_importer->get_armature_for_joint(root); + } + else { + ob = object_map.find(uid) == object_map.end() ? NULL : object_map.find(uid)->second; + } + + if (!ob) { + fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); + return; + } + + AnimationImporter::AnimMix *animType = get_animation_type(node, FW_object_map); + bAction *act; + Main *bmain = CTX_data_main(mContext); + + if ((animType->transform) != 0) { + /* const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; */ /* UNUSED */ + char joint_path[200]; + + if (is_joint) { + armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); + } + + if (!ob->adt || !ob->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&ob->id); + } + else { + act = ob->adt->action; + } + + /* Get the list of animation curves of the object */ + ListBase *AnimCurves = &(act->curves); + + const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); + + /* for each transformation in node */ + for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { + COLLADAFW::Transformation *transform = nodeTransforms[i]; + COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); + + bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; + bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; + + const COLLADAFW::UniqueId &listid = transform->getAnimationList(); + + /* check if transformation has animations */ + if (animlist_map.find(listid) == animlist_map.end()) { + continue; + } + else { + /* transformation has animations */ + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings &bindings = + animlist->getAnimationBindings(); + /* all the curves belonging to the current binding */ + std::vector animcurves; + for (unsigned int j = 0; j < bindings.getCount(); j++) { + animcurves = curve_map[bindings[j].animation]; + if (is_matrix) { + apply_matrix_curves(ob, animcurves, root, node, transform); + } + else { + /* calculate rnapaths and array index of fcurves according to transformation and + * animation class */ + Assign_transform_animations( + transform, &bindings[j], &animcurves, is_joint, joint_path); + + std::vector::iterator iter; + /* Add the curves of the current animation to the object */ + for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { + FCurve *fcu = *iter; + + BLI_addtail(AnimCurves, fcu); + fcurve_is_used(fcu); + } + } + } + } + if (is_rotation && !(is_joint || is_matrix)) { + ob->rotmode = ROT_MODE_EUL; + } + } + } + + if ((animType->light) != 0) { + Light *lamp = (Light *)ob->data; + if (!lamp->adt || !lamp->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&lamp->id); + } + else { + act = lamp->adt->action; + } + + ListBase *AnimCurves = &(act->curves); + const COLLADAFW::InstanceLightPointerArray &nodeLights = node->getInstanceLights(); + + for (unsigned int i = 0; i < nodeLights.getCount(); i++) { + const COLLADAFW::Light *light = (COLLADAFW::Light *) + FW_object_map[nodeLights[i]->getInstanciatedObjectId()]; + + if ((animType->light & LIGHT_COLOR) != 0) { + const COLLADAFW::Color *col = &(light->getColor()); + const COLLADAFW::UniqueId &listid = col->getAnimationList(); + + Assign_color_animations(listid, AnimCurves, "color"); + } + if ((animType->light & LIGHT_FOA) != 0) { + const COLLADAFW::AnimatableFloat *foa = &(light->getFallOffAngle()); + const COLLADAFW::UniqueId &listid = foa->getAnimationList(); + + Assign_float_animations(listid, AnimCurves, "spot_size"); + } + if ((animType->light & LIGHT_FOE) != 0) { + const COLLADAFW::AnimatableFloat *foe = &(light->getFallOffExponent()); + const COLLADAFW::UniqueId &listid = foe->getAnimationList(); + + Assign_float_animations(listid, AnimCurves, "spot_blend"); + } + } + } + + if (animType->camera != 0) { + + Camera *cam = (Camera *)ob->data; + if (!cam->adt || !cam->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&cam->id); + } + else { + act = cam->adt->action; + } + + ListBase *AnimCurves = &(act->curves); + const COLLADAFW::InstanceCameraPointerArray &nodeCameras = node->getInstanceCameras(); + + for (unsigned int i = 0; i < nodeCameras.getCount(); i++) { + const COLLADAFW::Camera *camera = (COLLADAFW::Camera *) + FW_object_map[nodeCameras[i]->getInstanciatedObjectId()]; + + if ((animType->camera & CAMERA_XFOV) != 0) { + const COLLADAFW::AnimatableFloat *xfov = &(camera->getXFov()); + const COLLADAFW::UniqueId &listid = xfov->getAnimationList(); + double aspect = get_aspect_ratio(camera); + Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_XFOV); + } + + else if ((animType->camera & CAMERA_YFOV) != 0) { + const COLLADAFW::AnimatableFloat *yfov = &(camera->getYFov()); + const COLLADAFW::UniqueId &listid = yfov->getAnimationList(); + double aspect = get_aspect_ratio(camera); + Assign_lens_animations(listid, AnimCurves, aspect, cam, "lens", CAMERA_YFOV); + } + + else if ((animType->camera & CAMERA_XMAG) != 0) { + const COLLADAFW::AnimatableFloat *xmag = &(camera->getXMag()); + const COLLADAFW::UniqueId &listid = xmag->getAnimationList(); + Assign_float_animations(listid, AnimCurves, "ortho_scale"); + } + + else if ((animType->camera & CAMERA_YMAG) != 0) { + const COLLADAFW::AnimatableFloat *ymag = &(camera->getYMag()); + const COLLADAFW::UniqueId &listid = ymag->getAnimationList(); + Assign_float_animations(listid, AnimCurves, "ortho_scale"); + } + + if ((animType->camera & CAMERA_ZFAR) != 0) { + const COLLADAFW::AnimatableFloat *zfar = &(camera->getFarClippingPlane()); + const COLLADAFW::UniqueId &listid = zfar->getAnimationList(); + Assign_float_animations(listid, AnimCurves, "clip_end"); + } + + if ((animType->camera & CAMERA_ZNEAR) != 0) { + const COLLADAFW::AnimatableFloat *znear = &(camera->getNearClippingPlane()); + const COLLADAFW::UniqueId &listid = znear->getAnimationList(); + Assign_float_animations(listid, AnimCurves, "clip_start"); + } + } + } + if (animType->material != 0) { + + Material *ma = BKE_object_material_get(ob, 1); + if (!ma->adt || !ma->adt->action) { + act = ED_id_action_ensure(bmain, (ID *)&ma->id); + } + else { + act = ma->adt->action; + } + + const COLLADAFW::InstanceGeometryPointerArray &nodeGeoms = node->getInstanceGeometries(); + for (unsigned int i = 0; i < nodeGeoms.getCount(); i++) { + const COLLADAFW::MaterialBindingArray &matBinds = nodeGeoms[i]->getMaterialBindings(); + for (unsigned int j = 0; j < matBinds.getCount(); j++) { + const COLLADAFW::UniqueId &matuid = matBinds[j].getReferencedMaterial(); + const COLLADAFW::Effect *ef = (COLLADAFW::Effect *)(FW_object_map[matuid]); + if (ef != NULL) { /* can be NULL [#28909] */ + Material *ma = uid_material_map[matuid]; + if (!ma) { + fprintf(stderr, + "Collada: Node %s refers to undefined material\n", + node->getName().c_str()); + continue; + } + ListBase &AnimCurves = get_animation_curves(bmain, ma); + const COLLADAFW::CommonEffectPointerArray &commonEffects = ef->getCommonEffects(); + COLLADAFW::EffectCommon *efc = commonEffects[0]; + if ((animType->material & MATERIAL_SHININESS) != 0) { + const COLLADAFW::FloatOrParam *shin = &(efc->getShininess()); + const COLLADAFW::UniqueId &listid = shin->getAnimationList(); + Assign_float_animations(listid, &AnimCurves, "specular_hardness"); + } + + if ((animType->material & MATERIAL_IOR) != 0) { + const COLLADAFW::FloatOrParam *ior = &(efc->getIndexOfRefraction()); + const COLLADAFW::UniqueId &listid = ior->getAnimationList(); + Assign_float_animations(listid, &AnimCurves, "raytrace_transparency.ior"); + } + + if ((animType->material & MATERIAL_SPEC_COLOR) != 0) { + const COLLADAFW::ColorOrTexture *cot = &(efc->getSpecular()); + const COLLADAFW::UniqueId &listid = cot->getColor().getAnimationList(); + Assign_color_animations(listid, &AnimCurves, "specular_color"); + } + + if ((animType->material & MATERIAL_DIFF_COLOR) != 0) { + const COLLADAFW::ColorOrTexture *cot = &(efc->getDiffuse()); + const COLLADAFW::UniqueId &listid = cot->getColor().getAnimationList(); + Assign_color_animations(listid, &AnimCurves, "diffuse_color"); + } + } + } + } + } + + delete animType; +} + +void AnimationImporter::add_bone_animation_sampled(Object *ob, + std::vector &animcurves, + COLLADAFW::Node *root, + COLLADAFW::Node *node, + COLLADAFW::Transformation *tm) +{ + const char *bone_name = bc_get_joint_name(node); + char joint_path[200]; + armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); + + std::vector frames; + find_frames(&frames, &animcurves); + + /* convert degrees to radians */ + if (tm->getTransformationType() == COLLADAFW::Transformation::ROTATE) { + + std::vector::iterator iter; + for (iter = animcurves.begin(); iter != animcurves.end(); iter++) { + FCurve *fcu = *iter; + + fcurve_deg_to_rad(fcu); + } + } + + float irest_dae[4][4]; + float rest[4][4], irest[4][4]; + + get_joint_rest_mat(irest_dae, root, node); + invert_m4(irest_dae); + + Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); + if (!bone) { + fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); + return; + } + + unit_m4(rest); + copy_m4_m4(rest, bone->arm_mat); + invert_m4_m4(irest, rest); + + /* new curves to assign matrix transform animation */ + FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale. */ + unsigned int totcu = 10; + const char *tm_str = NULL; + char rna_path[200]; + for (int i = 0; i < totcu; i++) { + + int axis = i; + + if (i < 4) { + tm_str = "rotation_quaternion"; + axis = i; + } + else if (i < 7) { + tm_str = "location"; + axis = i - 4; + } + else { + tm_str = "scale"; + axis = i - 7; + } + + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); + + newcu[i] = create_fcurve(axis, rna_path); + newcu[i]->totvert = frames.size(); + } + + if (frames.size() == 0) { + return; + } + + std::sort(frames.begin(), frames.end()); + + BCQuat qref; + + std::vector::iterator it; + + /* sample values at each frame */ + for (it = frames.begin(); it != frames.end(); it++) { + float fra = *it; + + Matrix mat; + Matrix matfra; + + unit_m4(matfra); + + /* calc object-space mat */ + evaluate_transform_at_frame(matfra, node, fra); + + /* for joints, we need a special matrix + * special matrix: iR * M * iR_dae * R + * where R, iR are bone rest and inverse rest mats in world space (Blender bones), + * iR_dae is joint inverse rest matrix (DAE) + * and M is an evaluated joint world-space matrix (DAE). */ + Matrix temp, par; + + /* calc M */ + calc_joint_parent_mat_rest(par, NULL, root, node); + mul_m4_m4m4(temp, par, matfra); + + /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ + + /* calc special matrix */ + mul_m4_series(mat, irest, temp, irest_dae, rest); + + Vector loc, scale; + + qref.rotate_to(mat); + + copy_v3_v3(loc, mat[3]); + mat4_to_size(scale, mat); + + /* add keys */ + for (int i = 0; i < totcu; i++) { + if (i < 4) { + add_bezt(newcu[i], fra, qref.quat()[i]); + } + else if (i < 7) { + add_bezt(newcu[i], fra, loc[i - 4]); + } + else { + add_bezt(newcu[i], fra, scale[i - 7]); + } + } + } + Main *bmain = CTX_data_main(mContext); + ED_id_action_ensure(bmain, (ID *)&ob->id); + + /* add curves */ + for (int i = 0; i < totcu; i++) { + add_bone_fcurve(ob, node, newcu[i]); +#if 0 + fcurve_is_used(newcu[i]); /* never added to unused */ +#endif + } + + bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); + chan->rotmode = ROT_MODE_QUAT; +} + +/* Check if object is animated by checking if animlist_map + * holds the animlist_id of node transforms */ +AnimationImporter::AnimMix *AnimationImporter::get_animation_type( + const COLLADAFW::Node *node, + std::map FW_object_map) +{ + AnimMix *types = new AnimMix(); + + const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); + + /* for each transformation in node */ + for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { + COLLADAFW::Transformation *transform = nodeTransforms[i]; + const COLLADAFW::UniqueId &listid = transform->getAnimationList(); + + /* check if transformation has animations */ + if (animlist_map.find(listid) == animlist_map.end()) { + continue; + } + else { + types->transform = types->transform | BC_NODE_TRANSFORM; + break; + } + } + const COLLADAFW::InstanceLightPointerArray &nodeLights = node->getInstanceLights(); + + for (unsigned int i = 0; i < nodeLights.getCount(); i++) { + const COLLADAFW::Light *light = (COLLADAFW::Light *) + FW_object_map[nodeLights[i]->getInstanciatedObjectId()]; + types->light = setAnimType(&(light->getColor()), (types->light), LIGHT_COLOR); + types->light = setAnimType(&(light->getFallOffAngle()), (types->light), LIGHT_FOA); + types->light = setAnimType(&(light->getFallOffExponent()), (types->light), LIGHT_FOE); + + if (types->light != 0) { + break; + } + } + + const COLLADAFW::InstanceCameraPointerArray &nodeCameras = node->getInstanceCameras(); + for (unsigned int i = 0; i < nodeCameras.getCount(); i++) { + const COLLADAFW::Camera *camera = (COLLADAFW::Camera *) + FW_object_map[nodeCameras[i]->getInstanciatedObjectId()]; + if (camera == NULL) { + /* Can happen if the node refers to an unknown camera. */ + continue; + } + + const bool is_perspective_type = camera->getCameraType() == COLLADAFW::Camera::PERSPECTIVE; + + int addition; + const COLLADAFW::Animatable *mag; + const COLLADAFW::UniqueId listid = camera->getYMag().getAnimationList(); + if (animlist_map.find(listid) != animlist_map.end()) { + mag = &(camera->getYMag()); + addition = (is_perspective_type) ? CAMERA_YFOV : CAMERA_YMAG; + } + else { + mag = &(camera->getXMag()); + addition = (is_perspective_type) ? CAMERA_XFOV : CAMERA_XMAG; + } + types->camera = setAnimType(mag, (types->camera), addition); + + types->camera = setAnimType(&(camera->getFarClippingPlane()), (types->camera), CAMERA_ZFAR); + types->camera = setAnimType(&(camera->getNearClippingPlane()), (types->camera), CAMERA_ZNEAR); + + if (types->camera != 0) { + break; + } + } + + const COLLADAFW::InstanceGeometryPointerArray &nodeGeoms = node->getInstanceGeometries(); + for (unsigned int i = 0; i < nodeGeoms.getCount(); i++) { + const COLLADAFW::MaterialBindingArray &matBinds = nodeGeoms[i]->getMaterialBindings(); + for (unsigned int j = 0; j < matBinds.getCount(); j++) { + const COLLADAFW::UniqueId &matuid = matBinds[j].getReferencedMaterial(); + const COLLADAFW::Effect *ef = (COLLADAFW::Effect *)(FW_object_map[matuid]); + if (ef != NULL) { /* can be NULL [#28909] */ + const COLLADAFW::CommonEffectPointerArray &commonEffects = ef->getCommonEffects(); + if (!commonEffects.empty()) { + COLLADAFW::EffectCommon *efc = commonEffects[0]; + types->material = setAnimType( + &(efc->getShininess()), (types->material), MATERIAL_SHININESS); + types->material = setAnimType( + &(efc->getSpecular().getColor()), (types->material), MATERIAL_SPEC_COLOR); + types->material = setAnimType( + &(efc->getDiffuse().getColor()), (types->material), MATERIAL_DIFF_COLOR); +#if 0 + types->material = setAnimType(&(efc->get()), (types->material), MATERIAL_TRANSPARENCY); +#endif + types->material = setAnimType( + &(efc->getIndexOfRefraction()), (types->material), MATERIAL_IOR); + } + } + } + } + return types; +} + +int AnimationImporter::setAnimType(const COLLADAFW::Animatable *prop, int types, int addition) +{ + int anim_type; + const COLLADAFW::UniqueId &listid = prop->getAnimationList(); + if (animlist_map.find(listid) != animlist_map.end()) { + anim_type = types | addition; + } + else { + anim_type = types; + } + + return anim_type; +} + +/* Is not used anymore. */ +void AnimationImporter::find_frames_old(std::vector *frames, + COLLADAFW::Node *node, + COLLADAFW::Transformation::TransformationType tm_type) +{ + bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; + bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; + /* for each , , etc. there is a separate Transformation */ + const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); + + unsigned int i; + /* find frames at which to sample plus convert all rotation keys to radians */ + for (i = 0; i < nodeTransforms.getCount(); i++) { + COLLADAFW::Transformation *transform = nodeTransforms[i]; + COLLADAFW::Transformation::TransformationType nodeTmType = transform->getTransformationType(); + + if (nodeTmType == tm_type) { + /* get animation bindings for the current transformation */ + const COLLADAFW::UniqueId &listid = transform->getAnimationList(); + /* if transform is animated its animlist must exist. */ + if (animlist_map.find(listid) != animlist_map.end()) { + + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings &bindings = + animlist->getAnimationBindings(); + + if (bindings.getCount()) { + /* for each AnimationBinding get the fcurves which animate the transform */ + for (unsigned int j = 0; j < bindings.getCount(); j++) { + std::vector &curves = curve_map[bindings[j].animation]; + bool xyz = ((nodeTmType == COLLADAFW::Transformation::TRANSLATE || + nodeTmType == COLLADAFW::Transformation::SCALE) && + bindings[j].animationClass == COLLADAFW::AnimationList::POSITION_XYZ); + + if ((!xyz && curves.size() == 1) || (xyz && curves.size() == 3) || is_matrix) { + std::vector::iterator iter; + + for (iter = curves.begin(); iter != curves.end(); iter++) { + FCurve *fcu = *iter; + + /* if transform is rotation the fcurves values must be turned in to radian. */ + if (is_rotation) { + fcurve_deg_to_rad(fcu); + } + + for (unsigned int k = 0; k < fcu->totvert; k++) { + /* get frame value from bezTriple */ + float fra = fcu->bezt[k].vec[1][0]; + /* if frame already not added add frame to frames */ + if (std::find(frames->begin(), frames->end(), fra) == frames->end()) { + frames->push_back(fra); + } + } + } + } + else { + fprintf(stderr, "expected %d curves, got %d\n", xyz ? 3 : 1, (int)curves.size()); + } + } + } + } + } + } +} + +/* prerequisites: + * animlist_map - map animlist id -> animlist + * curve_map - map anim id -> curve(s) */ +Object *AnimationImporter::translate_animation_OLD( + COLLADAFW::Node *node, + std::map &object_map, + std::map &root_map, + COLLADAFW::Transformation::TransformationType tm_type, + Object *par_job) +{ + + bool is_rotation = tm_type == COLLADAFW::Transformation::ROTATE; + bool is_matrix = tm_type == COLLADAFW::Transformation::MATRIX; + bool is_joint = node->getType() == COLLADAFW::Node::JOINT; + + COLLADAFW::Node *root = root_map.find(node->getUniqueId()) == root_map.end() ? + node : + root_map[node->getUniqueId()]; + Object *ob = is_joint ? armature_importer->get_armature_for_joint(node) : + object_map[node->getUniqueId()]; + const char *bone_name = is_joint ? bc_get_joint_name(node) : NULL; + if (!ob) { + fprintf(stderr, "cannot find Object for Node with id=\"%s\"\n", node->getOriginalId().c_str()); + return NULL; + } + + /* frames at which to sample */ + std::vector frames; + + find_frames_old(&frames, node, tm_type); + + unsigned int i; + + float irest_dae[4][4]; + float rest[4][4], irest[4][4]; + + if (is_joint) { + get_joint_rest_mat(irest_dae, root, node); + invert_m4(irest_dae); + + Bone *bone = BKE_armature_find_bone_name((bArmature *)ob->data, bone_name); + if (!bone) { + fprintf(stderr, "cannot find bone \"%s\"\n", bone_name); + return NULL; + } + + unit_m4(rest); + copy_m4_m4(rest, bone->arm_mat); + invert_m4_m4(irest, rest); + } + + Object *job = NULL; + +#ifdef ARMATURE_TEST + FCurve *job_curves[10]; + job = get_joint_object(root, node, par_job); +#endif + + if (frames.size() == 0) { + return job; + } + + std::sort(frames.begin(), frames.end()); + + const char *tm_str = NULL; + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + tm_str = "rotation_quaternion"; + break; + case COLLADAFW::Transformation::SCALE: + tm_str = "scale"; + break; + case COLLADAFW::Transformation::TRANSLATE: + tm_str = "location"; + break; + case COLLADAFW::Transformation::MATRIX: + break; + default: + return job; + } + + char rna_path[200]; + char joint_path[200]; + + if (is_joint) { + armature_importer->get_rna_path_for_joint(node, joint_path, sizeof(joint_path)); + } + + /* new curves */ + FCurve *newcu[10]; /* if tm_type is matrix, then create 10 curves: 4 rot, 3 loc, 3 scale */ + unsigned int totcu = is_matrix ? 10 : (is_rotation ? 4 : 3); + + for (i = 0; i < totcu; i++) { + + int axis = i; + + if (is_matrix) { + if (i < 4) { + tm_str = "rotation_quaternion"; + axis = i; + } + else if (i < 7) { + tm_str = "location"; + axis = i - 4; + } + else { + tm_str = "scale"; + axis = i - 7; + } + } + + if (is_joint) { + BLI_snprintf(rna_path, sizeof(rna_path), "%s.%s", joint_path, tm_str); + } + else { + BLI_strncpy(rna_path, tm_str, sizeof(rna_path)); + } + newcu[i] = create_fcurve(axis, rna_path); + +#ifdef ARMATURE_TEST + if (is_joint) { + job_curves[i] = create_fcurve(axis, tm_str); + } +#endif + } + + std::vector::iterator it; + + /* sample values at each frame */ + for (it = frames.begin(); it != frames.end(); it++) { + float fra = *it; + + float mat[4][4]; + float matfra[4][4]; + + unit_m4(matfra); + + /* calc object-space mat */ + evaluate_transform_at_frame(matfra, node, fra); + + /* for joints, we need a special matrix */ + if (is_joint) { + /* special matrix: iR * M * iR_dae * R + * where R, iR are bone rest and inverse rest mats in world space (Blender bones), + * iR_dae is joint inverse rest matrix (DAE) + * and M is an evaluated joint world-space matrix (DAE). */ + float temp[4][4], par[4][4]; + + /* calc M */ + calc_joint_parent_mat_rest(par, NULL, root, node); + mul_m4_m4m4(temp, par, matfra); + + /* evaluate_joint_world_transform_at_frame(temp, NULL, node, fra); */ + + /* calc special matrix */ + mul_m4_series(mat, irest, temp, irest_dae, rest); + } + else { + copy_m4_m4(mat, matfra); + } + + float val[4] = {}; + float rot[4], loc[3], scale[3]; + + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + mat4_to_quat(val, mat); + break; + case COLLADAFW::Transformation::SCALE: + mat4_to_size(val, mat); + break; + case COLLADAFW::Transformation::TRANSLATE: + copy_v3_v3(val, mat[3]); + break; + case COLLADAFW::Transformation::MATRIX: + mat4_to_quat(rot, mat); + copy_v3_v3(loc, mat[3]); + mat4_to_size(scale, mat); + break; + default: + break; + } + + /* add keys */ + for (i = 0; i < totcu; i++) { + if (is_matrix) { + if (i < 4) { + add_bezt(newcu[i], fra, rot[i]); + } + else if (i < 7) { + add_bezt(newcu[i], fra, loc[i - 4]); + } + else { + add_bezt(newcu[i], fra, scale[i - 7]); + } + } + else { + add_bezt(newcu[i], fra, val[i]); + } + } + +#ifdef ARMATURE_TEST + if (is_joint) { + switch (tm_type) { + case COLLADAFW::Transformation::ROTATE: + mat4_to_quat(val, matfra); + break; + case COLLADAFW::Transformation::SCALE: + mat4_to_size(val, matfra); + break; + case COLLADAFW::Transformation::TRANSLATE: + copy_v3_v3(val, matfra[3]); + break; + case MATRIX: + mat4_to_quat(rot, matfra); + copy_v3_v3(loc, matfra[3]); + mat4_to_size(scale, matfra); + break; + default: + break; + } + + for (i = 0; i < totcu; i++) { + if (is_matrix) { + if (i < 4) { + add_bezt(job_curves[i], fra, rot[i]); + } + else if (i < 7) { + add_bezt(job_curves[i], fra, loc[i - 4]); + } + else { + add_bezt(job_curves[i], fra, scale[i - 7]); + } + } + else { + add_bezt(job_curves[i], fra, val[i]); + } + } + } +#endif + } + Main *bmain = CTX_data_main(mContext); + ED_id_action_ensure(bmain, (ID *)&ob->id); + + ListBase *curves = &ob->adt->action->curves; + + /* add curves */ + for (i = 0; i < totcu; i++) { + if (is_joint) { + add_bone_fcurve(ob, node, newcu[i]); + } + else { + BLI_addtail(curves, newcu[i]); + } + +#ifdef ARMATURE_TEST + if (is_joint) { + BLI_addtail(&job->adt->action->curves, job_curves[i]); + } +#endif + } + + if (is_rotation || is_matrix) { + if (is_joint) { + bPoseChannel *chan = BKE_pose_channel_find_name(ob->pose, bone_name); + chan->rotmode = (is_matrix) ? ROT_MODE_QUAT : ROT_MODE_EUL; + } + else { + ob->rotmode = (is_matrix) ? ROT_MODE_QUAT : ROT_MODE_EUL; + } + } + + return job; +} + +/* internal, better make it private + * warning: evaluates only rotation and only assigns matrix transforms now + * prerequisites: animlist_map, curve_map */ +void AnimationImporter::evaluate_transform_at_frame(float mat[4][4], + COLLADAFW::Node *node, + float fra) +{ + const COLLADAFW::TransformationPointerArray &tms = node->getTransformations(); + + unit_m4(mat); + + for (unsigned int i = 0; i < tms.getCount(); i++) { + COLLADAFW::Transformation *tm = tms[i]; + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + float m[4][4]; + + unit_m4(m); + + std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId(); + if (!evaluate_animation(tm, m, fra, nodename.c_str())) { + switch (type) { + case COLLADAFW::Transformation::ROTATE: + dae_rotate_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::TRANSLATE: + dae_translate_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::SCALE: + dae_scale_to_mat4(tm, m); + break; + case COLLADAFW::Transformation::MATRIX: + dae_matrix_to_mat4(tm, m); + break; + default: + fprintf(stderr, "unsupported transformation type %d\n", type); + } + } + + float temp[4][4]; + copy_m4_m4(temp, mat); + + mul_m4_m4m4(mat, temp, m); + } +} + +static void report_class_type_unsupported(const char *path, + const COLLADAFW::AnimationList::AnimationClass animclass, + const COLLADAFW::Transformation::TransformationType type) +{ + if (animclass == COLLADAFW::AnimationList::UNKNOWN_CLASS) { + fprintf(stderr, "%s: UNKNOWN animation class\n", path); + } + else { + fprintf(stderr, + "%s: animation class %d is not supported yet for transformation type %d\n", + path, + animclass, + type); + } +} + +/* return true to indicate that mat contains a sane value */ +bool AnimationImporter::evaluate_animation(COLLADAFW::Transformation *tm, + float mat[4][4], + float fra, + const char *node_id) +{ + const COLLADAFW::UniqueId &listid = tm->getAnimationList(); + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + + if (type != COLLADAFW::Transformation::ROTATE && type != COLLADAFW::Transformation::SCALE && + type != COLLADAFW::Transformation::TRANSLATE && type != COLLADAFW::Transformation::MATRIX) { + fprintf(stderr, "animation of transformation %d is not supported yet\n", type); + return false; + } + + if (animlist_map.find(listid) == animlist_map.end()) { + return false; + } + + const COLLADAFW::AnimationList *animlist = animlist_map[listid]; + const COLLADAFW::AnimationList::AnimationBindings &bindings = animlist->getAnimationBindings(); + + if (bindings.getCount()) { + float vec[3]; + + bool is_scale = (type == COLLADAFW::Transformation::SCALE); + bool is_translate = (type == COLLADAFW::Transformation::TRANSLATE); + + if (is_scale) { + dae_scale_to_v3(tm, vec); + } + else if (is_translate) { + dae_translate_to_v3(tm, vec); + } + + for (unsigned int index = 0; index < bindings.getCount(); index++) { + const COLLADAFW::AnimationList::AnimationBinding &binding = bindings[index]; + std::vector &curves = curve_map[binding.animation]; + COLLADAFW::AnimationList::AnimationClass animclass = binding.animationClass; + char path[100]; + + switch (type) { + case COLLADAFW::Transformation::ROTATE: + BLI_snprintf(path, sizeof(path), "%s.rotate (binding %u)", node_id, index); + break; + case COLLADAFW::Transformation::SCALE: + BLI_snprintf(path, sizeof(path), "%s.scale (binding %u)", node_id, index); + break; + case COLLADAFW::Transformation::TRANSLATE: + BLI_snprintf(path, sizeof(path), "%s.translate (binding %u)", node_id, index); + break; + case COLLADAFW::Transformation::MATRIX: + BLI_snprintf(path, sizeof(path), "%s.matrix (binding %u)", node_id, index); + break; + default: + break; + } + + if (type == COLLADAFW::Transformation::ROTATE) { + if (curves.size() != 1) { + fprintf(stderr, "expected 1 curve, got %d\n", (int)curves.size()); + return false; + } + + /* TODO support other animclasses */ + if (animclass != COLLADAFW::AnimationList::ANGLE) { + report_class_type_unsupported(path, animclass, type); + return false; + } + + COLLADABU::Math::Vector3 &axis = ((COLLADAFW::Rotate *)tm)->getRotationAxis(); + + float ax[3] = {(float)axis[0], (float)axis[1], (float)axis[2]}; + float angle = evaluate_fcurve(curves[0], fra); + axis_angle_to_mat4(mat, ax, angle); + + return true; + } + else if (is_scale || is_translate) { + bool is_xyz = animclass == COLLADAFW::AnimationList::POSITION_XYZ; + + if ((!is_xyz && curves.size() != 1) || (is_xyz && curves.size() != 3)) { + if (is_xyz) { + fprintf(stderr, "%s: expected 3 curves, got %d\n", path, (int)curves.size()); + } + else { + fprintf(stderr, "%s: expected 1 curve, got %d\n", path, (int)curves.size()); + } + return false; + } + + switch (animclass) { + case COLLADAFW::AnimationList::POSITION_X: + vec[0] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_Y: + vec[1] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_Z: + vec[2] = evaluate_fcurve(curves[0], fra); + break; + case COLLADAFW::AnimationList::POSITION_XYZ: + vec[0] = evaluate_fcurve(curves[0], fra); + vec[1] = evaluate_fcurve(curves[1], fra); + vec[2] = evaluate_fcurve(curves[2], fra); + break; + default: + report_class_type_unsupported(path, animclass, type); + break; + } + } + else if (type == COLLADAFW::Transformation::MATRIX) { + /* for now, of matrix animation, + * support only the case when all values are packed into one animation */ + if (curves.size() != 16) { + fprintf(stderr, "%s: expected 16 curves, got %d\n", path, (int)curves.size()); + return false; + } + + COLLADABU::Math::Matrix4 matrix; + int mi = 0, mj = 0; + + for (std::vector::iterator it = curves.begin(); it != curves.end(); it++) { + matrix.setElement(mi, mj, evaluate_fcurve(*it, fra)); + mj++; + if (mj == 4) { + mi++; + mj = 0; + } + } + unit_converter->dae_matrix_to_mat4_(mat, matrix); + return true; + } + } + + if (is_scale) { + size_to_mat4(mat, vec); + } + else { + copy_v3_v3(mat[3], vec); + } + + return is_scale || is_translate; + } + + return false; +} + +/* gives a world-space mat of joint at rest position */ +void AnimationImporter::get_joint_rest_mat(float mat[4][4], + COLLADAFW::Node *root, + COLLADAFW::Node *node) +{ + /* if bind mat is not available, + * use "current" node transform, i.e. all those tms listed inside */ + if (!armature_importer->get_joint_bind_mat(mat, node)) { + float par[4][4], m[4][4]; + + calc_joint_parent_mat_rest(par, NULL, root, node); + get_node_mat(m, node, NULL, NULL); + mul_m4_m4m4(mat, par, m); + } +} + +/* gives a world-space mat, end's mat not included */ +bool AnimationImporter::calc_joint_parent_mat_rest(float mat[4][4], + float par[4][4], + COLLADAFW::Node *node, + COLLADAFW::Node *end) +{ + float m[4][4]; + + if (node == end) { + par ? copy_m4_m4(mat, par) : unit_m4(mat); + return true; + } + + /* use bind matrix if available or calc "current" world mat */ + if (!armature_importer->get_joint_bind_mat(m, node)) { + if (par) { + float temp[4][4]; + get_node_mat(temp, node, NULL, NULL); + mul_m4_m4m4(m, par, temp); + } + else { + get_node_mat(m, node, NULL, NULL); + } + } + + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (calc_joint_parent_mat_rest(mat, m, children[i], end)) { + return true; + } + } + + return false; +} + +#ifdef ARMATURE_TEST +Object *AnimationImporter::get_joint_object(COLLADAFW::Node *root, + COLLADAFW::Node *node, + Object *par_job) +{ + if (joint_objects.find(node->getUniqueId()) == joint_objects.end()) { + Object *job = bc_add_object(scene, OB_EMPTY, (char *)get_joint_name(node)); + + job->lay = BKE_scene_base_find(scene, job)->lay = 2; + + mul_v3_fl(job->scale, 0.5f); + DEG_id_tag_update(&job->id, ID_RECALC_TRANSFORM); + + ED_id_action_ensure((ID *)&job->id); + + job->rotmode = ROT_MODE_QUAT; + + float mat[4][4]; + get_joint_rest_mat(mat, root, node); + + if (par_job) { + float temp[4][4], ipar[4][4]; + invert_m4_m4(ipar, par_job->obmat); + copy_m4_m4(temp, mat); + mul_m4_m4m4(mat, ipar, temp); + } + + bc_decompose(mat, job->loc, NULL, job->quat, job->scale); + + if (par_job) { + job->parent = par_job; + + DEG_id_tag_update(&par_job->id, ID_RECALC_TRANSFORM); + job->parsubstr[0] = 0; + } + + BKE_object_where_is_calc(scene, job); + + /* after parenting and layer change */ + DEG_relations_tag_update(CTX_data_main(C)); + + joint_objects[node->getUniqueId()] = job; + } + + return joint_objects[node->getUniqueId()]; +} +#endif + +#if 0 +/* recursively evaluates joint tree until end is found, + * mat then is world-space matrix of end mat must be identity on enter, node must be root. */ +bool AnimationImporter::evaluate_joint_world_transform_at_frame( + float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra) +{ + float m[4][4]; + if (par) { + float temp[4][4]; + evaluate_transform_at_frame(temp, node, node == end ? fra : 0.0f); + mul_m4_m4m4(m, par, temp); + } + else { + evaluate_transform_at_frame(m, node, node == end ? fra : 0.0f); + } + + if (node == end) { + copy_m4_m4(mat, m); + return true; + } + else { + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + if (evaluate_joint_world_transform_at_frame(mat, m, children[i], end, fra)) { + return true; + } + } + } + + return false; +} +#endif + +void AnimationImporter::add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu) +{ + const char *bone_name = bc_get_joint_name(node); + bAction *act = ob->adt->action; + + /* try to find group */ + bActionGroup *grp = BKE_action_group_find_name(act, bone_name); + + /* no matching groups, so add one */ + if (grp == NULL) { + /* Add a new group, and make it active */ + grp = (bActionGroup *)MEM_callocN(sizeof(bActionGroup), "bActionGroup"); + + grp->flag = AGRP_SELECTED; + BLI_strncpy(grp->name, bone_name, sizeof(grp->name)); + + BLI_addtail(&act->groups, grp); + BLI_uniquename(&act->groups, + grp, + CTX_DATA_(BLT_I18NCONTEXT_ID_ACTION, "Group"), + '.', + offsetof(bActionGroup, name), + 64); + } + + /* add F-Curve to group */ + action_groups_add_channel(act, grp, fcu); +} + +void AnimationImporter::set_import_from_version(std::string import_from_version) +{ + this->import_from_version = import_from_version; +} diff --git a/source/blender/io/collada/AnimationImporter.h b/source/blender/io/collada/AnimationImporter.h new file mode 100644 index 00000000000..0043dad7116 --- /dev/null +++ b/source/blender/io/collada/AnimationImporter.h @@ -0,0 +1,254 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __ANIMATIONIMPORTER_H__ +#define __ANIMATIONIMPORTER_H__ + +#include +#include + +#include "COLLADAFWAnimation.h" +#include "COLLADAFWAnimationCurve.h" +#include "COLLADAFWAnimationList.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWUniqueId.h" +#include "COLLADAFWLight.h" +#include "COLLADAFWCamera.h" +#include "COLLADAFWMaterial.h" +#include "COLLADAFWEffect.h" +#include "COLLADAFWInstanceGeometry.h" + +extern "C" { +#include "BKE_context.h" +#include "DNA_anim_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_light_types.h" +#include "DNA_camera_types.h" +} + +//#include "ArmatureImporter.h" +#include "TransformReader.h" + +#include "collada_internal.h" + +class ArmatureImporter; + +class AnimationImporterBase { + public: + // virtual void change_eul_to_quat(Object *ob, bAction *act) = 0; +}; + +class AnimationImporter : private TransformReader, public AnimationImporterBase { + private: + bContext *mContext; + ArmatureImporter *armature_importer; + Scene *scene; + + std::map> curve_map; + std::map uid_animated_map; + // std::map > fcurves_actionGroup_map; + std::map animlist_map; + std::vector unused_curves; + std::map joint_objects; + + FCurve *create_fcurve(int array_index, const char *rna_path); + + void add_bezt(FCurve *fcu, + float frame, + float value, + eBezTriple_Interpolation ipo = BEZT_IPO_LIN); + + // create one or several fcurves depending on the number of parameters being animated + void animation_to_fcurves(COLLADAFW::AnimationCurve *curve); + + void fcurve_deg_to_rad(FCurve *cu); + void fcurve_scale(FCurve *cu, int scale); + + void fcurve_is_used(FCurve *fcu); + + void add_fcurves_to_object(Main *bmain, + Object *ob, + std::vector &curves, + char *rna_path, + int array_index, + Animation *animated); + + int typeFlag; + + std::string import_from_version; + + enum lightAnim { + // INANIMATE = 0, + LIGHT_COLOR = 2, + LIGHT_FOA = 4, + LIGHT_FOE = 8, + }; + + enum cameraAnim { + // INANIMATE = 0, + CAMERA_XFOV = 2, + CAMERA_XMAG = 4, + CAMERA_YFOV = 8, + CAMERA_YMAG = 16, + CAMERA_ZFAR = 32, + CAMERA_ZNEAR = 64, + }; + + enum matAnim { + MATERIAL_SHININESS = 2, + MATERIAL_SPEC_COLOR = 4, + MATERIAL_DIFF_COLOR = 1 << 3, + MATERIAL_TRANSPARENCY = 1 << 4, + MATERIAL_IOR = 1 << 5, + }; + + enum AnimationType { + BC_INANIMATE = 0, + BC_NODE_TRANSFORM = 1, + }; + + struct AnimMix { + int transform; + int light; + int camera; + int material; + int texture; + }; + + public: + AnimationImporter(bContext *C, UnitConverter *conv, ArmatureImporter *arm, Scene *scene) + : TransformReader(conv), mContext(C), armature_importer(arm), scene(scene) + { + } + + ~AnimationImporter(); + + void set_import_from_version(std::string import_from_version); + bool write_animation(const COLLADAFW::Animation *anim); + + // called on post-process stage after writeVisualScenes + bool write_animation_list(const COLLADAFW::AnimationList *animlist); + + void read_node_transform(COLLADAFW::Node *node, Object *ob); +#if 0 + virtual void change_eul_to_quat(Object *ob, bAction *act); +#endif + + void translate_Animations(COLLADAFW::Node *Node, + std::map &root_map, + std::multimap &object_map, + std::map FW_object_map, + std::map uid_material_map); + + AnimMix *get_animation_type( + const COLLADAFW::Node *node, + std::map FW_object_map); + + void apply_matrix_curves(Object *ob, + std::vector &animcurves, + COLLADAFW::Node *root, + COLLADAFW::Node *node, + COLLADAFW::Transformation *tm); + + void add_bone_animation_sampled(Object *ob, + std::vector &animcurves, + COLLADAFW::Node *root, + COLLADAFW::Node *node, + COLLADAFW::Transformation *tm); + + void Assign_transform_animations(COLLADAFW::Transformation *transform, + const COLLADAFW::AnimationList::AnimationBinding *binding, + std::vector *curves, + bool is_joint, + char *joint_path); + + void Assign_color_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const char *anim_type); + void Assign_float_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const char *anim_type); + void Assign_lens_animations(const COLLADAFW::UniqueId &listid, + ListBase *AnimCurves, + const double aspect, + Camera *cam, + const char *anim_type, + int fov_type); + + int setAnimType(const COLLADAFW::Animatable *prop, int type, int addition); + + void modify_fcurve(std::vector *curves, + const char *rna_path, + int array_index, + int scale = 1); + void unused_fcurve(std::vector *curves); + // prerequisites: + // animlist_map - map animlist id -> animlist + // curve_map - map anim id -> curve(s) + Object *translate_animation_OLD(COLLADAFW::Node *node, + std::map &object_map, + std::map &root_map, + COLLADAFW::Transformation::TransformationType tm_type, + Object *par_job = NULL); + + void find_frames(std::vector *frames, std::vector *curves); + void find_frames_old(std::vector *frames, + COLLADAFW::Node *node, + COLLADAFW::Transformation::TransformationType tm_type); + // internal, better make it private + // warning: evaluates only rotation + // prerequisites: animlist_map, curve_map + void evaluate_transform_at_frame(float mat[4][4], COLLADAFW::Node *node, float fra); + + // return true to indicate that mat contains a sane value + bool evaluate_animation(COLLADAFW::Transformation *tm, + float mat[4][4], + float fra, + const char *node_id); + + // gives a world-space mat of joint at rest position + void get_joint_rest_mat(float mat[4][4], COLLADAFW::Node *root, COLLADAFW::Node *node); + + // gives a world-space mat, end's mat not included + bool calc_joint_parent_mat_rest(float mat[4][4], + float par[4][4], + COLLADAFW::Node *node, + COLLADAFW::Node *end); + + float convert_to_focal_length(float in_xfov, int fov_type, float aspect, float sensorx); + +#ifdef ARMATURE_TEST + Object *get_joint_object(COLLADAFW::Node *root, COLLADAFW::Node *node, Object *par_job); +#endif + +#if 0 + // recursively evaluates joint tree until end is found, mat then is world-space matrix of end + // mat must be identity on enter, node must be root + bool evaluate_joint_world_transform_at_frame( + float mat[4][4], float par[4][4], COLLADAFW::Node *node, COLLADAFW::Node *end, float fra); +#endif + + void add_bone_fcurve(Object *ob, COLLADAFW::Node *node, FCurve *fcu); + + void extra_data_importer(std::string elementName); +}; + +#endif diff --git a/source/blender/io/collada/ArmatureExporter.cpp b/source/blender/io/collada/ArmatureExporter.cpp new file mode 100644 index 00000000000..84979cc4ca4 --- /dev/null +++ b/source/blender/io/collada/ArmatureExporter.cpp @@ -0,0 +1,328 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "COLLADASWBaseInputElement.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWSource.h" + +#include "DNA_action_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" + +extern "C" { +#include "BKE_global.h" +#include "BKE_mesh.h" +} + +#include "ED_armature.h" + +#include "BLI_listbase.h" + +#include "GeometryExporter.h" +#include "ArmatureExporter.h" +#include "SceneExporter.h" + +// write bone nodes +void ArmatureExporter::add_armature_bones(Object *ob_arm, + ViewLayer *view_layer, + SceneExporter *se, + std::vector &child_objects) + +{ + // write bone nodes + + bArmature *armature = (bArmature *)ob_arm->data; + bool is_edited = armature->edbo != NULL; + + if (!is_edited) { + ED_armature_to_edit(armature); + } + + for (Bone *bone = (Bone *)armature->bonebase.first; bone; bone = bone->next) { + add_bone_node(bone, ob_arm, se, child_objects); + } + + if (!is_edited) { + ED_armature_edit_free(armature); + } +} + +void ArmatureExporter::write_bone_URLs(COLLADASW::InstanceController &ins, + Object *ob_arm, + Bone *bone) +{ + if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) { + std::string joint_id = translate_id(id_name(ob_arm) + "_" + bone->name); + ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, joint_id)); + } + else { + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + write_bone_URLs(ins, ob_arm, child); + } + } +} + +bool ArmatureExporter::add_instance_controller(Object *ob) +{ + Object *ob_arm = bc_get_assigned_armature(ob); + bArmature *arm = (bArmature *)ob_arm->data; + + const std::string &controller_id = get_controller_id(ob_arm, ob); + + COLLADASW::InstanceController ins(mSW); + ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id)); + + Mesh *me = (Mesh *)ob->data; + if (!me->dvert) { + return false; + } + + // write root bone URLs + Bone *bone; + for (bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { + write_bone_URLs(ins, ob_arm, bone); + } + + InstanceWriter::add_material_bindings( + ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); + + ins.add(); + return true; +} + +#if 0 +void ArmatureExporter::operator()(Object *ob) +{ + Object *ob_arm = bc_get_assigned_armature(ob); +} + +bool ArmatureExporter::already_written(Object *ob_arm) +{ + return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != + written_armatures.end(); +} + +void ArmatureExporter::wrote(Object *ob_arm) +{ + written_armatures.push_back(ob_arm); +} + +void ArmatureExporter::find_objects_using_armature(Object *ob_arm, + std::vector &objects, + Scene *sce) +{ + objects.clear(); + + Base *base = (Base *)sce->base.first; + while (base) { + Object *ob = base->object; + + if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) { + objects.push_back(ob); + } + + base = base->next; + } +} +#endif + +// parent_mat is armature-space +void ArmatureExporter::add_bone_node(Bone *bone, + Object *ob_arm, + SceneExporter *se, + std::vector &child_objects) +{ + if (can_export(bone)) { + std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name); + std::string node_name = std::string(bone->name); + std::string node_sid = get_joint_sid(bone); + + COLLADASW::Node node(mSW); + + node.setType(COLLADASW::Node::JOINT); + node.setNodeId(node_id); + node.setNodeName(node_name); + node.setNodeSid(node_sid); + + if (this->export_settings.get_use_blender_profile()) { + if (!is_export_root(bone)) { + if (bone->flag & BONE_CONNECTED) { + node.addExtraTechniqueParameter("blender", "connect", true); + } + } + std::string layers = BoneExtended::get_bone_layers(bone->layer); + node.addExtraTechniqueParameter("blender", "layer", layers); + + bArmature *armature = (bArmature *)ob_arm->data; + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + if (ebone && ebone->roll != 0) { + node.addExtraTechniqueParameter("blender", "roll", ebone->roll); + } + if (bc_is_leaf_bone(bone)) { + Vector head, tail; + const BCMatrix &global_transform = this->export_settings.get_global_transform(); + if (this->export_settings.get_apply_global_orientation()) { + bc_add_global_transform(head, bone->arm_head, global_transform); + bc_add_global_transform(tail, bone->arm_tail, global_transform); + } + else { + copy_v3_v3(head, bone->arm_head); + copy_v3_v3(tail, bone->arm_tail); + } + node.addExtraTechniqueParameter("blender", "tip_x", tail[0] - head[0]); + node.addExtraTechniqueParameter("blender", "tip_y", tail[1] - head[1]); + node.addExtraTechniqueParameter("blender", "tip_z", tail[2] - head[2]); + } + } + + node.start(); + + add_bone_transform(ob_arm, bone, node); + + // Write nodes of childobjects, remove written objects from list + std::vector::iterator iter = child_objects.begin(); + + while (iter != child_objects.end()) { + Object *ob = *iter; + if (ob->partype == PARBONE && STREQ(ob->parsubstr, bone->name)) { + float backup_parinv[4][4]; + copy_m4_m4(backup_parinv, ob->parentinv); + + // crude, temporary change to parentinv + // so transform gets exported correctly. + + // Add bone tail- translation... don't know why + // bone parenting is against the tail of a bone + // and not it's head, seems arbitrary. + ob->parentinv[3][1] += bone->length; + + // OPEN_SIM_COMPATIBILITY + // TODO: when such objects are animated as + // single matrix the tweak must be applied + // to the result. + if (export_settings.get_open_sim()) { + // tweak objects parentinverse to match compatibility + float temp[4][4]; + + copy_m4_m4(temp, bone->arm_mat); + temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; + + mul_m4_m4m4(ob->parentinv, temp, ob->parentinv); + } + + se->writeNode(ob); + copy_m4_m4(ob->parentinv, backup_parinv); + iter = child_objects.erase(iter); + } + else { + iter++; + } + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + add_bone_node(child, ob_arm, se, child_objects); + } + node.end(); + } + else { + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + add_bone_node(child, ob_arm, se, child_objects); + } + } +} + +bool ArmatureExporter::is_export_root(Bone *bone) +{ + Bone *entry = bone->parent; + while (entry) { + if (can_export(entry)) { + return false; + } + entry = entry->parent; + } + return can_export(bone); +} + +void ArmatureExporter::add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node &node) +{ + // bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone->name); + + float mat[4][4]; + float bone_rest_mat[4][4]; /* derived from bone->arm_mat */ + float parent_rest_mat[4][4]; /* derived from bone->parent->arm_mat */ + + bool has_restmat = bc_get_property_matrix(bone, "rest_mat", mat); + + if (!has_restmat) { + + /* Have no restpose matrix stored, try old style <= Blender 2.78 */ + + bc_create_restpose_mat(this->export_settings, bone, bone_rest_mat, bone->arm_mat, true); + + if (is_export_root(bone)) { + copy_m4_m4(mat, bone_rest_mat); + } + else { + Matrix parent_inverse; + bc_create_restpose_mat( + this->export_settings, bone->parent, parent_rest_mat, bone->parent->arm_mat, true); + + invert_m4_m4(parent_inverse, parent_rest_mat); + mul_m4_m4m4(mat, parent_inverse, bone_rest_mat); + } + + // OPEN_SIM_COMPATIBILITY + + if (export_settings.get_open_sim()) { + // Remove rotations vs armature from transform + // parent_rest_rot * mat * irest_rot + Matrix workmat; + copy_m4_m4(workmat, bone_rest_mat); + + workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f; + invert_m4(workmat); + + mul_m4_m4m4(mat, mat, workmat); + + if (!is_export_root(bone)) { + copy_m4_m4(workmat, parent_rest_mat); + workmat[3][0] = workmat[3][1] = workmat[3][2] = 0.0f; + + mul_m4_m4m4(mat, workmat, mat); + } + } + } + + if (this->export_settings.get_limit_precision()) { + BCMatrix::sanitize(mat, LIMITTED_PRECISION); + } + + TransformWriter::add_joint_transform(node, mat, NULL, this->export_settings, has_restmat); +} + +std::string ArmatureExporter::get_controller_id(Object *ob_arm, Object *ob) +{ + return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + + SKIN_CONTROLLER_ID_SUFFIX; +} diff --git a/source/blender/io/collada/ArmatureExporter.h b/source/blender/io/collada/ArmatureExporter.h new file mode 100644 index 00000000000..da6d6f79ef5 --- /dev/null +++ b/source/blender/io/collada/ArmatureExporter.h @@ -0,0 +1,107 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __ARMATUREEXPORTER_H__ +#define __ARMATUREEXPORTER_H__ + +#include +#include +//#include + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryControllers.h" +#include "COLLADASWInputList.h" +#include "COLLADASWNode.h" + +#include "DNA_armature_types.h" +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_constraint_types.h" +#include "DNA_scene_types.h" + +#include "TransformWriter.h" +#include "InstanceWriter.h" + +#include "ExportSettings.h" + +class SceneExporter; + +// XXX exporter writes wrong data for shared armatures. A separate +// controller should be written for each armature-mesh binding how do +// we make controller ids then? +class ArmatureExporter : public COLLADASW::LibraryControllers, + protected TransformWriter, + protected InstanceWriter { + public: + // XXX exporter writes wrong data for shared armatures. A separate + // controller should be written for each armature-mesh binding how do + // we make controller ids then? + ArmatureExporter(BlenderContext &blender_context, + COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings) + : COLLADASW::LibraryControllers(sw), + blender_context(blender_context), + export_settings(export_settings) + { + } + + void add_armature_bones(Object *ob_arm, + ViewLayer *view_layer, + SceneExporter *se, + std::vector &child_objects); + + bool add_instance_controller(Object *ob); + + private: + BlenderContext &blender_context; + BCExportSettings &export_settings; + +#if 0 + std::vector written_armatures; + + bool already_written(Object *ob_arm); + + void wrote(Object *ob_arm); + + void find_objects_using_armature(Object *ob_arm, std::vector &objects, Scene *sce); +#endif + + // Scene, SceneExporter and the list of child_objects + // are required for writing bone parented objects + void add_bone_node(Bone *bone, + Object *ob_arm, + SceneExporter *se, + std::vector &child_objects); + + inline bool can_export(Bone *bone) + { + return !(export_settings.get_deform_bones_only() && bone->flag & BONE_NO_DEFORM); + } + + bool is_export_root(Bone *bone); + void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node &node); + + std::string get_controller_id(Object *ob_arm, Object *ob); + + void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); +}; + +#endif diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp new file mode 100644 index 00000000000..b8c534f97dd --- /dev/null +++ b/source/blender/io/collada/ArmatureImporter.cpp @@ -0,0 +1,1115 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include + +#include "COLLADAFWUniqueId.h" + +extern "C" { +#include "BKE_action.h" +#include "BKE_object.h" +#include "BKE_armature.h" +#include "BLI_string.h" +#include "BLI_listbase.h" +#include "ED_armature.h" +} + +#include "DEG_depsgraph.h" + +#include "collada_utils.h" +#include "ArmatureImporter.h" + +/* use node name, or fall back to original id if not present (name is optional) */ +template static const char *bc_get_joint_name(T *node) +{ + const std::string &id = node->getName(); + return id.size() ? id.c_str() : node->getOriginalId().c_str(); +} + +ArmatureImporter::ArmatureImporter(UnitConverter *conv, + MeshImporterBase *mesh, + Main *bmain, + Scene *sce, + ViewLayer *view_layer, + const ImportSettings *import_settings) + : TransformReader(conv), + m_bmain(bmain), + scene(sce), + view_layer(view_layer), + unit_converter(conv), + import_settings(import_settings), + empty(NULL), + mesh_importer(mesh) +{ +} + +ArmatureImporter::~ArmatureImporter() +{ + /* free skin controller data if we forget to do this earlier */ + std::map::iterator it; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + it->second.free(); + } +} + +#if 0 +JointData *ArmatureImporter::get_joint_data(COLLADAFW::Node *node); +{ + const COLLADAFW::UniqueId &joint_id = node->getUniqueId(); + + if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) { + fprintf( + stderr, "Cannot find a joint index by joint id for %s.\n", node->getOriginalId().c_str()); + return NULL; + } + + int joint_index = joint_id_to_joint_index_map[joint_id]; + + return &joint_index_to_joint_info_map[joint_index]; +} +#endif + +int ArmatureImporter::create_bone(SkinInfo *skin, + COLLADAFW::Node *node, + EditBone *parent, + int totchild, + float parent_mat[4][4], + bArmature *arm, + std::vector &layer_labels) +{ + float mat[4][4]; + float joint_inv_bind_mat[4][4]; + float joint_bind_mat[4][4]; + int chain_length = 0; + + /* Checking if bone is already made. */ + std::vector::iterator it; + it = std::find(finished_joints.begin(), finished_joints.end(), node); + if (it != finished_joints.end()) { + return chain_length; + } + + EditBone *bone = ED_armature_ebone_add(arm, bc_get_joint_name(node)); + totbone++; + + /* + * We use the inv_bind_shape matrix to apply the armature bind pose as its rest pose. + */ + + std::map::iterator skin_it; + bool bone_is_skinned = false; + for (skin_it = skin_by_data_uid.begin(); skin_it != skin_by_data_uid.end(); skin_it++) { + + SkinInfo *b = &skin_it->second; + if (b->get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) { + + /* get original world-space matrix */ + invert_m4_m4(mat, joint_inv_bind_mat); + copy_m4_m4(joint_bind_mat, mat); + /* And make local to armature */ + Object *ob_arm = skin->BKE_armature_from_object(); + if (ob_arm) { + float invmat[4][4]; + invert_m4_m4(invmat, ob_arm->obmat); + mul_m4_m4m4(mat, invmat, mat); + } + + bone_is_skinned = true; + break; + } + } + + /* create a bone even if there's no joint data for it (i.e. it has no influence) */ + if (!bone_is_skinned) { + get_node_mat(mat, node, NULL, NULL, parent_mat); + } + + if (parent) { + bone->parent = parent; + } + + float loc[3], size[3], rot[3][3]; + BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(arm); + BoneExtended &be = add_bone_extended(bone, node, totchild, layer_labels, extended_bones); + int layer = be.get_bone_layers(); + if (layer) { + bone->layer = layer; + } + arm->layer |= layer; // ensure that all populated bone layers are visible after import + + float *tail = be.get_tail(); + int use_connect = be.get_use_connect(); + + switch (use_connect) { + case 1: + bone->flag |= BONE_CONNECTED; + break; + case -1: /* Connect type not specified */ + case 0: + bone->flag &= ~BONE_CONNECTED; + break; + } + + if (be.has_roll()) { + bone->roll = be.get_roll(); + } + else { + float angle; + mat4_to_loc_rot_size(loc, rot, size, mat); + mat3_to_vec_roll(rot, NULL, &angle); + bone->roll = angle; + } + copy_v3_v3(bone->head, mat[3]); + + if (bone_is_skinned && this->import_settings->keep_bind_info) { + float rest_mat[4][4]; + get_node_mat(rest_mat, node, NULL, NULL, NULL); + bc_set_IDPropertyMatrix(bone, "bind_mat", joint_bind_mat); + bc_set_IDPropertyMatrix(bone, "rest_mat", rest_mat); + } + + add_v3_v3v3(bone->tail, bone->head, tail); /* tail must be non zero */ + + /* find smallest bone length in armature (used later for leaf bone length) */ + if (parent) { + + if (use_connect == 1) { + copy_v3_v3(parent->tail, bone->head); + } + + /* guess reasonable leaf bone length */ + float length = len_v3v3(parent->head, bone->head); + if ((length < leaf_bone_length || totbone == 0) && length > MINIMUM_BONE_LENGTH) { + leaf_bone_length = length; + } + } + + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + + for (unsigned int i = 0; i < children.getCount(); i++) { + int cl = create_bone(skin, children[i], bone, children.getCount(), mat, arm, layer_labels); + if (cl > chain_length) { + chain_length = cl; + } + } + + bone->length = len_v3v3(bone->head, bone->tail); + joint_by_uid[node->getUniqueId()] = node; + finished_joints.push_back(node); + + be.set_chain_length(chain_length + 1); + + return chain_length + 1; +} + +/** + * Collada only knows Joints, hence bones at the end of a bone chain + * don't have a defined length. This function guesses reasonable + * tail locations for the affected bones (nodes which don't have any connected child) + * Hint: The extended_bones set gets populated in ArmatureImporter::create_bone + */ +void ArmatureImporter::fix_leaf_bone_hierarchy(bArmature *armature, + Bone *bone, + bool fix_orientation) +{ + if (bone == NULL) { + return; + } + + if (bc_is_leaf_bone(bone)) { + BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(armature); + BoneExtended *be = extended_bones[bone->name]; + EditBone *ebone = bc_get_edit_bone(armature, bone->name); + fix_leaf_bone(armature, ebone, be, fix_orientation); + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + fix_leaf_bone_hierarchy(armature, child, fix_orientation); + } +} + +void ArmatureImporter::fix_leaf_bone(bArmature *armature, + EditBone *ebone, + BoneExtended *be, + bool fix_orientation) +{ + if (be == NULL || !be->has_tail()) { + + /* Collada only knows Joints, Here we guess a reasonable leaf bone length */ + float leaf_length = (leaf_bone_length == FLT_MAX) ? 1.0 : leaf_bone_length; + + float vec[3]; + + if (fix_orientation && ebone->parent != NULL) { + EditBone *parent = ebone->parent; + sub_v3_v3v3(vec, ebone->head, parent->head); + if (len_squared_v3(vec) < MINIMUM_BONE_LENGTH) { + sub_v3_v3v3(vec, parent->tail, parent->head); + } + } + else { + vec[2] = 0.1f; + sub_v3_v3v3(vec, ebone->tail, ebone->head); + } + + normalize_v3_v3(vec, vec); + mul_v3_fl(vec, leaf_length); + add_v3_v3v3(ebone->tail, ebone->head, vec); + } +} + +void ArmatureImporter::fix_parent_connect(bArmature *armature, Bone *bone) +{ + /* armature has no bones */ + if (bone == NULL) { + return; + } + + if (bone->parent && bone->flag & BONE_CONNECTED) { + copy_v3_v3(bone->parent->tail, bone->head); + } + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + fix_parent_connect(armature, child); + } +} + +void ArmatureImporter::connect_bone_chains(bArmature *armature, Bone *parentbone, int clip) +{ + BoneExtensionMap &extended_bones = bone_extension_manager.getExtensionMap(armature); + BoneExtended *dominant_child = NULL; + int maxlen = 0; + + if (parentbone == NULL) { + return; + } + + Bone *child = (Bone *)parentbone->childbase.first; + if (child && (import_settings->find_chains || child->next == NULL)) { + for (; child; child = child->next) { + BoneExtended *be = extended_bones[child->name]; + if (be != NULL) { + int chain_len = be->get_chain_length(); + if (chain_len <= clip) { + if (chain_len > maxlen) { + dominant_child = be; + maxlen = chain_len; + } + else if (chain_len == maxlen) { + dominant_child = NULL; + } + } + } + } + } + + BoneExtended *pbe = extended_bones[parentbone->name]; + if (dominant_child != NULL) { + /* Found a valid chain. Now connect current bone with that chain.*/ + EditBone *pebone = bc_get_edit_bone(armature, parentbone->name); + EditBone *cebone = bc_get_edit_bone(armature, dominant_child->get_name()); + if (pebone && !(cebone->flag & BONE_CONNECTED)) { + float vec[3]; + sub_v3_v3v3(vec, cebone->head, pebone->head); + + /* + * It is possible that the child's head is located on the parents head. + * When this happens, then moving the parent's tail to the child's head + * would result in a zero sized bone and Blender would silently remove the bone. + * So we move the tail only when the resulting bone has a minimum length: + */ + + if (len_squared_v3(vec) > MINIMUM_BONE_LENGTH) { + copy_v3_v3(pebone->tail, cebone->head); + pbe->set_tail(pebone->tail); /* to make fix_leafbone happy ...*/ + if (pbe && pbe->get_chain_length() >= this->import_settings->min_chain_length) { + + BoneExtended *cbe = extended_bones[cebone->name]; + cbe->set_use_connect(true); + + cebone->flag |= BONE_CONNECTED; + pbe->set_leaf_bone(false); + printf("Connect Bone chain: parent (%s --> %s) child)\n", pebone->name, cebone->name); + } + } + } + for (Bone *ch = (Bone *)parentbone->childbase.first; ch; ch = ch->next) { + ArmatureImporter::connect_bone_chains(armature, ch, UNLIMITED_CHAIN_MAX); + } + } + else if (maxlen > 1 && maxlen > this->import_settings->min_chain_length) { + /* Try again with smaller chain length */ + ArmatureImporter::connect_bone_chains(armature, parentbone, maxlen - 1); + } + else { + /* can't connect this Bone. Proceed with children ... */ + if (pbe) { + pbe->set_leaf_bone(true); + } + for (Bone *ch = (Bone *)parentbone->childbase.first; ch; ch = ch->next) { + ArmatureImporter::connect_bone_chains(armature, ch, UNLIMITED_CHAIN_MAX); + } + } +} + +#if 0 +void ArmatureImporter::set_leaf_bone_shapes(Object *ob_arm) +{ + bPose *pose = ob_arm->pose; + + std::vector::iterator it; + for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) { + LeafBone &leaf = *it; + + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, leaf.name); + if (pchan) { + pchan->custom = get_empty_for_leaves(); + } + else { + fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name); + } + } +} + +void ArmatureImporter::set_euler_rotmode() +{ + /* just set rotmode = ROT_MODE_EUL on pose channel for each joint */ + + std::map::iterator it; + + for (it = joint_by_uid.begin(); it != joint_by_uid.end(); it++) { + + COLLADAFW::Node *joint = it->second; + + std::map::iterator sit; + + for (sit = skin_by_data_uid.begin(); sit != skin_by_data_uid.end(); sit++) { + SkinInfo &skin = sit->second; + + if (skin.uses_joint_or_descendant(joint)) { + bPoseChannel *pchan = skin.get_pose_channel_from_node(joint); + + if (pchan) { + pchan->rotmode = ROT_MODE_EUL; + } + else { + fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint)); + } + + break; + } + } + } +} +#endif + +Object *ArmatureImporter::get_empty_for_leaves() +{ + if (empty) { + return empty; + } + + empty = bc_add_object(m_bmain, scene, view_layer, OB_EMPTY, NULL); + empty->empty_drawtype = OB_EMPTY_SPHERE; + + return empty; +} + +#if 0 +Object *ArmatureImporter::find_armature(COLLADAFW::Node *node) +{ + JointData *jd = get_joint_data(node); + if (jd) { + return jd->ob_arm; + } + + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + for (int i = 0; i < children.getCount(); i++) { + Object *ob_arm = find_armature(children[i]); + if (ob_arm) { + return ob_arm; + } + } + + return NULL; +} + +ArmatureJoints &ArmatureImporter::get_armature_joints(Object *ob_arm) +{ + /* try finding it */ + std::vector::iterator it; + for (it = armature_joints.begin(); it != armature_joints.end(); it++) { + if ((*it).ob_arm == ob_arm) { + return *it; + } + } + + /* not found, create one */ + ArmatureJoints aj; + aj.ob_arm = ob_arm; + armature_joints.push_back(aj); + + return armature_joints.back(); +} +#endif +void ArmatureImporter::create_armature_bones(Main *bmain, std::vector &ob_arms) +{ + std::vector::iterator ri; + std::vector layer_labels; + + /* if there is an armature created for root_joint next root_joint */ + for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { + COLLADAFW::Node *node = *ri; + if (get_armature_for_joint(node) != NULL) { + continue; + } + + Object *ob_arm = joint_parent_map[node->getUniqueId()]; + if (!ob_arm) { + continue; + } + + bArmature *armature = (bArmature *)ob_arm->data; + if (!armature) { + continue; + } + + char *bone_name = (char *)bc_get_joint_name(node); + Bone *bone = BKE_armature_find_bone_name(armature, bone_name); + if (bone) { + fprintf(stderr, + "Reuse of child bone [%s] as root bone in same Armature is not supported.\n", + bone_name); + continue; + } + + ED_armature_to_edit(armature); + armature->layer = 0; // layer is set according to imported bone set in create_bone() + + create_bone(NULL, node, NULL, node->getChildNodes().getCount(), NULL, armature, layer_labels); + if (this->import_settings->find_chains) { + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + } + + /* exit armature edit mode to populate the Armature object */ + ED_armature_from_edit(bmain, armature); + ED_armature_edit_free(armature); + ED_armature_to_edit(armature); + + fix_leaf_bone_hierarchy( + armature, (Bone *)armature->bonebase.first, this->import_settings->fix_orientation); + unskinned_armature_map[node->getUniqueId()] = ob_arm; + + ED_armature_from_edit(bmain, armature); + ED_armature_edit_free(armature); + + set_bone_transformation_type(node, ob_arm); + + int index = std::find(ob_arms.begin(), ob_arms.end(), ob_arm) - ob_arms.begin(); + if (index == 0) { + ob_arms.push_back(ob_arm); + } + + DEG_id_tag_update(&ob_arm->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + } +} + +Object *ArmatureImporter::create_armature_bones(Main *bmain, SkinInfo &skin) +{ + /* just do like so: + * - get armature + * - enter editmode + * - add edit bones and head/tail properties using matrices and parent-child info + * - exit edit mode + * - set a sphere shape to leaf bones */ + + Object *ob_arm = NULL; + + /* + * find if there's another skin sharing at least one bone with this skin + * if so, use that skin's armature + */ + + /** + * Pseudocode: + * + * find_node_in_tree(node, root_joint) + * + * skin::find_root_joints(root_joints): + * std::vector root_joints; + * for each root in root_joints: + * for each joint in joints: + * if find_node_in_tree(joint, root): + * if (std::find(root_joints.begin(), root_joints.end(), root) == + * root_joints.end()) root_joints.push_back(root); + * + * for (each skin B with armature) { + * find all root joints for skin B + * + * for each joint X in skin A: + * for each root joint R in skin B: + * if (find_node_in_tree(X, R)) { + * shared = 1; + * goto endloop; + * } + * } + * + * endloop: + */ + + SkinInfo *a = &skin; + Object *shared = NULL; + std::vector skin_root_joints; + std::vector layer_labels; + + std::map::iterator it; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + SkinInfo *b = &it->second; + if (b == a || b->BKE_armature_from_object() == NULL) { + continue; + } + + skin_root_joints.clear(); + + b->find_root_joints(root_joints, joint_by_uid, skin_root_joints); + + std::vector::iterator ri; + for (ri = skin_root_joints.begin(); ri != skin_root_joints.end(); ri++) { + COLLADAFW::Node *node = *ri; + if (a->uses_joint_or_descendant(node)) { + shared = b->BKE_armature_from_object(); + break; + } + } + + if (shared != NULL) { + break; + } + } + + if (!shared && this->joint_parent_map.size() > 0) { + /* All armatures have been created while creating the Node tree. + * The Collada exporter currently does not create a + * strict relationship between geometries and armatures + * So when we reimport a Blender collada file, then we have + * to guess what is meant. + * XXX This is not safe when we have more than one armatures + * in the import. */ + shared = this->joint_parent_map.begin()->second; + } + + if (shared) { + ob_arm = skin.set_armature(shared); + } + else { + ob_arm = skin.create_armature(m_bmain, scene, view_layer); // once for every armature + } + + /* enter armature edit mode */ + bArmature *armature = (bArmature *)ob_arm->data; + ED_armature_to_edit(armature); + + totbone = 0; + // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on + /* default row */ + + /* create bones */ + /* TODO: + * check if bones have already been created for a given joint */ + + std::vector::iterator ri; + for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { + COLLADAFW::Node *node = *ri; + /* for shared armature check if bone tree is already created */ + if (shared && std::find(skin_root_joints.begin(), skin_root_joints.end(), node) != + skin_root_joints.end()) { + continue; + } + + /* since root_joints may contain joints for multiple controllers, we need to filter */ + if (skin.uses_joint_or_descendant(node)) { + + create_bone( + &skin, node, NULL, node->getChildNodes().getCount(), NULL, armature, layer_labels); + + if (joint_parent_map.find(node->getUniqueId()) != joint_parent_map.end() && + !skin.get_parent()) { + skin.set_parent(joint_parent_map[node->getUniqueId()]); + } + } + } + + /* exit armature edit mode to populate the Armature object */ + ED_armature_from_edit(bmain, armature); + ED_armature_edit_free(armature); + + for (ri = root_joints.begin(); ri != root_joints.end(); ri++) { + COLLADAFW::Node *node = *ri; + set_bone_transformation_type(node, ob_arm); + } + + ED_armature_to_edit(armature); + if (this->import_settings->find_chains) { + connect_bone_chains(armature, (Bone *)armature->bonebase.first, UNLIMITED_CHAIN_MAX); + } + fix_leaf_bone_hierarchy( + armature, (Bone *)armature->bonebase.first, this->import_settings->fix_orientation); + ED_armature_from_edit(bmain, armature); + ED_armature_edit_free(armature); + + DEG_id_tag_update(&ob_arm->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); + + return ob_arm; +} + +void ArmatureImporter::set_bone_transformation_type(const COLLADAFW::Node *node, Object *ob_arm) +{ + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bc_get_joint_name(node)); + if (pchan) { + pchan->rotmode = (node_is_decomposed(node)) ? ROT_MODE_EUL : ROT_MODE_QUAT; + } + + COLLADAFW::NodePointerArray childnodes = node->getChildNodes(); + for (int index = 0; index < childnodes.getCount(); index++) { + node = childnodes[index]; + set_bone_transformation_type(node, ob_arm); + } +} + +void ArmatureImporter::set_pose(Object *ob_arm, + COLLADAFW::Node *root_node, + const char *parentname, + float parent_mat[4][4]) +{ + const char *bone_name = bc_get_joint_name(root_node); + float mat[4][4]; + float obmat[4][4]; + + /* object-space */ + get_node_mat(obmat, root_node, NULL, NULL); + bool is_decomposed = node_is_decomposed(root_node); + + // if (*edbone) + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, bone_name); + pchan->rotmode = (is_decomposed) ? ROT_MODE_EUL : ROT_MODE_QUAT; + + // else fprintf ( "", + + /* get world-space */ + if (parentname) { + mul_m4_m4m4(mat, parent_mat, obmat); + bPoseChannel *parchan = BKE_pose_channel_find_name(ob_arm->pose, parentname); + + mul_m4_m4m4(pchan->pose_mat, parchan->pose_mat, mat); + } + else { + + copy_m4_m4(mat, obmat); + float invObmat[4][4]; + invert_m4_m4(invObmat, ob_arm->obmat); + mul_m4_m4m4(pchan->pose_mat, invObmat, mat); + } + +#if 0 + float angle = 0.0f; + mat4_to_axis_angle(ax, &angle, mat); + pchan->bone->roll = angle; +#endif + + COLLADAFW::NodePointerArray &children = root_node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + set_pose(ob_arm, children[i], bone_name, mat); + } +} + +bool ArmatureImporter::node_is_decomposed(const COLLADAFW::Node *node) +{ + const COLLADAFW::TransformationPointerArray &nodeTransforms = node->getTransformations(); + for (unsigned int i = 0; i < nodeTransforms.getCount(); i++) { + COLLADAFW::Transformation *transform = nodeTransforms[i]; + COLLADAFW::Transformation::TransformationType tm_type = transform->getTransformationType(); + if (tm_type == COLLADAFW::Transformation::MATRIX) { + return false; + } + } + return true; +} + +/** + * root - if this joint is the top joint in hierarchy, if a joint + * is a child of a node (not joint), root should be true since + * this is where we build armature bones from + */ +void ArmatureImporter::add_root_joint(COLLADAFW::Node *node, Object *parent) +{ + root_joints.push_back(node); + if (parent) { + joint_parent_map[node->getUniqueId()] = parent; + } +} + +#if 0 +void ArmatureImporter::add_root_joint(COLLADAFW::Node *node) +{ + // root_joints.push_back(node); + Object *ob_arm = find_armature(node); + if (ob_arm) { + get_armature_joints(ob_arm).root_joints.push_back(node); + } +# ifdef COLLADA_DEBUG + else { + fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node)); + } +# endif +} +#endif + +/* here we add bones to armatures, having armatures previously created in write_controller */ +void ArmatureImporter::make_armatures(bContext *C, std::vector &objects_to_scale) +{ + Main *bmain = CTX_data_main(C); + std::vector ob_arms; + std::map::iterator it; + + /* TODO: Make this work for more than one armature in the import file. */ + leaf_bone_length = FLT_MAX; + + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + + SkinInfo &skin = it->second; + + Object *ob_arm = create_armature_bones(bmain, skin); + + /* link armature with a mesh object */ + const COLLADAFW::UniqueId &uid = skin.get_controller_uid(); + const COLLADAFW::UniqueId *guid = get_geometry_uid(uid); + if (guid != NULL) { + Object *ob = mesh_importer->get_object_by_geom_uid(*guid); + if (ob) { + skin.link_armature(C, ob, joint_by_uid, this); + + std::vector::iterator ob_it = std::find( + objects_to_scale.begin(), objects_to_scale.end(), ob); + + if (ob_it != objects_to_scale.end()) { + int index = ob_it - objects_to_scale.begin(); + objects_to_scale.erase(objects_to_scale.begin() + index); + } + + if (std::find(objects_to_scale.begin(), objects_to_scale.end(), ob_arm) == + objects_to_scale.end()) { + objects_to_scale.push_back(ob_arm); + } + + if (std::find(ob_arms.begin(), ob_arms.end(), ob_arm) == ob_arms.end()) { + ob_arms.push_back(ob_arm); + } + } + else { + fprintf(stderr, "Cannot find object to link armature with.\n"); + } + } + else { + fprintf(stderr, "Cannot find geometry to link armature with.\n"); + } + + /* set armature parent if any */ + Object *par = skin.get_parent(); + if (par) { + bc_set_parent(skin.BKE_armature_from_object(), par, C, false); + } + + /* free memory stolen from SkinControllerData */ + skin.free(); + } + + /* for bones without skins */ + create_armature_bones(bmain, ob_arms); + + /* Fix bone relations */ + std::vector::iterator ob_arm_it; + for (ob_arm_it = ob_arms.begin(); ob_arm_it != ob_arms.end(); ob_arm_it++) { + + Object *ob_arm = *ob_arm_it; + bArmature *armature = (bArmature *)ob_arm->data; + + /* and step back to edit mode to fix the leaf nodes */ + ED_armature_to_edit(armature); + + fix_parent_connect(armature, (Bone *)armature->bonebase.first); + + ED_armature_from_edit(bmain, armature); + ED_armature_edit_free(armature); + } +} + +#if 0 +/* link with meshes, create vertex groups, assign weights */ +void ArmatureImporter::link_armature(Object *ob_arm, + const COLLADAFW::UniqueId &geom_id, + const COLLADAFW::UniqueId &controller_data_id) +{ + Object *ob = mesh_importer->get_object_by_geom_uid(geom_id); + + if (!ob) { + fprintf(stderr, "Cannot find object by geometry UID.\n"); + return; + } + + if (skin_by_data_uid.find(controller_data_id) == skin_by_data_uid.end()) { + fprintf(stderr, "Cannot find skin info by controller data UID.\n"); + return; + } + + SkinInfo &skin = skin_by_data_uid[conroller_data_id]; + + /* create vertex groups */ +} +#endif + +bool ArmatureImporter::write_skin_controller_data(const COLLADAFW::SkinControllerData *data) +{ + /* at this stage we get vertex influence info that should go into me->verts and ob->defbase + * there's no info to which object this should be long so we associate it with + * skin controller data UID. */ + + /* don't forget to call BKE_object_defgroup_unique_name before we copy */ + + /* controller data uid -> [armature] -> joint data, + * [mesh object] */ + + SkinInfo skin(unit_converter); + skin.borrow_skin_controller_data(data); + + /* store join inv bind matrix to use it later in armature construction */ + const COLLADAFW::Matrix4Array &inv_bind_mats = data->getInverseBindMatrices(); + for (unsigned int i = 0; i < data->getJointsCount(); i++) { + skin.add_joint(inv_bind_mats[i]); + } + + skin_by_data_uid[data->getUniqueId()] = skin; + + return true; +} + +bool ArmatureImporter::write_controller(const COLLADAFW::Controller *controller) +{ + /* - create and store armature object */ + const COLLADAFW::UniqueId &con_id = controller->getUniqueId(); + + if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) { + COLLADAFW::SkinController *co = (COLLADAFW::SkinController *)controller; + /* to be able to find geom id by controller id */ + geom_uid_by_controller_uid[con_id] = co->getSource(); + + const COLLADAFW::UniqueId &data_uid = co->getSkinControllerData(); + if (skin_by_data_uid.find(data_uid) == skin_by_data_uid.end()) { + fprintf(stderr, "Cannot find skin by controller data UID.\n"); + return true; + } + + skin_by_data_uid[data_uid].set_controller(co); + } + /* morph controller */ + else if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_MORPH) { + COLLADAFW::MorphController *co = (COLLADAFW::MorphController *)controller; + /* to be able to find geom id by controller id */ + geom_uid_by_controller_uid[con_id] = co->getSource(); + /* Shape keys are applied in DocumentImporter->finish() */ + morph_controllers.push_back(co); + } + + return true; +} + +void ArmatureImporter::make_shape_keys(bContext *C) +{ + Main *bmain = CTX_data_main(C); + std::vector::iterator mc; + float weight; + + for (mc = morph_controllers.begin(); mc != morph_controllers.end(); mc++) { + /* Controller data */ + COLLADAFW::UniqueIdArray &morphTargetIds = (*mc)->getMorphTargets(); + COLLADAFW::FloatOrDoubleArray &morphWeights = (*mc)->getMorphWeights(); + + /* Prereq: all the geometries must be imported and mesh objects must be made */ + Object *source_ob = this->mesh_importer->get_object_by_geom_uid((*mc)->getSource()); + + if (source_ob) { + + Mesh *source_me = (Mesh *)source_ob->data; + /* insert key to source mesh */ + Key *key = source_me->key = BKE_key_add(bmain, (ID *)source_me); + key->type = KEY_RELATIVE; + KeyBlock *kb; + + /* insert basis key */ + kb = BKE_keyblock_add_ctime(key, "Basis", false); + BKE_keyblock_convert_from_mesh(source_me, key, kb); + + /* insert other shape keys */ + for (int i = 0; i < morphTargetIds.getCount(); i++) { + /* better to have a separate map of morph objects, + * This'll do for now since only mesh morphing is imported */ + + Mesh *me = this->mesh_importer->get_mesh_by_geom_uid(morphTargetIds[i]); + + if (me) { + me->key = key; + std::string morph_name = *this->mesh_importer->get_geometry_name(me->id.name); + + kb = BKE_keyblock_add_ctime(key, morph_name.c_str(), false); + BKE_keyblock_convert_from_mesh(me, key, kb); + + /* apply weights */ + weight = morphWeights.getFloatValues()->getData()[i]; + kb->curval = weight; + } + else { + fprintf(stderr, "Morph target geometry not found.\n"); + } + } + } + else { + fprintf(stderr, "Morph target object not found.\n"); + } + } +} + +COLLADAFW::UniqueId *ArmatureImporter::get_geometry_uid(const COLLADAFW::UniqueId &controller_uid) +{ + if (geom_uid_by_controller_uid.find(controller_uid) == geom_uid_by_controller_uid.end()) { + return NULL; + } + + return &geom_uid_by_controller_uid[controller_uid]; +} + +Object *ArmatureImporter::get_armature_for_joint(COLLADAFW::Node *node) +{ + std::map::iterator it; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + SkinInfo &skin = it->second; + + if (skin.uses_joint_or_descendant(node)) { + return skin.BKE_armature_from_object(); + } + } + + std::map::iterator arm; + for (arm = unskinned_armature_map.begin(); arm != unskinned_armature_map.end(); arm++) { + if (arm->first == node->getUniqueId()) { + return arm->second; + } + } + return NULL; +} + +void ArmatureImporter::set_tags_map(TagsMap &tagsMap) +{ + this->uid_tags_map = tagsMap; +} + +void ArmatureImporter::get_rna_path_for_joint(COLLADAFW::Node *node, + char *joint_path, + size_t count) +{ + BLI_snprintf(joint_path, count, "pose.bones[\"%s\"]", bc_get_joint_name(node)); +} + +/* gives a world-space mat */ +bool ArmatureImporter::get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint) +{ + std::map::iterator it; + bool found = false; + for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) { + SkinInfo &skin = it->second; + if ((found = skin.get_joint_inv_bind_matrix(m, joint))) { + invert_m4(m); + break; + } + } + + return found; +} + +BoneExtended &ArmatureImporter::add_bone_extended(EditBone *bone, + COLLADAFW::Node *node, + int sibcount, + std::vector &layer_labels, + BoneExtensionMap &extended_bones) +{ + BoneExtended *be = new BoneExtended(bone); + extended_bones[bone->name] = be; + + TagsMap::iterator etit; + ExtraTags *et = 0; + etit = uid_tags_map.find(node->getUniqueId().toAscii()); + + bool has_connect = false; + int connect_type = -1; + + if (etit != uid_tags_map.end()) { + + float tail[3] = {FLT_MAX, FLT_MAX, FLT_MAX}; + float roll = 0; + std::string layers; + + et = etit->second; + + bool has_tail = false; + has_tail |= et->setData("tip_x", &tail[0]); + has_tail |= et->setData("tip_y", &tail[1]); + has_tail |= et->setData("tip_z", &tail[2]); + + has_connect = et->setData("connect", &connect_type); + bool has_roll = et->setData("roll", &roll); + + layers = et->setData("layer", layers); + + if (has_tail && !has_connect) { + /* got a bone tail definition but no connect info -> bone is not connected */ + has_connect = true; + connect_type = 0; + } + + be->set_bone_layers(layers, layer_labels); + if (has_tail) { + be->set_tail(tail); + } + if (has_roll) { + be->set_roll(roll); + } + } + + if (!has_connect && this->import_settings->auto_connect) { + /* auto connect only whyen parent has exactly one child*/ + connect_type = sibcount == 1; + } + + be->set_use_connect(connect_type); + be->set_leaf_bone(true); + + return *be; +} diff --git a/source/blender/io/collada/ArmatureImporter.h b/source/blender/io/collada/ArmatureImporter.h new file mode 100644 index 00000000000..da92c04e5dc --- /dev/null +++ b/source/blender/io/collada/ArmatureImporter.h @@ -0,0 +1,187 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __ARMATUREIMPORTER_H__ +#define __ARMATUREIMPORTER_H__ + +#include "COLLADAFWNode.h" +#include "COLLADAFWUniqueId.h" +#include "COLLADAFWMorphController.h" + +extern "C" { +#include "BKE_context.h" +#include "BKE_key.h" + +#include "DNA_armature_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_key_types.h" + +#include "ED_armature.h" +} + +#include "AnimationImporter.h" +#include "MeshImporter.h" +#include "SkinInfo.h" +#include "TransformReader.h" +#include "ExtraTags.h" + +#include +#include + +#include "collada_internal.h" +#include "collada_utils.h" +#include "ImportSettings.h" + +#define UNLIMITED_CHAIN_MAX INT_MAX +#define MINIMUM_BONE_LENGTH 0.000001f + +class ArmatureImporter : private TransformReader { + private: + Main *m_bmain; + Scene *scene; + ViewLayer *view_layer; + UnitConverter *unit_converter; + const ImportSettings *import_settings; + + // std::map joint_index_to_joint_info_map; + // std::map joint_id_to_joint_index_map; + BoneExtensionManager bone_extension_manager; + // int bone_direction_row; // XXX not used + float leaf_bone_length; + int totbone; + // XXX not used + // float min_angle; // minimum angle between bone head-tail and a row of bone matrix + +#if 0 + struct ArmatureJoints { + Object *ob_arm; + std::vector root_joints; + }; + std::vector armature_joints; +#endif + + Object *empty; // empty for leaf bones + + std::map geom_uid_by_controller_uid; + std::map joint_by_uid; // contains all joints + std::vector root_joints; + std::vector finished_joints; + std::vector morph_controllers; + std::map joint_parent_map; + std::map unskinned_armature_map; + + MeshImporterBase *mesh_importer; + + // This is used to store data passed in write_controller_data. + // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members + // so that arrays don't get freed until we free them explicitly. + + std::map skin_by_data_uid; // data UID = skin controller data UID +#if 0 + JointData *get_joint_data(COLLADAFW::Node *node); +#endif + + int create_bone(SkinInfo *skin, + COLLADAFW::Node *node, + EditBone *parent, + int totchild, + float parent_mat[4][4], + bArmature *arm, + std::vector &layer_labels); + + BoneExtended &add_bone_extended(EditBone *bone, + COLLADAFW::Node *node, + int sibcount, + std::vector &layer_labels, + BoneExtensionMap &extended_bones); + + void fix_leaf_bone_hierarchy(bArmature *armature, Bone *bone, bool fix_orientation); + void fix_leaf_bone(bArmature *armature, EditBone *ebone, BoneExtended *be, bool fix_orientation); + void fix_parent_connect(bArmature *armature, Bone *bone); + void connect_bone_chains(bArmature *armature, Bone *bone, const int max_chain_length); + + void set_pose(Object *ob_arm, + COLLADAFW::Node *root_node, + const char *parentname, + float parent_mat[4][4]); + + void set_bone_transformation_type(const COLLADAFW::Node *node, Object *ob_arm); + bool node_is_decomposed(const COLLADAFW::Node *node); +#if 0 + void set_leaf_bone_shapes(Object *ob_arm); + void set_euler_rotmode(); +#endif + + Object *get_empty_for_leaves(); + +#if 0 + Object *find_armature(COLLADAFW::Node *node); + + ArmatureJoints &get_armature_joints(Object *ob_arm); +#endif + + Object *create_armature_bones(Main *bmain, SkinInfo &skin); + void create_armature_bones(Main *bmain, std::vector &arm_objs); + + /** TagsMap typedef for uid_tags_map. */ + typedef std::map TagsMap; + TagsMap uid_tags_map; + + public: + ArmatureImporter(UnitConverter *conv, + MeshImporterBase *mesh, + Main *bmain, + Scene *sce, + ViewLayer *view_layer, + const ImportSettings *import_settings); + ~ArmatureImporter(); + + void add_root_joint(COLLADAFW::Node *node, Object *parent); + + // here we add bones to armatures, having armatures previously created in write_controller + void make_armatures(bContext *C, std::vector &objects_to_scale); + + void make_shape_keys(bContext *C); + +#if 0 + // link with meshes, create vertex groups, assign weights + void link_armature(Object *ob_arm, + const COLLADAFW::UniqueId &geom_id, + const COLLADAFW::UniqueId &controller_data_id); +#endif + + bool write_skin_controller_data(const COLLADAFW::SkinControllerData *data); + + bool write_controller(const COLLADAFW::Controller *controller); + + COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId &controller_uid); + + Object *get_armature_for_joint(COLLADAFW::Node *node); + + void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count); + + // gives a world-space mat + bool get_joint_bind_mat(float m[4][4], COLLADAFW::Node *joint); + + void set_tags_map(TagsMap &tags_map); +}; + +#endif diff --git a/source/blender/io/collada/BCAnimationCurve.cpp b/source/blender/io/collada/BCAnimationCurve.cpp new file mode 100644 index 00000000000..36800d611d2 --- /dev/null +++ b/source/blender/io/collada/BCAnimationCurve.cpp @@ -0,0 +1,691 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#include "BCAnimationCurve.h" + +BCAnimationCurve::BCAnimationCurve() +{ + this->curve_key.set_object_type(BC_ANIMATION_TYPE_OBJECT); + this->fcurve = NULL; + this->curve_is_local_copy = false; +} + +BCAnimationCurve::BCAnimationCurve(const BCAnimationCurve &other) +{ + this->min = other.min; + this->max = other.max; + this->fcurve = other.fcurve; + this->curve_key = other.curve_key; + this->curve_is_local_copy = false; + this->id_ptr = other.id_ptr; + + /* The fcurve of the new instance is a copy and can be modified */ + + get_edit_fcurve(); +} + +BCAnimationCurve::BCAnimationCurve(BCCurveKey key, Object *ob, FCurve *fcu) +{ + this->min = 0; + this->max = 0; + this->curve_key = key; + this->fcurve = fcu; + this->curve_is_local_copy = false; + init_pointer_rna(ob); +} + +BCAnimationCurve::BCAnimationCurve(const BCCurveKey &key, Object *ob) +{ + this->curve_key = key; + this->fcurve = NULL; + this->curve_is_local_copy = false; + init_pointer_rna(ob); +} + +void BCAnimationCurve::init_pointer_rna(Object *ob) +{ + switch (this->curve_key.get_animation_type()) { + case BC_ANIMATION_TYPE_BONE: { + bArmature *arm = (bArmature *)ob->data; + RNA_id_pointer_create(&arm->id, &id_ptr); + } break; + case BC_ANIMATION_TYPE_OBJECT: { + RNA_id_pointer_create(&ob->id, &id_ptr); + } break; + case BC_ANIMATION_TYPE_MATERIAL: { + Material *ma = BKE_object_material_get(ob, curve_key.get_subindex() + 1); + RNA_id_pointer_create(&ma->id, &id_ptr); + } break; + case BC_ANIMATION_TYPE_CAMERA: { + Camera *camera = (Camera *)ob->data; + RNA_id_pointer_create(&camera->id, &id_ptr); + } break; + case BC_ANIMATION_TYPE_LIGHT: { + Light *lamp = (Light *)ob->data; + RNA_id_pointer_create(&lamp->id, &id_ptr); + } break; + default: + fprintf( + stderr, "BC_animation_curve_type %d not supported", this->curve_key.get_array_index()); + break; + } +} + +void BCAnimationCurve::delete_fcurve(FCurve *fcu) +{ + free_fcurve(fcu); +} + +FCurve *BCAnimationCurve::create_fcurve(int array_index, const char *rna_path) +{ + FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve"); + fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED); + fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path)); + fcu->array_index = array_index; + return fcu; +} + +void BCAnimationCurve::create_bezt(float frame, float output) +{ + FCurve *fcu = get_edit_fcurve(); + BezTriple bez; + memset(&bez, 0, sizeof(BezTriple)); + bez.vec[1][0] = frame; + bez.vec[1][1] = output; + bez.ipo = U.ipo_new; /* use default interpolation mode here... */ + bez.f1 = bez.f2 = bez.f3 = SELECT; + bez.h1 = bez.h2 = HD_AUTO; + insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS); + calchandles_fcurve(fcu); +} + +BCAnimationCurve::~BCAnimationCurve() +{ + if (curve_is_local_copy && fcurve) { + // fprintf(stderr, "removed fcurve %s\n", fcurve->rna_path); + delete_fcurve(fcurve); + this->fcurve = NULL; + } +} + +const bool BCAnimationCurve::is_of_animation_type(BC_animation_type type) const +{ + return curve_key.get_animation_type() == type; +} + +const std::string BCAnimationCurve::get_channel_target() const +{ + const std::string path = curve_key.get_path(); + + if (bc_startswith(path, "pose.bones")) { + return bc_string_after(path, "pose.bones"); + } + return bc_string_after(path, "."); +} + +const std::string BCAnimationCurve::get_channel_type() const +{ + const std::string channel = get_channel_target(); + return bc_string_after(channel, "."); +} + +const std::string BCAnimationCurve::get_channel_posebone() const +{ + const std::string channel = get_channel_target(); + std::string pose_bone_name = bc_string_before(channel, "."); + if (pose_bone_name == channel) { + pose_bone_name = ""; + } + else { + pose_bone_name = bc_string_after(pose_bone_name, "\"["); + pose_bone_name = bc_string_before(pose_bone_name, "]\""); + } + return pose_bone_name; +} + +const std::string BCAnimationCurve::get_animation_name(Object *ob) const +{ + std::string name; + + switch (curve_key.get_animation_type()) { + case BC_ANIMATION_TYPE_OBJECT: { + name = id_name(ob); + } break; + + case BC_ANIMATION_TYPE_BONE: { + if (fcurve == NULL || fcurve->rna_path == NULL) { + name = ""; + } + else { + const char *boneName = BLI_str_quoted_substrN(fcurve->rna_path, "pose.bones["); + name = (boneName) ? id_name(ob) + "_" + std::string(boneName) : ""; + } + } break; + + case BC_ANIMATION_TYPE_CAMERA: { + Camera *camera = (Camera *)ob->data; + name = id_name(ob) + "-" + id_name(camera) + "-camera"; + } break; + + case BC_ANIMATION_TYPE_LIGHT: { + Light *lamp = (Light *)ob->data; + name = id_name(ob) + "-" + id_name(lamp) + "-light"; + } break; + + case BC_ANIMATION_TYPE_MATERIAL: { + Material *ma = BKE_object_material_get(ob, this->curve_key.get_subindex() + 1); + name = id_name(ob) + "-" + id_name(ma) + "-material"; + } break; + + default: { + name = ""; + } + } + + return name; +} + +const int BCAnimationCurve::get_channel_index() const +{ + return curve_key.get_array_index(); +} + +const int BCAnimationCurve::get_subindex() const +{ + return curve_key.get_subindex(); +} + +const std::string BCAnimationCurve::get_rna_path() const +{ + return curve_key.get_path(); +} + +const int BCAnimationCurve::sample_count() const +{ + if (fcurve == NULL) { + return 0; + } + return fcurve->totvert; +} + +const int BCAnimationCurve::closest_index_above(const float sample_frame, const int start_at) const +{ + if (fcurve == NULL) { + return -1; + } + + const int cframe = fcurve->bezt[start_at].vec[1][0]; // inacurate! + + if (fabs(cframe - sample_frame) < 0.00001) { + return start_at; + } + return (fcurve->totvert > start_at + 1) ? start_at + 1 : start_at; +} + +const int BCAnimationCurve::closest_index_below(const float sample_frame) const +{ + if (fcurve == NULL) { + return -1; + } + + float lower_frame = sample_frame; + float upper_frame = sample_frame; + int lower_index = 0; + int upper_index = 0; + + for (int fcu_index = 0; fcu_index < fcurve->totvert; fcu_index++) { + upper_index = fcu_index; + + const int cframe = fcurve->bezt[fcu_index].vec[1][0]; // inacurate! + if (cframe <= sample_frame) { + lower_frame = cframe; + lower_index = fcu_index; + } + if (cframe >= sample_frame) { + upper_frame = cframe; + break; + } + } + + if (lower_index == upper_index) { + return lower_index; + } + + const float fraction = float(sample_frame - lower_frame) / (upper_frame - lower_frame); + return (fraction < 0.5) ? lower_index : upper_index; +} + +const int BCAnimationCurve::get_interpolation_type(float sample_frame) const +{ + const int index = closest_index_below(sample_frame); + if (index < 0) { + return BEZT_IPO_BEZ; + } + return fcurve->bezt[index].ipo; +} + +const FCurve *BCAnimationCurve::get_fcurve() const +{ + return fcurve; +} + +FCurve *BCAnimationCurve::get_edit_fcurve() +{ + if (!curve_is_local_copy) { + const int index = curve_key.get_array_index(); + const std::string &path = curve_key.get_path(); + fcurve = create_fcurve(index, path.c_str()); + + /* Caution here: + * Replacing the pointer here is OK only because the original value + * of FCurve was a const pointer into Blender territory. We do not + * touch that! We use the local copy to prepare data for export. */ + + curve_is_local_copy = true; + } + return fcurve; +} + +void BCAnimationCurve::clean_handles() +{ + if (fcurve == NULL) { + fcurve = get_edit_fcurve(); + } + + /* Keep old bezt data for copy)*/ + BezTriple *old_bezts = fcurve->bezt; + int totvert = fcurve->totvert; + fcurve->bezt = NULL; + fcurve->totvert = 0; + + for (int i = 0; i < totvert; i++) { + BezTriple *bezt = &old_bezts[i]; + float x = bezt->vec[1][0]; + float y = bezt->vec[1][1]; + insert_vert_fcurve(fcurve, x, y, (eBezTriple_KeyframeType)BEZKEYTYPE(bezt), INSERTKEY_NOFLAGS); + BezTriple *lastb = fcurve->bezt + (fcurve->totvert - 1); + lastb->f1 = lastb->f2 = lastb->f3 = 0; + } + + /* now free the memory used by the old BezTriples */ + if (old_bezts) { + MEM_freeN(old_bezts); + } +} + +const bool BCAnimationCurve::is_transform_curve() const +{ + std::string channel_type = this->get_channel_type(); + return (is_rotation_curve() || channel_type == "scale" || channel_type == "location"); +} + +const bool BCAnimationCurve::is_rotation_curve() const +{ + std::string channel_type = this->get_channel_type(); + return (channel_type == "rotation" || channel_type == "rotation_euler" || + channel_type == "rotation_quaternion"); +} + +const float BCAnimationCurve::get_value(const float frame) +{ + if (fcurve) { + return evaluate_fcurve(fcurve, frame); + } + return 0; // TODO: handle case where neither sample nor fcu exist +} + +void BCAnimationCurve::update_range(float val) +{ + if (val < min) { + min = val; + } + if (val > max) { + max = val; + } +} + +void BCAnimationCurve::init_range(float val) +{ + min = max = val; +} + +void BCAnimationCurve::adjust_range(const int frame_index) +{ + if (fcurve && fcurve->totvert > 1) { + const float eval = evaluate_fcurve(fcurve, frame_index); + + int first_frame = fcurve->bezt[0].vec[1][0]; + if (first_frame == frame_index) { + init_range(eval); + } + else { + update_range(eval); + } + } +} + +void BCAnimationCurve::add_value(const float val, const int frame_index) +{ + FCurve *fcu = get_edit_fcurve(); + fcu->auto_smoothing = U.auto_smoothing_new; + insert_vert_fcurve(fcu, frame_index, val, BEZT_KEYTYPE_KEYFRAME, INSERTKEY_NOFLAGS); + + if (fcu->totvert == 1) { + init_range(val); + } + else { + update_range(val); + } +} + +bool BCAnimationCurve::add_value_from_matrix(const BCSample &sample, const int frame_index) +{ + int array_index = curve_key.get_array_index(); + + /* transformation curves are fed directly from the transformation matrix + * to resolve parent inverse matrix issues with object hierarchies. + * Maybe this can be unified with the + */ + const std::string channel_target = get_channel_target(); + float val = 0; + /* Pick the value from the sample according to the definition of the FCurve */ + bool good = sample.get_value(channel_target, array_index, &val); + if (good) { + add_value(val, frame_index); + } + return good; +} + +bool BCAnimationCurve::add_value_from_rna(const int frame_index) +{ + PointerRNA ptr; + PropertyRNA *prop; + float value = 0.0f; + int array_index = curve_key.get_array_index(); + const std::string full_path = curve_key.get_full_path(); + + /* get property to read from, and get value as appropriate */ + bool path_resolved = RNA_path_resolve_full( + &id_ptr, full_path.c_str(), &ptr, &prop, &array_index); + if (!path_resolved && array_index == 0) { + const std::string rna_path = curve_key.get_path(); + path_resolved = RNA_path_resolve_full(&id_ptr, rna_path.c_str(), &ptr, &prop, &array_index); + } + + if (path_resolved) { + bool is_array = RNA_property_array_check(prop); + if (is_array) { + /* array */ + if ((array_index >= 0) && (array_index < RNA_property_array_length(&ptr, prop))) { + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get_index(&ptr, prop, array_index); + break; + case PROP_INT: + value = (float)RNA_property_int_get_index(&ptr, prop, array_index); + break; + case PROP_FLOAT: + value = RNA_property_float_get_index(&ptr, prop, array_index); + break; + default: + break; + } + } + else { + fprintf(stderr, + "Out of Bounds while reading data for Curve %s\n", + curve_key.get_full_path().c_str()); + return false; + } + } + else { + /* not an array */ + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + value = (float)RNA_property_boolean_get(&ptr, prop); + break; + case PROP_INT: + value = (float)RNA_property_int_get(&ptr, prop); + break; + case PROP_FLOAT: + value = RNA_property_float_get(&ptr, prop); + break; + case PROP_ENUM: + value = (float)RNA_property_enum_get(&ptr, prop); + break; + default: + fprintf(stderr, + "property type %d not supported for Curve %s\n", + RNA_property_type(prop), + curve_key.get_full_path().c_str()); + return false; + break; + } + } + } + else { + /* path couldn't be resolved */ + fprintf(stderr, "Path not recognized for Curve %s\n", curve_key.get_full_path().c_str()); + return false; + } + + add_value(value, frame_index); + return true; +} + +void BCAnimationCurve::get_value_map(BCValueMap &value_map) +{ + value_map.clear(); + if (fcurve == NULL) { + return; + } + + for (int i = 0; i < fcurve->totvert; i++) { + const float frame = fcurve->bezt[i].vec[1][0]; + const float val = fcurve->bezt[i].vec[1][1]; + value_map[frame] = val; + } +} + +void BCAnimationCurve::get_frames(BCFrames &frames) const +{ + frames.clear(); + if (fcurve) { + for (int i = 0; i < fcurve->totvert; i++) { + const float val = fcurve->bezt[i].vec[1][0]; + frames.push_back(val); + } + } +} + +void BCAnimationCurve::get_values(BCValues &values) const +{ + values.clear(); + if (fcurve) { + for (int i = 0; i < fcurve->totvert; i++) { + const float val = fcurve->bezt[i].vec[1][1]; + values.push_back(val); + } + } +} + +bool BCAnimationCurve::is_animated() +{ + static float MIN_DISTANCE = 0.00001; + return fabs(max - min) > MIN_DISTANCE; +} + +bool BCAnimationCurve::is_keyframe(int frame) +{ + if (this->fcurve == NULL) { + return false; + } + + for (int i = 0; i < fcurve->totvert; i++) { + const int cframe = nearbyint(fcurve->bezt[i].vec[1][0]); + if (cframe == frame) { + return true; + } + if (cframe > frame) { + break; + } + } + return false; +} + +/* Needed for adding a BCAnimationCurve into a BCAnimationCurveSet */ +inline bool operator<(const BCAnimationCurve &lhs, const BCAnimationCurve &rhs) +{ + std::string lhtgt = lhs.get_channel_target(); + std::string rhtgt = rhs.get_channel_target(); + if (lhtgt == rhtgt) { + const int lha = lhs.get_channel_index(); + const int rha = rhs.get_channel_index(); + return lha < rha; + } + else { + return lhtgt < rhtgt; + } +} + +BCCurveKey::BCCurveKey() +{ + this->key_type = BC_ANIMATION_TYPE_OBJECT; + this->rna_path = ""; + this->curve_array_index = 0; + this->curve_subindex = -1; +} + +BCCurveKey::BCCurveKey(const BC_animation_type type, + const std::string path, + const int array_index, + const int subindex) +{ + this->key_type = type; + this->rna_path = path; + this->curve_array_index = array_index; + this->curve_subindex = subindex; +} + +void BCCurveKey::operator=(const BCCurveKey &other) +{ + this->key_type = other.key_type; + this->rna_path = other.rna_path; + this->curve_array_index = other.curve_array_index; + this->curve_subindex = other.curve_subindex; +} + +const std::string BCCurveKey::get_full_path() const +{ + return this->rna_path + '[' + std::to_string(this->curve_array_index) + ']'; +} + +const std::string BCCurveKey::get_path() const +{ + return this->rna_path; +} + +const int BCCurveKey::get_array_index() const +{ + return this->curve_array_index; +} + +const int BCCurveKey::get_subindex() const +{ + return this->curve_subindex; +} + +void BCCurveKey::set_object_type(BC_animation_type object_type) +{ + this->key_type = object_type; +} + +const BC_animation_type BCCurveKey::get_animation_type() const +{ + return this->key_type; +} + +const bool BCCurveKey::operator<(const BCCurveKey &other) const +{ + /* needed for using this class as key in maps and sets */ + if (this->key_type != other.key_type) { + return this->key_type < other.key_type; + } + + if (this->curve_subindex != other.curve_subindex) { + return this->curve_subindex < other.curve_subindex; + } + + if (this->rna_path != other.rna_path) { + return this->rna_path < other.rna_path; + } + + return this->curve_array_index < other.curve_array_index; +} + +BCBezTriple::BCBezTriple(BezTriple &bezt) : bezt(bezt) +{ +} + +const float BCBezTriple::get_frame() const +{ + return bezt.vec[1][0]; +} + +const float BCBezTriple::get_time(Scene *scene) const +{ + return FRA2TIME(bezt.vec[1][0]); +} + +const float BCBezTriple::get_value() const +{ + return bezt.vec[1][1]; +} + +const float BCBezTriple::get_angle() const +{ + return RAD2DEGF(get_value()); +} + +void BCBezTriple::get_in_tangent(Scene *scene, float point[2], bool as_angle) const +{ + get_tangent(scene, point, as_angle, 0); +} + +void BCBezTriple::get_out_tangent(Scene *scene, float point[2], bool as_angle) const +{ + get_tangent(scene, point, as_angle, 2); +} + +void BCBezTriple::get_tangent(Scene *scene, float point[2], bool as_angle, int index) const +{ + point[0] = FRA2TIME(bezt.vec[index][0]); + if (bezt.ipo != BEZT_IPO_BEZ) { + /* We're in a mixed interpolation scenario, set zero as it's irrelevant but value might contain + * unused data */ + point[0] = 0; + point[1] = 0; + } + else if (as_angle) { + point[1] = RAD2DEGF(bezt.vec[index][1]); + } + else { + point[1] = bezt.vec[index][1]; + } +} diff --git a/source/blender/io/collada/BCAnimationCurve.h b/source/blender/io/collada/BCAnimationCurve.h new file mode 100644 index 00000000000..7b523ac53ca --- /dev/null +++ b/source/blender/io/collada/BCAnimationCurve.h @@ -0,0 +1,152 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#ifndef __BCANIMATIONCURVE_H__ +#define __BCANIMATIONCURVE_H__ + +#include "collada_utils.h" +#include "BCSampleData.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BKE_fcurve.h" +#include "BKE_armature.h" +#include "BKE_material.h" +#include "ED_anim_api.h" +#include "ED_keyframing.h" +#include "ED_keyframes_edit.h" +} + +typedef float(TangentPoint)[2]; + +typedef std::set BCFrameSet; +typedef std::vector BCFrames; +typedef std::vector BCValues; +typedef std::vector BCTimes; +typedef std::map BCValueMap; + +typedef enum BC_animation_type { + BC_ANIMATION_TYPE_OBJECT, + BC_ANIMATION_TYPE_BONE, + BC_ANIMATION_TYPE_CAMERA, + BC_ANIMATION_TYPE_MATERIAL, + BC_ANIMATION_TYPE_LIGHT, +} BC_animation_type; + +class BCCurveKey { + private: + BC_animation_type key_type; + std::string rna_path; + int curve_array_index; + int curve_subindex; /* only needed for materials */ + + public: + BCCurveKey(); + BCCurveKey(const BC_animation_type type, + const std::string path, + const int array_index, + const int subindex = -1); + void operator=(const BCCurveKey &other); + const std::string get_full_path() const; + const std::string get_path() const; + const int get_array_index() const; + const int get_subindex() const; + void set_object_type(BC_animation_type object_type); + const BC_animation_type get_animation_type() const; + const bool operator<(const BCCurveKey &other) const; +}; + +class BCBezTriple { + public: + BezTriple &bezt; + + BCBezTriple(BezTriple &bezt); + const float get_frame() const; + const float get_time(Scene *scene) const; + const float get_value() const; + const float get_angle() const; + void get_in_tangent(Scene *scene, float point[2], bool as_angle) const; + void get_out_tangent(Scene *scene, float point[2], bool as_angle) const; + void get_tangent(Scene *scene, float point[2], bool as_angle, int index) const; +}; + +class BCAnimationCurve { + private: + BCCurveKey curve_key; + float min = 0; + float max = 0; + + bool curve_is_local_copy = false; + FCurve *fcurve; + PointerRNA id_ptr; + void init_pointer_rna(Object *ob); + void delete_fcurve(FCurve *fcu); + FCurve *create_fcurve(int array_index, const char *rna_path); + void create_bezt(float frame, float output); + void update_range(float val); + void init_range(float val); + + public: + BCAnimationCurve(); + BCAnimationCurve(const BCAnimationCurve &other); + BCAnimationCurve(const BCCurveKey &key, Object *ob); + BCAnimationCurve(BCCurveKey key, Object *ob, FCurve *fcu); + ~BCAnimationCurve(); + + const bool is_of_animation_type(BC_animation_type type) const; + const int get_interpolation_type(float sample_frame) const; + bool is_animated(); + const bool is_transform_curve() const; + const bool is_rotation_curve() const; + bool is_keyframe(int frame); + void adjust_range(int frame); + + const std::string get_animation_name(Object *ob) const; /* xxx: this is collada specific */ + const std::string get_channel_target() const; + const std::string get_channel_type() const; + const std::string get_channel_posebone() const; // returns "" if channel is not a bone channel + + const int get_channel_index() const; + const int get_subindex() const; + const std::string get_rna_path() const; + const FCurve *get_fcurve() const; + const int sample_count() const; + + const float get_value(const float frame); + void get_values(BCValues &values) const; + void get_value_map(BCValueMap &value_map); + + void get_frames(BCFrames &frames) const; + + /* Curve edit functions create a copy of the underlaying FCurve */ + FCurve *get_edit_fcurve(); + bool add_value_from_rna(const int frame); + bool add_value_from_matrix(const BCSample &sample, const int frame); + void add_value(const float val, const int frame); + void clean_handles(); + + /* experimental stuff */ + const int closest_index_above(const float sample_frame, const int start_at) const; + const int closest_index_below(const float sample_frame) const; +}; + +typedef std::map BCAnimationCurveMap; + +#endif /* __BCANIMATIONCURVE_H__ */ diff --git a/source/blender/io/collada/BCAnimationSampler.cpp b/source/blender/io/collada/BCAnimationSampler.cpp new file mode 100644 index 00000000000..e6996e95a5b --- /dev/null +++ b/source/blender/io/collada/BCAnimationSampler.cpp @@ -0,0 +1,662 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#include +#include +#include // std::find + +#include "ExportSettings.h" +#include "BCAnimationCurve.h" +#include "BCAnimationSampler.h" +#include "collada_utils.h" +#include "BCMath.h" + +extern "C" { +#include "BKE_action.h" +#include "BKE_constraint.h" +#include "BKE_key.h" +#include "BKE_main.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BLI_listbase.h" +#include "DNA_anim_types.h" +#include "DNA_scene_types.h" +#include "DNA_key_types.h" +#include "DNA_constraint_types.h" +#include "ED_object.h" +} + +static std::string EMPTY_STRING; +static BCAnimationCurveMap BCEmptyAnimationCurves; + +BCAnimationSampler::BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &object_set) + : export_settings(export_settings) +{ + BCObjectSet::iterator it; + for (it = object_set.begin(); it != object_set.end(); ++it) { + Object *ob = *it; + add_object(ob); + } +} + +BCAnimationSampler::~BCAnimationSampler() +{ + BCAnimationObjectMap::iterator it; + for (it = objects.begin(); it != objects.end(); ++it) { + BCAnimation *animation = it->second; + delete animation; + } +} + +void BCAnimationSampler::add_object(Object *ob) +{ + BlenderContext blender_context = export_settings.get_blender_context(); + BCAnimation *animation = new BCAnimation(blender_context.get_context(), ob); + objects[ob] = animation; + + initialize_keyframes(animation->frame_set, ob); + initialize_curves(animation->curve_map, ob); +} + +BCAnimationCurveMap *BCAnimationSampler::get_curves(Object *ob) +{ + BCAnimation &animation = *objects[ob]; + if (animation.curve_map.size() == 0) { + initialize_curves(animation.curve_map, ob); + } + return &animation.curve_map; +} + +static void get_sample_frames(BCFrameSet &sample_frames, + int sampling_rate, + bool keyframe_at_end, + Scene *scene) +{ + sample_frames.clear(); + + if (sampling_rate < 1) { + return; // no sample frames in this case + } + + float sfra = scene->r.sfra; + float efra = scene->r.efra; + + int frame_index; + for (frame_index = nearbyint(sfra); frame_index < efra; frame_index += sampling_rate) { + sample_frames.insert(frame_index); + } + + if (frame_index >= efra && keyframe_at_end) { + sample_frames.insert(efra); + } +} + +static bool is_object_keyframe(Object *ob, int frame_index) +{ + return false; +} + +static void add_keyframes_from(bAction *action, BCFrameSet &frameset) +{ + if (action) { + FCurve *fcu = NULL; + for (fcu = (FCurve *)action->curves.first; fcu; fcu = fcu->next) { + BezTriple *bezt = fcu->bezt; + for (int i = 0; i < fcu->totvert; bezt++, i++) { + int frame_index = nearbyint(bezt->vec[1][0]); + frameset.insert(frame_index); + } + } + } +} + +void BCAnimationSampler::check_property_is_animated( + BCAnimation &animation, float *ref, float *val, std::string data_path, int length) +{ + for (int array_index = 0; array_index < length; array_index++) { + if (!bc_in_range(ref[length], val[length], 0.00001)) { + BCCurveKey key(BC_ANIMATION_TYPE_OBJECT, data_path, array_index); + BCAnimationCurveMap::iterator it = animation.curve_map.find(key); + if (it == animation.curve_map.end()) { + animation.curve_map[key] = new BCAnimationCurve(key, animation.get_reference()); + } + } + } +} + +void BCAnimationSampler::update_animation_curves(BCAnimation &animation, + BCSample &sample, + Object *ob, + int frame) +{ + BCAnimationCurveMap::iterator it; + for (it = animation.curve_map.begin(); it != animation.curve_map.end(); ++it) { + BCAnimationCurve *curve = it->second; + if (curve->is_transform_curve()) { + curve->add_value_from_matrix(sample, frame); + } + else { + curve->add_value_from_rna(frame); + } + } +} + +BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim) +{ + BCSample &ob_sample = sample_data.add(ob, frame_index); + // if (export_settings.get_apply_global_orientation()) { + // const BCMatrix &global_transform = export_settings.get_global_transform(); + // ob_sample.get_matrix(global_transform); + //} + + if (ob->type == OB_ARMATURE) { + bPoseChannel *pchan; + for (pchan = (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan = pchan->next) { + Bone *bone = pchan->bone; + Matrix bmat; + if (bc_bone_matrix_local_get(ob, bone, bmat, for_opensim)) { + + ob_sample.add_bone_matrix(bone, bmat); + } + } + } + return ob_sample; +} + +void BCAnimationSampler::sample_scene(BCExportSettings &export_settings, bool keyframe_at_end) +{ + BlenderContext blender_context = export_settings.get_blender_context(); + int sampling_rate = export_settings.get_sampling_rate(); + bool for_opensim = export_settings.get_open_sim(); + bool keep_keyframes = export_settings.get_keep_keyframes(); + BC_export_animation_type export_animation_type = export_settings.get_export_animation_type(); + + Scene *scene = blender_context.get_scene(); + BCFrameSet scene_sample_frames; + get_sample_frames(scene_sample_frames, sampling_rate, keyframe_at_end, scene); + BCFrameSet::iterator it; + + int startframe = scene->r.sfra; + int endframe = scene->r.efra; + + for (int frame_index = startframe; frame_index <= endframe; frame_index++) { + /* Loop over all frames and decide for each frame if sampling is necessary */ + bool is_scene_sample_frame = false; + bool needs_update = true; + if (scene_sample_frames.find(frame_index) != scene_sample_frames.end()) { + bc_update_scene(blender_context, frame_index); + needs_update = false; + is_scene_sample_frame = true; + } + + bool needs_sampling = is_scene_sample_frame || keep_keyframes || + export_animation_type == BC_ANIMATION_EXPORT_KEYS; + if (!needs_sampling) { + continue; + } + + BCAnimationObjectMap::iterator obit; + for (obit = objects.begin(); obit != objects.end(); ++obit) { + Object *ob = obit->first; + BCAnimation *animation = obit->second; + BCFrameSet &object_keyframes = animation->frame_set; + if (is_scene_sample_frame || object_keyframes.find(frame_index) != object_keyframes.end()) { + + if (needs_update) { + bc_update_scene(blender_context, frame_index); + needs_update = false; + } + + BCSample &sample = sample_object(ob, frame_index, for_opensim); + update_animation_curves(*animation, sample, ob, frame_index); + } + } + } +} + +bool BCAnimationSampler::is_animated_by_constraint(Object *ob, + ListBase *conlist, + std::set &animated_objects) +{ + bConstraint *con; + for (con = (bConstraint *)conlist->first; con; con = con->next) { + ListBase targets = {NULL, NULL}; + + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + + if (!bc_validateConstraints(con)) { + continue; + } + + if (cti && cti->get_constraint_targets) { + bConstraintTarget *ct; + Object *obtar; + cti->get_constraint_targets(con, &targets); + for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) { + obtar = ct->tar; + if (obtar) { + if (animated_objects.find(obtar) != animated_objects.end()) { + return true; + } + } + } + } + } + return false; +} + +void BCAnimationSampler::find_depending_animated(std::set &animated_objects, + std::set &candidates) +{ + bool found_more; + do { + found_more = false; + std::set::iterator it; + for (it = candidates.begin(); it != candidates.end(); ++it) { + Object *cob = *it; + ListBase *conlist = get_active_constraints(cob); + if (is_animated_by_constraint(cob, conlist, animated_objects)) { + animated_objects.insert(cob); + candidates.erase(cob); + found_more = true; + break; + } + } + } while (found_more && candidates.size() > 0); +} + +void BCAnimationSampler::get_animated_from_export_set(std::set &animated_objects, + LinkNode &export_set) +{ + /* Check if this object is animated. That is: Check if it has its own action, or: + * + * - Check if it has constraints to other objects. + * - at least one of the other objects is animated as well. + */ + + animated_objects.clear(); + std::set static_objects; + std::set candidates; + + LinkNode *node; + for (node = &export_set; node; node = node->next) { + Object *cob = (Object *)node->link; + if (bc_has_animations(cob)) { + animated_objects.insert(cob); + } + else { + ListBase conlist = cob->constraints; + if (conlist.first) { + candidates.insert(cob); + } + } + } + find_depending_animated(animated_objects, candidates); +} + +void BCAnimationSampler::get_object_frames(BCFrames &frames, Object *ob) +{ + sample_data.get_frames(ob, frames); +} + +void BCAnimationSampler::get_bone_frames(BCFrames &frames, Object *ob, Bone *bone) +{ + sample_data.get_frames(ob, bone, frames); +} + +bool BCAnimationSampler::get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone) +{ + sample_data.get_matrices(ob, bone, samples); + return bc_is_animated(samples); +} + +bool BCAnimationSampler::get_object_samples(BCMatrixSampleMap &samples, Object *ob) +{ + sample_data.get_matrices(ob, samples); + return bc_is_animated(samples); +} + +#if 0 +/** + * Add sampled values to #FCurve + * If no #FCurve exists, create a temporary #FCurve; + * \note The temporary #FCurve will later be removed when the + * #BCAnimationSampler is removed (by its destructor). + * + * \param curve: The curve to which the data is added. + * \param matrices: The set of matrix values from where the data is taken. + * \param animation_type: + * - #BC_ANIMATION_EXPORT_SAMPLES: Use all matrix data. + * - #BC_ANIMATION_EXPORT_KEYS: Only take data from matrices for keyframes. + */ +void BCAnimationSampler::add_value_set(BCAnimationCurve &curve, + BCFrameSampleMap &samples, + BC_export_animation_type animation_type) +{ + int array_index = curve.get_array_index(); + const BC_animation_transform_type tm_type = curve.get_transform_type(); + + BCFrameSampleMap::iterator it; + for (it = samples.begin(); it != samples.end(); ++it) { + const int frame_index = nearbyint(it->first); + if (animation_type == BC_ANIMATION_EXPORT_SAMPLES || curve.is_keyframe(frame_index)) { + + const BCSample *sample = it->second; + float val = 0; + + int subindex = curve.get_subindex(); + bool good; + if (subindex == -1) { + good = sample->get_value(tm_type, array_index, &val); + } + else { + good = sample->get_value(tm_type, array_index, &val, subindex); + } + + if (good) { + curve.add_value(val, frame_index); + } + } + } + curve.remove_unused_keyframes(); + curve.calchandles(); +} +#endif + +void BCAnimationSampler::generate_transform(Object *ob, + const BCCurveKey &key, + BCAnimationCurveMap &curves) +{ + BCAnimationCurveMap::const_iterator it = curves.find(key); + if (it == curves.end()) { + curves[key] = new BCAnimationCurve(key, ob); + } +} + +void BCAnimationSampler::generate_transforms(Object *ob, + const std::string prep, + const BC_animation_type type, + BCAnimationCurveMap &curves) +{ + generate_transform(ob, BCCurveKey(type, prep + "location", 0), curves); + generate_transform(ob, BCCurveKey(type, prep + "location", 1), curves); + generate_transform(ob, BCCurveKey(type, prep + "location", 2), curves); + generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 0), curves); + generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 1), curves); + generate_transform(ob, BCCurveKey(type, prep + "rotation_euler", 2), curves); + generate_transform(ob, BCCurveKey(type, prep + "scale", 0), curves); + generate_transform(ob, BCCurveKey(type, prep + "scale", 1), curves); + generate_transform(ob, BCCurveKey(type, prep + "scale", 2), curves); +} + +void BCAnimationSampler::generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves) +{ + std::string prep = "pose.bones[\"" + std::string(bone->name) + "\"]."; + generate_transforms(ob, prep, BC_ANIMATION_TYPE_BONE, curves); + + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + generate_transforms(ob, child, curves); + } +} + +/** + * Collect all keyframes from all animation curves related to the object. + * The bc_get... functions check for NULL and correct object type. + * The #add_keyframes_from() function checks for NULL. + */ +void BCAnimationSampler::initialize_keyframes(BCFrameSet &frameset, Object *ob) +{ + frameset.clear(); + add_keyframes_from(bc_getSceneObjectAction(ob), frameset); + add_keyframes_from(bc_getSceneCameraAction(ob), frameset); + add_keyframes_from(bc_getSceneLightAction(ob), frameset); + + for (int a = 0; a < ob->totcol; a++) { + Material *ma = BKE_object_material_get(ob, a + 1); + add_keyframes_from(bc_getSceneMaterialAction(ma), frameset); + } +} + +void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object *ob) +{ + BC_animation_type object_type = BC_ANIMATION_TYPE_OBJECT; + + bAction *action = bc_getSceneObjectAction(ob); + if (action) { + FCurve *fcu = (FCurve *)action->curves.first; + + for (; fcu; fcu = fcu->next) { + object_type = BC_ANIMATION_TYPE_OBJECT; + if (ob->type == OB_ARMATURE) { + char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones["); + if (boneName) { + object_type = BC_ANIMATION_TYPE_BONE; + } + } + + /* Adding action curves on object */ + BCCurveKey key(object_type, fcu->rna_path, fcu->array_index); + curves[key] = new BCAnimationCurve(key, ob, fcu); + } + } + + /* Add missing curves */ + object_type = BC_ANIMATION_TYPE_OBJECT; + generate_transforms(ob, EMPTY_STRING, object_type, curves); + if (ob->type == OB_ARMATURE) { + bArmature *arm = (bArmature *)ob->data; + for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next) { + generate_transforms(ob, root_bone, curves); + } + } + + /* Add curves on Object->data actions */ + action = NULL; + if (ob->type == OB_CAMERA) { + action = bc_getSceneCameraAction(ob); + object_type = BC_ANIMATION_TYPE_CAMERA; + } + else if (ob->type == OB_LAMP) { + action = bc_getSceneLightAction(ob); + object_type = BC_ANIMATION_TYPE_LIGHT; + } + + if (action) { + /* Add light action or Camera action */ + FCurve *fcu = (FCurve *)action->curves.first; + for (; fcu; fcu = fcu->next) { + BCCurveKey key(object_type, fcu->rna_path, fcu->array_index); + curves[key] = new BCAnimationCurve(key, ob, fcu); + } + } + + /* Add curves on Object->material actions*/ + object_type = BC_ANIMATION_TYPE_MATERIAL; + for (int a = 0; a < ob->totcol; a++) { + /* Export Material parameter animations. */ + Material *ma = BKE_object_material_get(ob, a + 1); + if (ma) { + action = bc_getSceneMaterialAction(ma); + if (action) { + /* isMatAnim = true; */ + FCurve *fcu = (FCurve *)action->curves.first; + for (; fcu; fcu = fcu->next) { + BCCurveKey key(object_type, fcu->rna_path, fcu->array_index, a); + curves[key] = new BCAnimationCurve(key, ob, fcu); + } + } + } + } +} + +/* ==================================================================== */ + +BCSample &BCSampleFrame::add(Object *ob) +{ + BCSample *sample = new BCSample(ob); + sampleMap[ob] = sample; + return *sample; +} + +/* Get the matrix for the given key, returns Unity when the key does not exist */ +const BCSample *BCSampleFrame::get_sample(Object *ob) const +{ + BCSampleMap::const_iterator it = sampleMap.find(ob); + if (it == sampleMap.end()) { + return NULL; + } + return it->second; +} + +const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob) const +{ + BCSampleMap::const_iterator it = sampleMap.find(ob); + if (it == sampleMap.end()) { + return NULL; + } + BCSample *sample = it->second; + return &sample->get_matrix(); +} + +/* Get the matrix for the given Bone, returns Unity when the Objewct is not sampled */ +const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob, Bone *bone) const +{ + BCSampleMap::const_iterator it = sampleMap.find(ob); + if (it == sampleMap.end()) { + return NULL; + } + + BCSample *sample = it->second; + const BCMatrix *bc_bone = sample->get_matrix(bone); + return bc_bone; +} + +/* Check if the key is in this BCSampleFrame */ +const bool BCSampleFrame::has_sample_for(Object *ob) const +{ + return sampleMap.find(ob) != sampleMap.end(); +} + +/* Check if the Bone is in this BCSampleFrame */ +const bool BCSampleFrame::has_sample_for(Object *ob, Bone *bone) const +{ + const BCMatrix *bc_bone = get_sample_matrix(ob, bone); + return (bc_bone); +} + +/* ==================================================================== */ + +BCSample &BCSampleFrameContainer::add(Object *ob, int frame_index) +{ + BCSampleFrame &frame = sample_frames[frame_index]; + return frame.add(ob); +} + +/* ====================================================== */ +/* Below are the getters which we need to export the data */ +/* ====================================================== */ + +/* Return either the BCSampleFrame or NULL if frame does not exist*/ +BCSampleFrame *BCSampleFrameContainer::get_frame(int frame_index) +{ + BCSampleFrameMap::iterator it = sample_frames.find(frame_index); + BCSampleFrame *frame = (it == sample_frames.end()) ? NULL : &it->second; + return frame; +} + +/* Return a list of all frames that need to be sampled */ +const int BCSampleFrameContainer::get_frames(std::vector &frames) const +{ + frames.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + frames.push_back(it->first); + } + return frames.size(); +} + +const int BCSampleFrameContainer::get_frames(Object *ob, BCFrames &frames) const +{ + frames.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + const BCSampleFrame &frame = it->second; + if (frame.has_sample_for(ob)) { + frames.push_back(it->first); + } + } + return frames.size(); +} + +const int BCSampleFrameContainer::get_frames(Object *ob, Bone *bone, BCFrames &frames) const +{ + frames.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + const BCSampleFrame &frame = it->second; + if (frame.has_sample_for(ob, bone)) { + frames.push_back(it->first); + } + } + return frames.size(); +} + +const int BCSampleFrameContainer::get_samples(Object *ob, BCFrameSampleMap &samples) const +{ + samples.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + const BCSampleFrame &frame = it->second; + const BCSample *sample = frame.get_sample(ob); + if (sample) { + samples[it->first] = sample; + } + } + return samples.size(); +} + +const int BCSampleFrameContainer::get_matrices(Object *ob, BCMatrixSampleMap &samples) const +{ + samples.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + const BCSampleFrame &frame = it->second; + const BCMatrix *matrix = frame.get_sample_matrix(ob); + if (matrix) { + samples[it->first] = matrix; + } + } + return samples.size(); +} + +const int BCSampleFrameContainer::get_matrices(Object *ob, + Bone *bone, + BCMatrixSampleMap &samples) const +{ + samples.clear(); // safety; + BCSampleFrameMap::const_iterator it; + for (it = sample_frames.begin(); it != sample_frames.end(); ++it) { + const BCSampleFrame &frame = it->second; + const BCMatrix *sample = frame.get_sample_matrix(ob, bone); + if (sample) { + samples[it->first] = sample; + } + } + return samples.size(); +} diff --git a/source/blender/io/collada/BCAnimationSampler.h b/source/blender/io/collada/BCAnimationSampler.h new file mode 100644 index 00000000000..96138d0cbca --- /dev/null +++ b/source/blender/io/collada/BCAnimationSampler.h @@ -0,0 +1,194 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BCANIMATIONSAMPLER_H__ +#define __BCANIMATIONSAMPLER_H__ + +#include "BCAnimationCurve.h" +#include "BCSampleData.h" +#include "collada_utils.h" + +extern "C" { +#include "BKE_action.h" +#include "BKE_lib_id.h" +#include "BLI_math_rotation.h" +#include "DNA_action_types.h" +} + +/* Collection of animation curves */ +class BCAnimation { + private: + Object *reference = NULL; + bContext *mContext; + + public: + BCFrameSet frame_set; + BCAnimationCurveMap curve_map; + + BCAnimation(bContext *C, Object *ob) : mContext(C) + { + Main *bmain = CTX_data_main(mContext); + reference = BKE_object_copy(bmain, ob); + } + + ~BCAnimation() + { + BCAnimationCurveMap::iterator it; + for (it = curve_map.begin(); it != curve_map.end(); ++it) { + delete it->second; + } + + if (reference && reference->id.us == 0) { + Main *bmain = CTX_data_main(mContext); + BKE_id_delete(bmain, &reference->id); + } + curve_map.clear(); + } + + Object *get_reference() + { + return reference; + } +}; + +typedef std::map BCAnimationObjectMap; + +class BCSampleFrame { + + /* Each frame on the timeline that needs to be sampled will have + * one BCSampleFrame where we collect sample information about all objects + * that need to be sampled for that frame. */ + + private: + BCSampleMap sampleMap; + + public: + ~BCSampleFrame() + { + BCSampleMap::iterator it; + for (it = sampleMap.begin(); it != sampleMap.end(); ++it) { + BCSample *sample = it->second; + delete sample; + } + sampleMap.clear(); + } + + BCSample &add(Object *ob); + + /* Following methods return NULL if object is not in the sampleMap*/ + const BCSample *get_sample(Object *ob) const; + const BCMatrix *get_sample_matrix(Object *ob) const; + const BCMatrix *get_sample_matrix(Object *ob, Bone *bone) const; + + const bool has_sample_for(Object *ob) const; + const bool has_sample_for(Object *ob, Bone *bone) const; +}; + +typedef std::map BCSampleFrameMap; + +class BCSampleFrameContainer { + + /* + * The BCSampleFrameContainer stores a map of BCSampleFrame objects + * with the timeline frame as key. + * + * Some details on the purpose: + * An Animation is made of multiple FCurves where each FCurve can + * have multiple keyframes. When we want to export the animation we + * also can decide whether we want to export the keyframes or a set + * of sample frames at equidistant locations (sample period). + * In any case we must resample first need to resample it fully + * to resolve things like: + * + * - animations by constraints + * - animations by drivers + * + * For this purpose we need to step through the entire animation and + * then sample each frame that contains at least one keyFrame or + * sampleFrame. Then for each frame we have to store the transform + * information for all exported objects in a BCSampleframe + * + * The entire set of BCSampleframes is finally collected into + * a BCSampleframneContainer + */ + + private: + BCSampleFrameMap sample_frames; + + public: + ~BCSampleFrameContainer() + { + } + + BCSample &add(Object *ob, int frame_index); + BCSampleFrame *get_frame(int frame_index); // returns NULL if frame does not exist + + const int get_frames(std::vector &frames) const; + const int get_frames(Object *ob, BCFrames &frames) const; + const int get_frames(Object *ob, Bone *bone, BCFrames &frames) const; + + const int get_samples(Object *ob, BCFrameSampleMap &samples) const; + const int get_matrices(Object *ob, BCMatrixSampleMap &matrices) const; + const int get_matrices(Object *ob, Bone *bone, BCMatrixSampleMap &bones) const; +}; + +class BCAnimationSampler { + private: + BCExportSettings &export_settings; + BCSampleFrameContainer sample_data; + BCAnimationObjectMap objects; + + void generate_transform(Object *ob, const BCCurveKey &key, BCAnimationCurveMap &curves); + void generate_transforms(Object *ob, + const std::string prep, + const BC_animation_type type, + BCAnimationCurveMap &curves); + void generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves); + + void initialize_curves(BCAnimationCurveMap &curves, Object *ob); + void initialize_keyframes(BCFrameSet &frameset, Object *ob); + BCSample &sample_object(Object *ob, int frame_index, bool for_opensim); + void update_animation_curves(BCAnimation &animation, + BCSample &sample, + Object *ob, + int frame_index); + void check_property_is_animated( + BCAnimation &animation, float *ref, float *val, std::string data_path, int length); + + public: + BCAnimationSampler(BCExportSettings &export_settings, BCObjectSet &animated_subset); + ~BCAnimationSampler(); + + void add_object(Object *ob); + + void sample_scene(BCExportSettings &export_settings, bool keyframe_at_end); + + BCAnimationCurveMap *get_curves(Object *ob); + void get_object_frames(BCFrames &frames, Object *ob); + bool get_object_samples(BCMatrixSampleMap &samples, Object *ob); + void get_bone_frames(BCFrames &frames, Object *ob, Bone *bone); + bool get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone); + + static void get_animated_from_export_set(std::set &animated_objects, + LinkNode &export_set); + static void find_depending_animated(std::set &animated_objects, + std::set &candidates); + static bool is_animated_by_constraint(Object *ob, + ListBase *conlist, + std::set &animated_objects); +}; + +#endif /* __BCANIMATIONSAMPLER_H__ */ diff --git a/source/blender/io/collada/BCMath.cpp b/source/blender/io/collada/BCMath.cpp new file mode 100644 index 00000000000..ec9977c1469 --- /dev/null +++ b/source/blender/io/collada/BCMath.cpp @@ -0,0 +1,244 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#include "BCMath.h" +#include "BlenderContext.h" + +void BCQuat::rotate_to(Matrix &mat_to) +{ + Quat qd; + Matrix matd; + Matrix mati; + Matrix mat_from; + + quat_to_mat4(mat_from, q); + + /* Calculate the difference matrix matd between mat_from and mat_to */ + invert_m4_m4(mati, mat_from); + mul_m4_m4m4(matd, mati, mat_to); + + mat4_to_quat(qd, matd); + + mul_qt_qtqt(q, qd, q); /* rotate to the final rotation to mat_to */ +} + +BCMatrix::BCMatrix(const BCMatrix &mat) +{ + set_transform(mat.matrix); +} + +BCMatrix::BCMatrix(Matrix &mat) +{ + set_transform(mat); +} + +BCMatrix::BCMatrix(Object *ob) +{ + set_transform(ob); +} + +BCMatrix::BCMatrix() +{ + unit(); +} + +BCMatrix::BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis global_up_axis) +{ + float mrot[3][3]; + float mat[4][4]; + mat3_from_axis_conversion( + BC_DEFAULT_FORWARD, BC_DEFAULT_UP, global_forward_axis, global_up_axis, mrot); + + transpose_m3(mrot); // TODO: Verify that mat3_from_axis_conversion() returns a transposed matrix + copy_m4_m3(mat, mrot); + set_transform(mat); +} + +void BCMatrix::add_transform(const Matrix &mat, bool inverse) +{ + add_transform(this->matrix, mat, this->matrix, inverse); +} + +void BCMatrix::add_transform(const BCMatrix &mat, bool inverse) +{ + add_transform(this->matrix, mat.matrix, this->matrix, inverse); +} + +void BCMatrix::apply_transform(const BCMatrix &mat, bool inverse) +{ + apply_transform(this->matrix, mat.matrix, this->matrix, inverse); +} + +void BCMatrix::add_transform(Matrix &to, const Matrix &transform, const Matrix &from, bool inverse) +{ + if (inverse) { + Matrix globinv; + invert_m4_m4(globinv, transform); + add_transform(to, globinv, from, /*inverse=*/false); + } + else { + mul_m4_m4m4(to, transform, from); + } +} + +void BCMatrix::apply_transform(Matrix &to, + const Matrix &transform, + const Matrix &from, + bool inverse) +{ + Matrix globinv; + invert_m4_m4(globinv, transform); + if (inverse) { + add_transform(to, globinv, from, /*inverse=*/false); + } + else { + mul_m4_m4m4(to, transform, from); + mul_m4_m4m4(to, to, globinv); + } +} + +void BCMatrix::add_inverted_transform(Matrix &to, const Matrix &transform, const Matrix &from) +{ + Matrix workmat; + invert_m4_m4(workmat, transform); + mul_m4_m4m4(to, workmat, from); +} + +void BCMatrix::set_transform(Object *ob) +{ + Matrix lmat; + + BKE_object_matrix_local_get(ob, lmat); + copy_m4_m4(matrix, lmat); + + mat4_decompose(this->loc, this->q, this->size, lmat); + quat_to_compatible_eul(this->rot, ob->rot, this->q); +} + +void BCMatrix::set_transform(Matrix &mat) +{ + copy_m4_m4(matrix, mat); + mat4_decompose(this->loc, this->q, this->size, mat); + quat_to_eul(this->rot, this->q); +} + +void BCMatrix::copy(Matrix &out, Matrix &in) +{ + /* destination comes first: */ + memcpy(out, in, sizeof(Matrix)); +} + +void BCMatrix::transpose(Matrix &mat) +{ + transpose_m4(mat); +} + +void BCMatrix::sanitize(Matrix &mat, int precision) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + double val = (double)mat[i][j]; + val = double_round(val, precision); + mat[i][j] = (float)val; + } + } +} + +void BCMatrix::sanitize(DMatrix &mat, int precision) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + mat[i][j] = double_round(mat[i][j], precision); + } + } +} + +void BCMatrix::unit() +{ + unit_m4(this->matrix); + mat4_decompose(this->loc, this->q, this->size, this->matrix); + quat_to_eul(this->rot, this->q); +} + +/* We need double here because the OpenCollada API needs it. + * precision = -1 indicates to not limit the precision. */ +void BCMatrix::get_matrix(DMatrix &mat, const bool transposed, const int precision) const +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float val = (transposed) ? matrix[j][i] : matrix[i][j]; + if (precision >= 0) { + val = floor((val * pow(10, precision) + 0.5)) / pow(10, precision); + } + mat[i][j] = val; + } + } +} + +void BCMatrix::get_matrix(Matrix &mat, + const bool transposed, + const int precision, + const bool inverted) const +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + float val = (transposed) ? matrix[j][i] : matrix[i][j]; + if (precision >= 0) { + val = floor((val * pow(10, precision) + 0.5)) / pow(10, precision); + } + mat[i][j] = val; + } + } + + if (inverted) { + invert_m4(mat); + } +} + +const bool BCMatrix::in_range(const BCMatrix &other, float distance) const +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + if (fabs(other.matrix[i][j] - matrix[i][j]) > distance) { + return false; + } + } + } + return true; +} + +float (&BCMatrix::location() const)[3] +{ + return loc; +} + +float (&BCMatrix::rotation() const)[3] +{ + return rot; +} + +float (&BCMatrix::scale() const)[3] +{ + return size; +} + +float (&BCMatrix::quat() const)[4] +{ + return q; +} diff --git a/source/blender/io/collada/BCMath.h b/source/blender/io/collada/BCMath.h new file mode 100644 index 00000000000..9ecea85b08c --- /dev/null +++ b/source/blender/io/collada/BCMath.h @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __BCMATH_H__ +#define __BCMATH_H__ + +#include "BlenderTypes.h" + +extern "C" { +#include "BKE_object.h" +#include "BLI_math.h" +} + +class BCQuat { + private: + mutable Quat q; + + public: + BCQuat(const BCQuat &other) + { + copy_v4_v4(q, other.q); + } + + BCQuat(Quat &other) + { + copy_v4_v4(q, other); + } + + BCQuat() + { + unit_qt(q); + } + + Quat &quat() + { + return q; + } + + void rotate_to(Matrix &mat_to); +}; + +class BCMatrix { + + private: + mutable float matrix[4][4]; + mutable float size[3]; + mutable float rot[3]; + mutable float loc[3]; + mutable float q[4]; + + void unit(); + void copy(Matrix &r, Matrix &a); + + public: + float (&location() const)[3]; + float (&rotation() const)[3]; + float (&scale() const)[3]; + float (&quat() const)[4]; + + BCMatrix(BC_global_forward_axis global_forward_axis, BC_global_up_axis global_up_axis); + BCMatrix(const BCMatrix &mat); + BCMatrix(Matrix &mat); + BCMatrix(Object *ob); + BCMatrix(); + + void get_matrix(DMatrix &matrix, const bool transposed = false, const int precision = -1) const; + void get_matrix(Matrix &matrix, + const bool transposed = false, + const int precision = -1, + const bool inverted = false) const; + void set_transform(Object *ob); + void set_transform(Matrix &mat); + void add_transform(Matrix &to, + const Matrix &transform, + const Matrix &from, + const bool inverted = false); + void apply_transform(Matrix &to, + const Matrix &transform, + const Matrix &from, + const bool inverted = false); + void add_inverted_transform(Matrix &to, const Matrix &transform, const Matrix &from); + void add_transform(const Matrix &matrix, const bool inverted = false); + void add_transform(const BCMatrix &matrix, const bool inverted = false); + void apply_transform(const BCMatrix &matrix, const bool inverted = false); + + const bool in_range(const BCMatrix &other, float distance) const; + + static void sanitize(Matrix &matrix, int precision); + static void sanitize(DMatrix &matrix, int precision); + static void transpose(Matrix &matrix); +}; + +#endif /* __BCMATH_H__ */ diff --git a/source/blender/io/collada/BCSampleData.cpp b/source/blender/io/collada/BCSampleData.cpp new file mode 100644 index 00000000000..7e23a2de00f --- /dev/null +++ b/source/blender/io/collada/BCSampleData.cpp @@ -0,0 +1,97 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#include "BCSampleData.h" +#include "collada_utils.h" + +BCSample::~BCSample() +{ + BCBoneMatrixMap::iterator it; + for (it = bonemats.begin(); it != bonemats.end(); ++it) { + delete it->second; + } +} + +void BCSample::add_bone_matrix(Bone *bone, Matrix &mat) +{ + BCMatrix *matrix; + BCBoneMatrixMap::const_iterator it = bonemats.find(bone); + if (it != bonemats.end()) { + throw std::invalid_argument("bone " + std::string(bone->name) + " already defined before"); + } + matrix = new BCMatrix(mat); + bonemats[bone] = matrix; +} + +/* Get channel value */ +const bool BCSample::get_value(std::string channel_target, const int array_index, float *val) const +{ + std::string bname = bc_string_before(channel_target, "."); + std::string channel_type = bc_string_after(channel_target, "."); + + const BCMatrix *matrix = &obmat; + if (bname != channel_target) { + bname = bname.substr(2); + bname = bc_string_before(bname, "\""); + BCBoneMatrixMap::const_iterator it; + for (it = bonemats.begin(); it != bonemats.end(); ++it) { + Bone *bone = it->first; + if (bname == bone->name) { + matrix = it->second; + break; + } + } + } + else { + matrix = &obmat; + } + + if (channel_type == "location") { + *val = matrix->location()[array_index]; + } + else if (channel_type == "scale") { + *val = matrix->scale()[array_index]; + } + else if (channel_type == "rotation" || channel_type == "rotation_euler") { + *val = matrix->rotation()[array_index]; + } + else if (channel_type == "rotation_quaternion") { + *val = matrix->quat()[array_index]; + } + else { + *val = 0; + return false; + } + + return true; +} + +const BCMatrix *BCSample::get_matrix(Bone *bone) const +{ + BCBoneMatrixMap::const_iterator it = bonemats.find(bone); + if (it == bonemats.end()) { + return NULL; + } + return it->second; +} + +const BCMatrix &BCSample::get_matrix() const +{ + return obmat; +} diff --git a/source/blender/io/collada/BCSampleData.h b/source/blender/io/collada/BCSampleData.h new file mode 100644 index 00000000000..07ecb544c71 --- /dev/null +++ b/source/blender/io/collada/BCSampleData.h @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +#ifndef __BCSAMPLEDATA_H__ +#define __BCSAMPLEDATA_H__ + +#include +#include +#include + +#include "ExportSettings.h" +#include "BCSampleData.h" +#include "BCMath.h" + +extern "C" { +#include "BKE_object.h" +#include "BLI_math_rotation.h" +#include "DNA_object_types.h" +#include "DNA_armature_types.h" +#include "DNA_material_types.h" +#include "DNA_light_types.h" +#include "DNA_camera_types.h" +} + +typedef std::map BCBoneMatrixMap; + +class BCSample { + private: + BCMatrix obmat; + BCBoneMatrixMap bonemats; /* For Armature animation */ + + public: + BCSample(Object *ob) : obmat(ob) + { + } + + ~BCSample(); + + void add_bone_matrix(Bone *bone, Matrix &mat); + + const bool get_value(std::string channel_target, const int array_index, float *val) const; + const BCMatrix &get_matrix() const; + const BCMatrix *get_matrix(Bone *bone) const; // returns NULL if bone is not animated +}; + +typedef std::map BCSampleMap; +typedef std::map BCFrameSampleMap; +typedef std::map BCMatrixSampleMap; + +#endif /* __BCSAMPLEDATA_H__ */ diff --git a/source/blender/io/collada/BlenderContext.cpp b/source/blender/io/collada/BlenderContext.cpp new file mode 100644 index 00000000000..a9783a9b9c4 --- /dev/null +++ b/source/blender/io/collada/BlenderContext.cpp @@ -0,0 +1,156 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +#include "BlenderContext.h" +#include "ExportSettings.h" + +#include "BKE_scene.h" + +bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer) +{ + Object *root = bc_get_highest_exported_ancestor_or_self(export_set, ob, view_layer); + return (root == ob); +} + +/** + * Returns the highest selected ancestor + * returns NULL if no ancestor is selected + * IMPORTANT: This function expects that all exported objects have set: + * ob->id.tag & LIB_TAG_DOIT + */ +Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set, + Object *ob, + ViewLayer *view_layer) +{ + Object *ancestor = ob; + while (ob->parent) { + if (bc_is_in_Export_set(export_set, ob->parent, view_layer)) { + ancestor = ob->parent; + } + ob = ob->parent; + } + return ancestor; +} + +void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer) +{ + Base *base; + for (base = (Base *)view_layer->object_bases.first; base; base = base->next) { + Object *cob = base->object; + if (cob->parent == ob) { + switch (ob->type) { + case OB_MESH: + case OB_CAMERA: + case OB_LAMP: + case OB_EMPTY: + case OB_ARMATURE: + child_set.push_back(cob); + default: + break; + } + } + } +} + +bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer) +{ + bool to_export = (BLI_linklist_index(export_set, ob) != -1); + + if (!to_export) { + /* Mark this object as to_export even if it is not in the + export list, but it contains children to export */ + + std::vector children; + bc_get_children(children, ob, view_layer); + for (int i = 0; i < children.size(); i++) { + if (bc_is_in_Export_set(export_set, children[i], view_layer)) { + to_export = true; + break; + } + } + } + return to_export; +} + +int bc_is_marked(Object *ob) +{ + return ob && (ob->id.tag & LIB_TAG_DOIT); +} + +void bc_remove_mark(Object *ob) +{ + ob->id.tag &= ~LIB_TAG_DOIT; +} + +void bc_set_mark(Object *ob) +{ + ob->id.tag |= LIB_TAG_DOIT; +} + +BlenderContext::BlenderContext(bContext *C) +{ + context = C; + main = CTX_data_main(C); + scene = CTX_data_scene(C); + view_layer = CTX_data_view_layer(C); + depsgraph = nullptr; // create only when needed +} + +bContext *BlenderContext::get_context() +{ + return context; +} + +Depsgraph *BlenderContext::get_depsgraph() +{ + if (!depsgraph) { + depsgraph = BKE_scene_get_depsgraph(main, scene, view_layer, true); + } + return depsgraph; +} + +Scene *BlenderContext::get_scene() +{ + return scene; +} + +Scene *BlenderContext::get_evaluated_scene() +{ + Scene *scene_eval = DEG_get_evaluated_scene(get_depsgraph()); + return scene_eval; +} + +Object *BlenderContext::get_evaluated_object(Object *ob) +{ + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + return ob_eval; +} + +ViewLayer *BlenderContext::get_view_layer() +{ + return view_layer; +} + +Main *BlenderContext::get_main() +{ + return main; +} diff --git a/source/blender/io/collada/BlenderContext.h b/source/blender/io/collada/BlenderContext.h new file mode 100644 index 00000000000..50781e8eede --- /dev/null +++ b/source/blender/io/collada/BlenderContext.h @@ -0,0 +1,73 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __BLENDERCONTEXT_H__ +#define __BLENDERCONTEXT_H__ + +#ifdef __cplusplus + +extern "C" { +#endif + +#include "DNA_object_types.h" +#include "BLI_linklist.h" +#include "BKE_context.h" +#include "BKE_main.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" +#include "DNA_layer_types.h" +#include "BlenderTypes.h" + +static const BC_global_forward_axis BC_DEFAULT_FORWARD = BC_GLOBAL_FORWARD_Y; +static const BC_global_up_axis BC_DEFAULT_UP = BC_GLOBAL_UP_Z; + +bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer); +bool bc_is_base_node(LinkNode *export_set, Object *ob, ViewLayer *view_layer); +Object *bc_get_highest_exported_ancestor_or_self(LinkNode *export_set, + Object *ob, + ViewLayer *view_layer); +int bc_is_marked(Object *ob); +void bc_remove_mark(Object *ob); +void bc_set_mark(Object *ob); + +#ifdef __cplusplus +} + +class BlenderContext { + private: + bContext *context; + Depsgraph *depsgraph; + Scene *scene; + ViewLayer *view_layer; + Main *main; + + public: + BlenderContext(bContext *C); + bContext *get_context(); + Depsgraph *get_depsgraph(); + Scene *get_scene(); + Scene *get_evaluated_scene(); + Object *get_evaluated_object(Object *ob); + ViewLayer *get_view_layer(); + Main *get_main(); +}; +#endif + +#endif diff --git a/source/blender/io/collada/BlenderTypes.h b/source/blender/io/collada/BlenderTypes.h new file mode 100644 index 00000000000..0e024be2374 --- /dev/null +++ b/source/blender/io/collada/BlenderTypes.h @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __BLENDERTYPES_H__ +#define __BLENDERTYPES_H__ + +typedef float(Vector)[3]; +typedef float(Quat)[4]; +typedef float(Color)[4]; +typedef float(Matrix)[4][4]; +typedef double(DMatrix)[4][4]; + +typedef enum BC_global_forward_axis { + BC_GLOBAL_FORWARD_X = 0, + BC_GLOBAL_FORWARD_Y = 1, + BC_GLOBAL_FORWARD_Z = 2, + BC_GLOBAL_FORWARD_MINUS_X = 3, + BC_GLOBAL_FORWARD_MINUS_Y = 4, + BC_GLOBAL_FORWARD_MINUS_Z = 5 +} BC_global_forward_axis; + +typedef enum BC_global_up_axis { + BC_GLOBAL_UP_X = 0, + BC_GLOBAL_UP_Y = 1, + BC_GLOBAL_UP_Z = 2, + BC_GLOBAL_UP_MINUS_X = 3, + BC_GLOBAL_UP_MINUS_Y = 4, + BC_GLOBAL_UP_MINUS_Z = 5 +} BC_global_up_axis; + +#endif diff --git a/source/blender/io/collada/CMakeLists.txt b/source/blender/io/collada/CMakeLists.txt new file mode 100644 index 00000000000..8ffce9e3e7e --- /dev/null +++ b/source/blender/io/collada/CMakeLists.txt @@ -0,0 +1,147 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2006, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +remove_strict_flags() +FIND_FILE(OPENCOLLADA_ANIMATION_CLIP + NAMES + COLLADAFWAnimationClip.h + PATHS + ${OPENCOLLADA_INCLUDE_DIRS} + NO_DEFAULT_PATH +) + +if(OPENCOLLADA_ANIMATION_CLIP) + add_definitions(-DWITH_OPENCOLLADA_ANIMATION_CLIP) +endif() + +set(INC + . + ../../blenkernel + ../../blenlib + ../../blentranslation + ../../depsgraph + ../../editors/include + ../../imbuf + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ../../ikplugin + ../../../../intern/iksolver/extern + ../../bmesh +) + +set(INC_SYS + ${OPENCOLLADA_INCLUDE_DIRS} +) + +set(SRC + AnimationClipExporter.cpp + AnimationExporter.cpp + AnimationImporter.cpp + ArmatureExporter.cpp + ArmatureImporter.cpp + BCAnimationCurve.cpp + BCAnimationSampler.cpp + BCMath.cpp + BCSampleData.cpp + BlenderContext.cpp + CameraExporter.cpp + ControllerExporter.cpp + DocumentExporter.cpp + DocumentImporter.cpp + EffectExporter.cpp + ErrorHandler.cpp + ExportSettings.cpp + ExtraHandler.cpp + ExtraTags.cpp + GeometryExporter.cpp + ImageExporter.cpp + ImportSettings.cpp + InstanceWriter.cpp + LightExporter.cpp + MaterialExporter.cpp + Materials.cpp + MeshImporter.cpp + SceneExporter.cpp + SkinInfo.cpp + TransformReader.cpp + TransformWriter.cpp + collada.cpp + collada_internal.cpp + collada_utils.cpp + + AnimationClipExporter.h + AnimationExporter.h + AnimationImporter.h + ArmatureExporter.h + ArmatureImporter.h + BCAnimationCurve.h + BCAnimationSampler.h + BCMath.h + BCSampleData.h + BlenderContext.h + BlenderTypes.h + CameraExporter.h + ControllerExporter.h + DocumentExporter.h + DocumentImporter.h + EffectExporter.h + ErrorHandler.h + ExportSettings.h + ExtraHandler.h + ExtraTags.h + GeometryExporter.h + ImageExporter.h + ImportSettings.h + InstanceWriter.h + LightExporter.h + MaterialExporter.h + Materials.h + MeshImporter.h + SceneExporter.h + SkinInfo.h + TransformReader.h + TransformWriter.h + collada.h + collada_internal.h + collada_utils.h +) + +set(LIB + ${OPENCOLLADA_LIBRARIES} + ${PCRE_LIBRARIES} + ${XML2_LIBRARIES} +) + +if(WITH_BUILDINFO) + add_definitions(-DWITH_BUILDINFO) +endif() + +if(WITH_INTERNATIONAL) + add_definitions(-DWITH_INTERNATIONAL) +endif() + +if(CMAKE_COMPILER_IS_GNUCXX) + # COLLADAFWArray.h gives error with gcc 4.5 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") +endif() + +blender_add_lib(bf_collada "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/io/collada/CameraExporter.cpp b/source/blender/io/collada/CameraExporter.cpp new file mode 100644 index 00000000000..74862c44270 --- /dev/null +++ b/source/blender/io/collada/CameraExporter.cpp @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +#include "COLLADASWCamera.h" + +extern "C" { +#include "DNA_camera_types.h" +} +#include "CameraExporter.h" + +#include "collada_internal.h" + +CamerasExporter::CamerasExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) + : COLLADASW::LibraryCameras(sw), export_settings(export_settings) +{ +} + +template +void forEachCameraObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) +{ + LinkNode *node; + for (node = export_set; node; node = node->next) { + Object *ob = (Object *)node->link; + + if (ob->type == OB_CAMERA && ob->data) { + f(ob, sce); + } + } +} + +void CamerasExporter::exportCameras(Scene *sce) +{ + openLibrary(); + + forEachCameraObjectInExportSet(sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); +} +void CamerasExporter::operator()(Object *ob, Scene *sce) +{ + Camera *cam = (Camera *)ob->data; + std::string cam_id(get_camera_id(ob)); + std::string cam_name(id_name(cam)); + + switch (cam->type) { + case CAM_PANO: + case CAM_PERSP: { + COLLADASW::PerspectiveOptic persp(mSW); + persp.setXFov(RAD2DEGF(focallength_to_fov(cam->lens, cam->sensor_x)), "xfov"); + persp.setAspectRatio((float)(sce->r.xsch) / (float)(sce->r.ysch), false, "aspect_ratio"); + persp.setZFar(cam->clip_end, false, "zfar"); + persp.setZNear(cam->clip_start, false, "znear"); + COLLADASW::Camera ccam(mSW, &persp, cam_id, cam_name); + exportBlenderProfile(ccam, cam); + addCamera(ccam); + + break; + } + case CAM_ORTHO: + default: { + COLLADASW::OrthographicOptic ortho(mSW); + ortho.setXMag(cam->ortho_scale / 2, "xmag"); + ortho.setAspectRatio((float)(sce->r.xsch) / (float)(sce->r.ysch), false, "aspect_ratio"); + ortho.setZFar(cam->clip_end, false, "zfar"); + ortho.setZNear(cam->clip_start, false, "znear"); + COLLADASW::Camera ccam(mSW, &ortho, cam_id, cam_name); + exportBlenderProfile(ccam, cam); + addCamera(ccam); + break; + } + } +} +bool CamerasExporter::exportBlenderProfile(COLLADASW::Camera &cm, Camera *cam) +{ + cm.addExtraTechniqueParameter("blender", "shiftx", cam->shiftx); + cm.addExtraTechniqueParameter("blender", "shifty", cam->shifty); + cm.addExtraTechniqueParameter("blender", "dof_distance", cam->dof.focus_distance); + return true; +} diff --git a/source/blender/io/collada/CameraExporter.h b/source/blender/io/collada/CameraExporter.h new file mode 100644 index 00000000000..04bcc4a5dad --- /dev/null +++ b/source/blender/io/collada/CameraExporter.h @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __CAMERAEXPORTER_H__ +#define __CAMERAEXPORTER_H__ + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryCameras.h" + +extern "C" { +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +} + +#include "ExportSettings.h" +#include "DNA_camera_types.h" + +class CamerasExporter : COLLADASW::LibraryCameras { + public: + CamerasExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); + void exportCameras(Scene *sce); + void operator()(Object *ob, Scene *sce); + + private: + bool exportBlenderProfile(COLLADASW::Camera &cla, Camera *cam); + BCExportSettings &export_settings; +}; + +#endif diff --git a/source/blender/io/collada/ControllerExporter.cpp b/source/blender/io/collada/ControllerExporter.cpp new file mode 100644 index 00000000000..0119aba7dfd --- /dev/null +++ b/source/blender/io/collada/ControllerExporter.cpp @@ -0,0 +1,649 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "COLLADASWBaseInputElement.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWSource.h" + +#include "DNA_action_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + +#include "BKE_action.h" +#include "BKE_armature.h" + +extern "C" { +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +} + +#include "ED_armature.h" + +#include "BLI_listbase.h" + +#include "GeometryExporter.h" +#include "ArmatureExporter.h" +#include "ControllerExporter.h" +#include "SceneExporter.h" + +#include "collada_utils.h" + +bool ControllerExporter::is_skinned_mesh(Object *ob) +{ + return bc_get_assigned_armature(ob) != NULL; +} + +void ControllerExporter::write_bone_URLs(COLLADASW::InstanceController &ins, + Object *ob_arm, + Bone *bone) +{ + if (bc_is_root_bone(bone, this->export_settings.get_deform_bones_only())) { + std::string node_id = translate_id(id_name(ob_arm) + "_" + bone->name); + ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, node_id)); + } + else { + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + write_bone_URLs(ins, ob_arm, child); + } + } +} + +bool ControllerExporter::add_instance_controller(Object *ob) +{ + Object *ob_arm = bc_get_assigned_armature(ob); + bArmature *arm = (bArmature *)ob_arm->data; + + const std::string &controller_id = get_controller_id(ob_arm, ob); + + COLLADASW::InstanceController ins(mSW); + ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id)); + + Mesh *me = (Mesh *)ob->data; + if (!me->dvert) { + return false; + } + + /* write root bone URLs */ + Bone *bone; + for (bone = (Bone *)arm->bonebase.first; bone; bone = bone->next) { + write_bone_URLs(ins, ob_arm, bone); + } + + InstanceWriter::add_material_bindings( + ins.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); + + ins.add(); + return true; +} + +void ControllerExporter::export_controllers() +{ + Scene *sce = blender_context.get_scene(); + openLibrary(); + + GeometryFunctor gf; + gf.forEachMeshObjectInExportSet( + sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); +} + +void ControllerExporter::operator()(Object *ob) +{ + Object *ob_arm = bc_get_assigned_armature(ob); + Key *key = BKE_key_from_object(ob); + + if (ob_arm) { + export_skin_controller(ob, ob_arm); + } + if (key && this->export_settings.get_include_shapekeys()) { + export_morph_controller(ob, key); + } +} +#if 0 + +bool ArmatureExporter::already_written(Object *ob_arm) +{ + return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != + written_armatures.end(); +} + +void ArmatureExporter::wrote(Object *ob_arm) +{ + written_armatures.push_back(ob_arm); +} + +void ArmatureExporter::find_objects_using_armature(Object *ob_arm, + std::vector &objects, + Scene *sce) +{ + objects.clear(); + + Base *base = (Base *)sce->base.first; + while (base) { + Object *ob = base->object; + + if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) { + objects.push_back(ob); + } + + base = base->next; + } +} +#endif + +std::string ControllerExporter::get_controller_id(Object *ob_arm, Object *ob) +{ + return translate_id(id_name(ob_arm)) + "_" + translate_id(id_name(ob)) + + SKIN_CONTROLLER_ID_SUFFIX; +} + +std::string ControllerExporter::get_controller_id(Key *key, Object *ob) +{ + return translate_id(id_name(ob)) + MORPH_CONTROLLER_ID_SUFFIX; +} + +/* ob should be of type OB_MESH + * both args are required */ +void ControllerExporter::export_skin_controller(Object *ob, Object *ob_arm) +{ + /* joint names + * joint inverse bind matrices + * vertex weights */ + + /* input: + * joint names: ob -> vertex group names + * vertex group weights: me->dvert -> groups -> index, weight */ + + bool use_instantiation = this->export_settings.get_use_object_instantiation(); + Mesh *me; + + if (((Mesh *)ob->data)->dvert == NULL) { + return; + } + + me = bc_get_mesh_copy(blender_context, + ob, + this->export_settings.get_export_mesh_type(), + this->export_settings.get_apply_modifiers(), + this->export_settings.get_triangulate()); + + std::string controller_name = id_name(ob_arm); + std::string controller_id = get_controller_id(ob_arm, ob); + + openSkin(controller_id, + controller_name, + COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation))); + + add_bind_shape_mat(ob); + + std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id); + std::string inv_bind_mat_source_id = add_inv_bind_mats_source( + ob_arm, &ob->defbase, controller_id); + + std::list vcounts; + std::list joints; + std::list weights; + + { + int i, j; + + /* def group index -> joint index */ + std::vector joint_index_by_def_index; + bDeformGroup *def; + + for (def = (bDeformGroup *)ob->defbase.first, i = 0, j = 0; def; def = def->next, i++) { + if (is_bone_defgroup(ob_arm, def)) { + joint_index_by_def_index.push_back(j++); + } + else { + joint_index_by_def_index.push_back(-1); + } + } + + int oob_counter = 0; + for (i = 0; i < me->totvert; i++) { + MDeformVert *vert = &me->dvert[i]; + std::map jw; + + /* We're normalizing the weights later */ + float sumw = 0.0f; + + for (j = 0; j < vert->totweight; j++) { + uint idx = vert->dw[j].def_nr; + if (idx >= joint_index_by_def_index.size()) { + /* XXX: Maybe better find out where and + * why the Out Of Bound indexes get created ? */ + oob_counter += 1; + } + else { + int joint_index = joint_index_by_def_index[idx]; + if (joint_index != -1 && vert->dw[j].weight > 0.0f) { + jw[joint_index] += vert->dw[j].weight; + sumw += vert->dw[j].weight; + } + } + } + + if (sumw > 0.0f) { + float invsumw = 1.0f / sumw; + vcounts.push_back(jw.size()); + for (std::map::iterator m = jw.begin(); m != jw.end(); ++m) { + joints.push_back((*m).first); + weights.push_back(invsumw * (*m).second); + } + } + else { + vcounts.push_back(0); +#if 0 + vcounts.push_back(1); + joints.push_back(-1); + weights.push_back(1.0f); +#endif + } + } + + if (oob_counter > 0) { + fprintf(stderr, + "Ignored %d Vertex weights which use index to non existing VGroup %zu.\n", + oob_counter, + joint_index_by_def_index.size()); + } + } + + std::string weights_source_id = add_weights_source(me, controller_id, weights); + add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id); + add_vertex_weights_element(weights_source_id, joints_source_id, vcounts, joints); + + BKE_id_free(NULL, me); + + closeSkin(); + closeController(); +} + +void ControllerExporter::export_morph_controller(Object *ob, Key *key) +{ + bool use_instantiation = this->export_settings.get_use_object_instantiation(); + Mesh *me; + + me = bc_get_mesh_copy(blender_context, + ob, + this->export_settings.get_export_mesh_type(), + this->export_settings.get_apply_modifiers(), + this->export_settings.get_triangulate()); + + std::string controller_name = id_name(ob) + "-morph"; + std::string controller_id = get_controller_id(key, ob); + + openMorph( + controller_id, + controller_name, + COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, use_instantiation))); + + std::string targets_id = add_morph_targets(key, ob); + std::string morph_weights_id = add_morph_weights(key, ob); + + COLLADASW::TargetsElement targets(mSW); + + COLLADASW::InputList &input = targets.getInputList(); + + input.push_back(COLLADASW::Input( + COLLADASW::InputSemantic::MORPH_TARGET, // constant declared in COLLADASWInputList.h + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, targets_id))); + input.push_back( + COLLADASW::Input(COLLADASW::InputSemantic::MORPH_WEIGHT, + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, morph_weights_id))); + targets.add(); + + BKE_id_free(NULL, me); + + /* support for animations + * can also try the base element and param alternative */ + add_weight_extras(key); + closeMorph(); + closeController(); +} + +std::string ControllerExporter::add_morph_targets(Key *key, Object *ob) +{ + std::string source_id = translate_id(id_name(ob)) + TARGETS_SOURCE_ID_SUFFIX; + + COLLADASW::IdRefSource source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(key->totkey - 1); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("IDREF"); + + source.prepareToAppendValues(); + + KeyBlock *kb = (KeyBlock *)key->block.first; + /* skip the basis */ + kb = kb->next; + for (; kb; kb = kb->next) { + std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name); + source.appendValues(geom_id); + } + + source.finish(); + + return source_id; +} + +std::string ControllerExporter::add_morph_weights(Key *key, Object *ob) +{ + std::string source_id = translate_id(id_name(ob)) + WEIGHTS_SOURCE_ID_SUFFIX; + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(key->totkey - 1); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("MORPH_WEIGHT"); + + source.prepareToAppendValues(); + + KeyBlock *kb = (KeyBlock *)key->block.first; + /* skip the basis */ + kb = kb->next; + for (; kb; kb = kb->next) { + float weight = kb->curval; + source.appendValues(weight); + } + source.finish(); + + return source_id; +} + +/* Added to implement support for animations. */ +void ControllerExporter::add_weight_extras(Key *key) +{ + /* can also try the base element and param alternative */ + COLLADASW::BaseExtraTechnique extra; + + KeyBlock *kb = (KeyBlock *)key->block.first; + /* skip the basis */ + kb = kb->next; + for (; kb; kb = kb->next) { + /* XXX why is the weight not used here and set to 0.0? + * float weight = kb->curval; */ + extra.addExtraTechniqueParameter("KHR", "morph_weights", 0.000, "MORPH_WEIGHT_TO_TARGET"); + } +} + +void ControllerExporter::add_joints_element(ListBase *defbase, + const std::string &joints_source_id, + const std::string &inv_bind_mat_source_id) +{ + COLLADASW::JointsElement joints(mSW); + COLLADASW::InputList &input = joints.getInputList(); + + input.push_back(COLLADASW::Input( + COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id))); + input.push_back( + COLLADASW::Input(COLLADASW::InputSemantic::BINDMATRIX, + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id))); + joints.add(); +} + +void ControllerExporter::add_bind_shape_mat(Object *ob) +{ + double bind_mat[4][4]; + float f_obmat[4][4]; + BKE_object_matrix_local_get(ob, f_obmat); + + if (export_settings.get_apply_global_orientation()) { + // do nothing, rotation is going to be applied to the Data + } + else { + bc_add_global_transform(f_obmat, export_settings.get_global_transform()); + } + + // UnitConverter::mat4_to_dae_double(bind_mat, ob->obmat); + UnitConverter::mat4_to_dae_double(bind_mat, f_obmat); + if (this->export_settings.get_limit_precision()) { + BCMatrix::sanitize(bind_mat, LIMITTED_PRECISION); + } + + addBindShapeTransform(bind_mat); +} + +std::string ControllerExporter::add_joints_source(Object *ob_arm, + ListBase *defbase, + const std::string &controller_id) +{ + std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX; + + int totjoint = 0; + bDeformGroup *def; + for (def = (bDeformGroup *)defbase->first; def; def = def->next) { + if (is_bone_defgroup(ob_arm, def)) { + totjoint++; + } + } + + COLLADASW::NameSource source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(totjoint); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("JOINT"); + + source.prepareToAppendValues(); + + for (def = (bDeformGroup *)defbase->first; def; def = def->next) { + Bone *bone = get_bone_from_defgroup(ob_arm, def); + if (bone) { + source.appendValues(get_joint_sid(bone)); + } + } + + source.finish(); + + return source_id; +} + +std::string ControllerExporter::add_inv_bind_mats_source(Object *ob_arm, + ListBase *defbase, + const std::string &controller_id) +{ + std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX; + + int totjoint = 0; + for (bDeformGroup *def = (bDeformGroup *)defbase->first; def; def = def->next) { + if (is_bone_defgroup(ob_arm, def)) { + totjoint++; + } + } + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(totjoint); // BLI_listbase_count(defbase)); + source.setAccessorStride(16); + + source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("TRANSFORM"); + + source.prepareToAppendValues(); + + bPose *pose = ob_arm->pose; + bArmature *arm = (bArmature *)ob_arm->data; + + int flag = arm->flag; + + /* put armature in rest position */ + if (!(arm->flag & ARM_RESTPOS)) { + Depsgraph *depsgraph = blender_context.get_depsgraph(); + Scene *scene = blender_context.get_scene(); + + arm->flag |= ARM_RESTPOS; + BKE_pose_where_is(depsgraph, scene, ob_arm); + } + + for (bDeformGroup *def = (bDeformGroup *)defbase->first; def; def = def->next) { + if (is_bone_defgroup(ob_arm, def)) { + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, def->name); + + float mat[4][4]; + float world[4][4]; + float inv_bind_mat[4][4]; + + float bind_mat[4][4]; /* derived from bone->arm_mat */ + + bool has_bindmat = bc_get_property_matrix(pchan->bone, "bind_mat", bind_mat); + + if (!has_bindmat) { + + /* Have no bind matrix stored, try old style <= Blender 2.78 */ + + bc_create_restpose_mat( + this->export_settings, pchan->bone, bind_mat, pchan->bone->arm_mat, true); + + /* SL/OPEN_SIM COMPATIBILITY */ + if (export_settings.get_open_sim()) { + float loc[3]; + float rot[3] = {0, 0, 0}; + float scale[3]; + bc_decompose(bind_mat, loc, NULL, NULL, scale); + + /* Only translations, no rotation vs armature */ + loc_eulO_size_to_mat4(bind_mat, loc, rot, scale, 6); + } + } + + /* make world-space matrix (bind_mat is armature-space) */ + mul_m4_m4m4(world, ob_arm->obmat, bind_mat); + + if (!has_bindmat) { + if (export_settings.get_apply_global_orientation()) { + bc_apply_global_transform(world, export_settings.get_global_transform()); + } + } + + invert_m4_m4(mat, world); + UnitConverter::mat4_to_dae(inv_bind_mat, mat); + if (this->export_settings.get_limit_precision()) { + BCMatrix::sanitize(inv_bind_mat, LIMITTED_PRECISION); + } + source.appendValues(inv_bind_mat); + } + } + + /* back from rest position */ + if (!(flag & ARM_RESTPOS)) { + Depsgraph *depsgraph = blender_context.get_depsgraph(); + Scene *scene = blender_context.get_scene(); + arm->flag = flag; + BKE_pose_where_is(depsgraph, scene, ob_arm); + } + + source.finish(); + + return source_id; +} + +Bone *ControllerExporter::get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def) +{ + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_arm->pose, def->name); + return pchan ? pchan->bone : NULL; +} + +bool ControllerExporter::is_bone_defgroup(Object *ob_arm, bDeformGroup *def) +{ + return get_bone_from_defgroup(ob_arm, def) != NULL; +} + +std::string ControllerExporter::add_weights_source(Mesh *me, + const std::string &controller_id, + const std::list &weights) +{ + std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX; + + COLLADASW::FloatSourceF source(mSW); + source.setId(source_id); + source.setArrayId(source_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(weights.size()); + source.setAccessorStride(1); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("WEIGHT"); + + source.prepareToAppendValues(); + + for (std::list::const_iterator i = weights.begin(); i != weights.end(); ++i) { + source.appendValues(*i); + } + + source.finish(); + + return source_id; +} + +void ControllerExporter::add_vertex_weights_element(const std::string &weights_source_id, + const std::string &joints_source_id, + const std::list &vcounts, + const std::list &joints) +{ + COLLADASW::VertexWeightsElement weightselem(mSW); + COLLADASW::InputList &input = weightselem.getInputList(); + + int offset = 0; + input.push_back(COLLADASW::Input( + COLLADASW::InputSemantic::JOINT, // constant declared in COLLADASWInputList.h + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), + offset++)); + input.push_back( + COLLADASW::Input(COLLADASW::InputSemantic::WEIGHT, + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), + offset++)); + + weightselem.setCount(vcounts.size()); + + /* write number of deformers per vertex */ + COLLADASW::PrimitivesBase::VCountList vcountlist; + + vcountlist.resize(vcounts.size()); + std::copy(vcounts.begin(), vcounts.end(), vcountlist.begin()); + + weightselem.prepareToAppendVCountValues(); + weightselem.appendVertexCount(vcountlist); + + weightselem.CloseVCountAndOpenVElement(); + + /* write deformer index - weight index pairs */ + int weight_index = 0; + for (std::list::const_iterator i = joints.begin(); i != joints.end(); ++i) { + weightselem.appendValues(*i, weight_index++); + } + + weightselem.finish(); +} diff --git a/source/blender/io/collada/ControllerExporter.h b/source/blender/io/collada/ControllerExporter.h new file mode 100644 index 00000000000..ce2ed9fe453 --- /dev/null +++ b/source/blender/io/collada/ControllerExporter.h @@ -0,0 +1,137 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __CONTROLLEREXPORTER_H__ +#define __CONTROLLEREXPORTER_H__ + +#include +#include +//#include + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryControllers.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWInputList.h" +#include "COLLADASWNode.h" +#include "COLLADASWExtraTechnique.h" + +#include "DNA_armature_types.h" +#include "DNA_listBase.h" +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_constraint_types.h" +#include "DNA_scene_types.h" +#include "DNA_key_types.h" + +#include "TransformWriter.h" +#include "InstanceWriter.h" + +#include "ExportSettings.h" + +#include "BKE_key.h" + +class SceneExporter; + +class ControllerExporter : public COLLADASW::LibraryControllers, + protected TransformWriter, + protected InstanceWriter { + private: + BlenderContext &blender_context; + BCExportSettings export_settings; + + public: + // XXX exporter writes wrong data for shared armatures. A separate + // controller should be written for each armature-mesh binding how do + // we make controller ids then? + ControllerExporter(BlenderContext &blender_context, + COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings) + : COLLADASW::LibraryControllers(sw), + blender_context(blender_context), + export_settings(export_settings) + { + } + + bool is_skinned_mesh(Object *ob); + + bool add_instance_controller(Object *ob); + + void export_controllers(); + + void operator()(Object *ob); + + private: +#if 0 + std::vector written_armatures; + + bool already_written(Object *ob_arm); + + void wrote(Object *ob_arm); + + void find_objects_using_armature(Object *ob_arm, std::vector &objects, Scene *sce); +#endif + + std::string get_controller_id(Object *ob_arm, Object *ob); + + std::string get_controller_id(Key *key, Object *ob); + + // ob should be of type OB_MESH + // both args are required + void export_skin_controller(Object *ob, Object *ob_arm); + + void export_morph_controller(Object *ob, Key *key); + + void add_joints_element(ListBase *defbase, + const std::string &joints_source_id, + const std::string &inv_bind_mat_source_id); + + void add_bind_shape_mat(Object *ob); + + std::string add_morph_targets(Key *key, Object *ob); + + std::string add_morph_weights(Key *key, Object *ob); + + void add_weight_extras(Key *key); + + std::string add_joints_source(Object *ob_arm, + ListBase *defbase, + const std::string &controller_id); + + std::string add_inv_bind_mats_source(Object *ob_arm, + ListBase *defbase, + const std::string &controller_id); + + Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup *def); + + bool is_bone_defgroup(Object *ob_arm, bDeformGroup *def); + + std::string add_weights_source(Mesh *me, + const std::string &controller_id, + const std::list &weights); + + void add_vertex_weights_element(const std::string &weights_source_id, + const std::string &joints_source_id, + const std::list &vcount, + const std::list &joints); + + void write_bone_URLs(COLLADASW::InstanceController &ins, Object *ob_arm, Bone *bone); +}; + +#endif diff --git a/source/blender/io/collada/DocumentExporter.cpp b/source/blender/io/collada/DocumentExporter.cpp new file mode 100644 index 00000000000..24a960ab287 --- /dev/null +++ b/source/blender/io/collada/DocumentExporter.cpp @@ -0,0 +1,346 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include +#include +#include +#include // std::find + +#include "COLLADASWCamera.h" +#include "COLLADASWAsset.h" +#include "COLLADASWLibraryVisualScenes.h" +#include "COLLADASWNode.h" +#include "COLLADASWSource.h" +#include "COLLADASWInstanceGeometry.h" +#include "COLLADASWInputList.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWVertices.h" +#include "COLLADASWLibraryAnimations.h" +#include "COLLADASWLibraryImages.h" +#include "COLLADASWLibraryEffects.h" +#include "COLLADASWImage.h" +#include "COLLADASWEffectProfile.h" +#include "COLLADASWColorOrTexture.h" +#include "COLLADASWParamTemplate.h" +#include "COLLADASWParamBase.h" +#include "COLLADASWSurfaceInitOption.h" +#include "COLLADASWSampler.h" +#include "COLLADASWScene.h" +#include "COLLADASWTechnique.h" +#include "COLLADASWTexture.h" +#include "COLLADASWLibraryMaterials.h" +#include "COLLADASWBindMaterial.h" +#include "COLLADASWInstanceCamera.h" +#include "COLLADASWInstanceLight.h" +#include "COLLADASWConstants.h" +#include "COLLADASWLibraryControllers.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWInstanceNode.h" +#include "COLLADASWBaseInputElement.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_collection_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_armature_types.h" +#include "DNA_modifier_types.h" +#include "DNA_userdef_types.h" + +#include "BLI_path_util.h" +#include "BLI_fileops.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_listbase.h" +#include "BLI_utildefines.h" + +#include "BKE_action.h" // pose functions +#include "BKE_animsys.h" +#include "BKE_armature.h" +#include "BKE_blender_version.h" +#include "BKE_customdata.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_appdir.h" + +#include "ED_keyframing.h" +#ifdef WITH_BUILDINFO +extern char build_commit_date[]; +extern char build_commit_time[]; +extern char build_hash[]; +#endif + +#include "RNA_access.h" +} + +#include "collada_internal.h" +#include "collada_utils.h" +#include "DocumentExporter.h" + +extern bool bc_has_object_type(LinkNode *export_set, short obtype); + +// can probably go after refactor is complete +#include "InstanceWriter.h" +#include "TransformWriter.h" + +#include "SceneExporter.h" +#include "ArmatureExporter.h" +#include "AnimationExporter.h" +#include "CameraExporter.h" +#include "ControllerExporter.h" +#include "EffectExporter.h" +#include "GeometryExporter.h" +#include "ImageExporter.h" +#include "LightExporter.h" +#include "MaterialExporter.h" + +#include + +char *bc_CustomData_get_layer_name(const struct CustomData *data, int type, int n) +{ + int layer_index = CustomData_get_layer_index(data, type); + if (layer_index < 0) { + return NULL; + } + + return data->layers[layer_index + n].name; +} + +char *bc_CustomData_get_active_layer_name(const CustomData *data, int type) +{ + /* get the layer index of the active layer of type */ + int layer_index = CustomData_get_active_layer_index(data, type); + if (layer_index < 0) { + return NULL; + } + + return data->layers[layer_index].name; +} + +DocumentExporter::DocumentExporter(BlenderContext &blender_context, ExportSettings *exportSettings) + : blender_context(blender_context), + export_settings(BCExportSettings(exportSettings, blender_context)) +{ +} + +static COLLADABU::NativeString make_temp_filepath(const char *name, const char *extension) +{ + char tempfile[FILE_MAX]; + const char *tempdir = BKE_tempdir_session(); + + if (name == NULL) { + name = "untitled"; + } + + BLI_make_file_string(NULL, tempfile, tempdir, name); + + if (extension) { + BLI_path_extension_ensure(tempfile, FILE_MAX, extension); + } + + COLLADABU::NativeString native_filename = COLLADABU::NativeString( + tempfile, COLLADABU::NativeString::ENCODING_UTF8); + return native_filename; +} + +// TODO: it would be better to instantiate animations rather than create a new one per object +// COLLADA allows this through multiple s in . +// For this to work, we need to know objects that use a certain action. + +int DocumentExporter::exportCurrentScene() +{ + Scene *sce = blender_context.get_scene(); + bContext *C = blender_context.get_context(); + + PointerRNA sceneptr, unit_settings; + PropertyRNA *system; /* unused , *scale; */ + + clear_global_id_map(); + + COLLADABU::NativeString native_filename = make_temp_filepath(NULL, ".dae"); + COLLADASW::StreamWriter *writer = new COLLADASW::StreamWriter(native_filename); + + // open + writer->startDocument(); + + // + COLLADASW::Asset asset(writer); + + RNA_id_pointer_create(&(sce->id), &sceneptr); + unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); + system = RNA_struct_find_property(&unit_settings, "system"); + // scale = RNA_struct_find_property(&unit_settings, "scale_length"); + + std::string unitname = "meter"; + float linearmeasure = RNA_float_get(&unit_settings, "scale_length"); + + switch (RNA_property_enum_get(&unit_settings, system)) { + case USER_UNIT_NONE: + case USER_UNIT_METRIC: + if (linearmeasure == 0.001f) { + unitname = "millimeter"; + } + else if (linearmeasure == 0.01f) { + unitname = "centimeter"; + } + else if (linearmeasure == 0.1f) { + unitname = "decimeter"; + } + else if (linearmeasure == 1.0f) { + unitname = "meter"; + } + else if (linearmeasure == 1000.0f) { + unitname = "kilometer"; + } + break; + case USER_UNIT_IMPERIAL: + if (linearmeasure == 0.0254f) { + unitname = "inch"; + } + else if (linearmeasure == 0.3048f) { + unitname = "foot"; + } + else if (linearmeasure == 0.9144f) { + unitname = "yard"; + } + break; + default: + break; + } + + asset.setUnit(unitname, linearmeasure); + asset.setUpAxisType(COLLADASW::Asset::Z_UP); + asset.getContributor().mAuthor = "Blender User"; + char version_buf[128]; +#ifdef WITH_BUILDINFO + BLI_snprintf(version_buf, + sizeof(version_buf), + "Blender %d.%02d.%d commit date:%s, commit time:%s, hash:%s", + BLENDER_VERSION / 100, + BLENDER_VERSION % 100, + BLENDER_SUBVERSION, + build_commit_date, + build_commit_time, + build_hash); +#else + BLI_snprintf(version_buf, + sizeof(version_buf), + "Blender %d.%02d.%d", + BLENDER_VERSION / 100, + BLENDER_VERSION % 100, + BLENDER_SUBVERSION); +#endif + asset.getContributor().mAuthoringTool = version_buf; + asset.add(); + + LinkNode *export_set = this->export_settings.get_export_set(); + // + if (bc_has_object_type(export_set, OB_CAMERA)) { + CamerasExporter ce(writer, this->export_settings); + ce.exportCameras(sce); + } + + // + if (bc_has_object_type(export_set, OB_LAMP)) { + LightsExporter le(writer, this->export_settings); + le.exportLights(sce); + } + + // + EffectsExporter ee(writer, this->export_settings, key_image_map); + ee.exportEffects(C, sce); + + // + ImagesExporter ie(writer, this->export_settings, key_image_map); + ie.exportImages(sce); + + // + MaterialsExporter me(writer, this->export_settings); + me.exportMaterials(sce); + + // + if (bc_has_object_type(export_set, OB_MESH)) { + GeometryExporter ge(blender_context, writer, this->export_settings); + ge.exportGeom(); + } + + // + ArmatureExporter arm_exporter(blender_context, writer, this->export_settings); + ControllerExporter controller_exporter(blender_context, writer, this->export_settings); + if (bc_has_object_type(export_set, OB_ARMATURE) || + this->export_settings.get_include_shapekeys()) { + controller_exporter.export_controllers(); + } + + // + + SceneExporter se(blender_context, writer, &arm_exporter, this->export_settings); + + if (this->export_settings.get_include_animations()) { + // + AnimationExporter ae(writer, this->export_settings); + ae.exportAnimations(); + } + + se.exportScene(); + + // + std::string scene_name(translate_id(id_name(sce))); + COLLADASW::Scene scene(writer, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, scene_name)); + scene.add(); + + // close + writer->endDocument(); + delete writer; + + // Finally move the created document into place + fprintf(stdout, "Collada export to: %s\n", this->export_settings.get_filepath()); + int status = BLI_rename(native_filename.c_str(), this->export_settings.get_filepath()); + if (status != 0) { + status = BLI_copy(native_filename.c_str(), this->export_settings.get_filepath()); + BLI_delete(native_filename.c_str(), false, false); + } + return status; +} + +void DocumentExporter::exportScenes(const char *filename) +{ +} + +/* + * NOTES: + * + * AnimationExporter::sample_animation enables all curves on armature, this is undesirable for a + * user + */ diff --git a/source/blender/io/collada/DocumentExporter.h b/source/blender/io/collada/DocumentExporter.h new file mode 100644 index 00000000000..70722ae601e --- /dev/null +++ b/source/blender/io/collada/DocumentExporter.h @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __DOCUMENTEXPORTER_H__ +#define __DOCUMENTEXPORTER_H__ + +#include "collada.h" +#include "collada_utils.h" +#include "BlenderContext.h" + +extern "C" { +#include "DNA_customdata_types.h" +} + +class DocumentExporter { + public: + DocumentExporter(BlenderContext &blender_context, ExportSettings *export_settings); + int exportCurrentScene(); + void exportScenes(const char *filename); + + private: + BlenderContext &blender_context; + BCExportSettings export_settings; + KeyImageMap key_image_map; +}; + +#endif diff --git a/source/blender/io/collada/DocumentImporter.cpp b/source/blender/io/collada/DocumentImporter.cpp new file mode 100644 index 00000000000..9b66ff429e1 --- /dev/null +++ b/source/blender/io/collada/DocumentImporter.cpp @@ -0,0 +1,1265 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* TODO: + * * name imported objects + * * import object rotation as euler */ + +#include +#include +#include // sort() + +#include "COLLADAFWRoot.h" +#include "COLLADAFWStableHeaders.h" +#include "COLLADAFWColorOrTexture.h" +#include "COLLADAFWIndexList.h" +#include "COLLADAFWMeshPrimitiveWithFaceVertexCount.h" +#include "COLLADAFWPolygons.h" +#include "COLLADAFWSampler.h" +#include "COLLADAFWTypes.h" +#include "COLLADAFWVisualScene.h" +#include "COLLADAFWArrayPrimitiveType.h" +#include "COLLADAFWLibraryNodes.h" +#include "COLLADAFWCamera.h" +#include "COLLADAFWLight.h" + +#include "COLLADASaxFWLLoader.h" +#include "COLLADASaxFWLIExtraDataCallbackHandler.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" +#include "BLI_fileops.h" + +#include "BKE_camera.h" +#include "BKE_collection.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_layer.h" +#include "BKE_light.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_scene.h" + +#include "BLI_path_util.h" + +#include "DNA_camera_types.h" +#include "DNA_light_types.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "ExtraHandler.h" +#include "ErrorHandler.h" +#include "DocumentImporter.h" +#include "TransformReader.h" + +#include "collada_internal.h" +#include "collada_utils.h" +#include "Materials.h" + +/* + * COLLADA Importer limitations: + * - no multiple scene import, all objects are added to active scene + */ + +// #define COLLADA_DEBUG +// creates empties for each imported bone on layer 2, for debugging +// #define ARMATURE_TEST + +DocumentImporter::DocumentImporter(bContext *C, const ImportSettings *import_settings) + : import_settings(import_settings), + mImportStage(Fetching_Scene_data), + mContext(C), + view_layer(CTX_data_view_layer(mContext)), + armature_importer(&unit_converter, + &mesh_importer, + CTX_data_main(C), + CTX_data_scene(C), + view_layer, + import_settings), + mesh_importer( + &unit_converter, &armature_importer, CTX_data_main(C), CTX_data_scene(C), view_layer), + anim_importer(C, &unit_converter, &armature_importer, CTX_data_scene(C)) +{ +} + +DocumentImporter::~DocumentImporter() +{ + TagsMap::iterator etit; + etit = uid_tags_map.begin(); + while (etit != uid_tags_map.end()) { + delete etit->second; + etit++; + } +} + +bool DocumentImporter::import() +{ + ErrorHandler errorHandler; + COLLADASaxFWL::Loader loader(&errorHandler); + COLLADAFW::Root root(&loader, this); + ExtraHandler *ehandler = new ExtraHandler(this, &(this->anim_importer)); + + loader.registerExtraDataCallbackHandler(ehandler); + + /* deselect all to select new objects */ + BKE_view_layer_base_deselect_all(view_layer); + + std::string mFilename = std::string(this->import_settings->filepath); + const std::string encodedFilename = bc_url_encode(mFilename); + if (!root.loadDocument(encodedFilename)) { + fprintf(stderr, "COLLADAFW::Root::loadDocument() returned false on 1st pass\n"); + delete ehandler; + return false; + } + + if (errorHandler.hasError()) { + delete ehandler; + return false; + } + + /** TODO set up scene graph and such here */ + mImportStage = Fetching_Controller_data; + COLLADASaxFWL::Loader loader2; + COLLADAFW::Root root2(&loader2, this); + + if (!root2.loadDocument(encodedFilename)) { + fprintf(stderr, "COLLADAFW::Root::loadDocument() returned false on 2nd pass\n"); + delete ehandler; + return false; + } + + delete ehandler; + + return true; +} + +void DocumentImporter::cancel(const COLLADAFW::String &errorMessage) +{ + /* TODO: if possible show error info + * + * Should we get rid of invisible Meshes that were created so far + * or maybe create objects at coordinate space origin? + * + * The latter sounds better. */ +} + +void DocumentImporter::start() +{ +} + +void DocumentImporter::finish() +{ + if (mImportStage == Fetching_Controller_data) { + return; + } + + Main *bmain = CTX_data_main(mContext); + /* TODO: create a new scene except the selected - + * use current blender scene for it */ + Scene *sce = CTX_data_scene(mContext); + unit_converter.calculate_scale(*sce); + + std::vector *objects_to_scale = new std::vector(); + + /** TODO Break up and put into 2-pass parsing of DAE */ + std::vector::iterator sit; + for (sit = vscenes.begin(); sit != vscenes.end(); sit++) { + PointerRNA sceneptr, unit_settings; + PropertyRNA *system, *scale; + + /* for scene unit settings: system, scale_length */ + + RNA_id_pointer_create(&sce->id, &sceneptr); + unit_settings = RNA_pointer_get(&sceneptr, "unit_settings"); + system = RNA_struct_find_property(&unit_settings, "system"); + scale = RNA_struct_find_property(&unit_settings, "scale_length"); + + if (this->import_settings->import_units) { + + switch (unit_converter.isMetricSystem()) { + case UnitConverter::Metric: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_METRIC); + break; + case UnitConverter::Imperial: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_IMPERIAL); + break; + default: + RNA_property_enum_set(&unit_settings, system, USER_UNIT_NONE); + break; + } + float unit_factor = unit_converter.getLinearMeter(); + RNA_property_float_set(&unit_settings, scale, unit_factor); + fprintf(stdout, "Collada: Adjusting Blender units to Importset units: %f.\n", unit_factor); + } + + /* Write nodes to scene */ + fprintf(stderr, "+-- Import Scene --------\n"); + const COLLADAFW::NodePointerArray &roots = (*sit)->getRootNodes(); + for (unsigned int i = 0; i < roots.getCount(); i++) { + std::vector *objects_done = write_node(roots[i], NULL, sce, NULL, false); + objects_to_scale->insert( + objects_to_scale->end(), objects_done->begin(), objects_done->end()); + delete objects_done; + } + } + + mesh_importer.optimize_material_assignements(); + + armature_importer.set_tags_map(this->uid_tags_map); + armature_importer.make_armatures(mContext, *objects_to_scale); + armature_importer.make_shape_keys(mContext); + +#if 0 + armature_importer.fix_animation(); +#endif + + for (std::vector::iterator vsit = vscenes.begin(); + vsit != vscenes.end(); + vsit++) { + const COLLADAFW::NodePointerArray &roots = (*vsit)->getRootNodes(); + + for (unsigned int i = 0; i < roots.getCount(); i++) { + translate_anim_recursive(roots[i], NULL, NULL); + } + } + + if (libnode_ob.size()) { + + fprintf(stderr, "| Cleanup: free %d library nodes\n", (int)libnode_ob.size()); + /* free all library_nodes */ + std::vector::iterator it; + for (it = libnode_ob.begin(); it != libnode_ob.end(); it++) { + Object *ob = *it; + BKE_scene_collections_object_remove(bmain, sce, ob, true); + } + libnode_ob.clear(); + } + + bc_match_scale(objects_to_scale, unit_converter, !this->import_settings->import_units); + + delete objects_to_scale; + + /* update scene */ + DEG_id_tag_update(&sce->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(mContext, NC_OBJECT | ND_TRANSFORM, NULL); +} + +void DocumentImporter::translate_anim_recursive(COLLADAFW::Node *node, + COLLADAFW::Node *par = NULL, + Object *parob = NULL) +{ + /* The split in #29246, rootmap must point at actual root when + * calculating bones in apply_curves_as_matrix. - actual root is the root node. + * This has to do with inverse bind poses being world space + * (the sources for skinned bones' restposes) and the way + * non-skinning nodes have their "restpose" recursively calculated. + * XXX TODO: design issue, how to support unrelated joints taking + * part in skinning. */ + if (par) { // && par->getType() == COLLADAFW::Node::JOINT) { + /* par is root if there's no corresp. key in root_map */ + if (root_map.find(par->getUniqueId()) == root_map.end()) { + root_map[node->getUniqueId()] = node; + } + else { + root_map[node->getUniqueId()] = root_map[par->getUniqueId()]; + } + } + +#if 0 + COLLADAFW::Transformation::TransformationType types[] = { + COLLADAFW::Transformation::ROTATE, + COLLADAFW::Transformation::SCALE, + COLLADAFW::Transformation::TRANSLATE, + COLLADAFW::Transformation::MATRIX, + }; + + Object *ob; +#endif + unsigned int i; + + if (node->getType() == COLLADAFW::Node::JOINT && par == NULL) { + /* For Skeletons without root node we have to simulate the + * root node here and recursively enter the same function + * XXX: maybe this can be made more elegant. */ + translate_anim_recursive(node, node, parob); + } + else { + anim_importer.translate_Animations( + node, root_map, object_map, FW_object_map, uid_material_map); + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + for (i = 0; i < children.getCount(); i++) { + translate_anim_recursive(children[i], node, NULL); + } + } +} + +/** + * If the imported file was made with Blender, return the Blender version used, + * otherwise return an empty std::string + */ +std::string DocumentImporter::get_import_version(const COLLADAFW::FileInfo *asset) +{ + const char AUTORING_TOOL[] = "authoring_tool"; + const std::string BLENDER("Blender "); + const COLLADAFW::FileInfo::ValuePairPointerArray &valuePairs = asset->getValuePairArray(); + for (size_t i = 0, count = valuePairs.getCount(); i < count; i++) { + const COLLADAFW::FileInfo::ValuePair *valuePair = valuePairs[i]; + const COLLADAFW::String &key = valuePair->first; + const COLLADAFW::String &value = valuePair->second; + if (key == AUTORING_TOOL) { + if (value.compare(0, BLENDER.length(), BLENDER) == 0) { + /* Was made with Blender, now get version string */ + std::string v = value.substr(BLENDER.length()); + std::string::size_type n = v.find(" "); + if (n > 0) { + return v.substr(0, n); + } + } + } + } + return ""; +} + +/** + * When this method is called, the writer must write the global document asset. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeGlobalAsset(const COLLADAFW::FileInfo *asset) +{ + unit_converter.read_asset(asset); + import_from_version = get_import_version(asset); + anim_importer.set_import_from_version(import_from_version); + return true; +} + +/** + * When this method is called, the writer must write the scene. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeScene(const COLLADAFW::Scene *scene) +{ + /* XXX could store the scene id, but do nothing for now */ + return true; +} +Object *DocumentImporter::create_camera_object(COLLADAFW::InstanceCamera *camera, Scene *sce) +{ + const COLLADAFW::UniqueId &cam_uid = camera->getInstanciatedObjectId(); + if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) { + // fprintf(stderr, "Couldn't find camera by UID.\n"); + return NULL; + } + + Main *bmain = CTX_data_main(mContext); + Object *ob = bc_add_object(bmain, sce, view_layer, OB_CAMERA, NULL); + Camera *cam = uid_camera_map[cam_uid]; + Camera *old_cam = (Camera *)ob->data; + ob->data = cam; + BKE_id_free_us(bmain, old_cam); + return ob; +} + +Object *DocumentImporter::create_light_object(COLLADAFW::InstanceLight *lamp, Scene *sce) +{ + const COLLADAFW::UniqueId &lamp_uid = lamp->getInstanciatedObjectId(); + if (uid_light_map.find(lamp_uid) == uid_light_map.end()) { + fprintf(stderr, "Couldn't find light by UID.\n"); + return NULL; + } + + Main *bmain = CTX_data_main(mContext); + Object *ob = bc_add_object(bmain, sce, view_layer, OB_LAMP, NULL); + Light *la = uid_light_map[lamp_uid]; + Light *old_light = (Light *)ob->data; + ob->data = la; + BKE_id_free_us(bmain, old_light); + return ob; +} + +Object *DocumentImporter::create_instance_node(Object *source_ob, + COLLADAFW::Node *source_node, + COLLADAFW::Node *instance_node, + Scene *sce, + bool is_library_node) +{ + // fprintf(stderr, "create under node id=%s from node id=%s\n", instance_node ? + // instance_node->getOriginalId().c_str() : NULL, source_node ? + // source_node->getOriginalId().c_str() : NULL); + + Main *bmain = CTX_data_main(mContext); + Object *obn = BKE_object_copy(bmain, source_ob); + DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + BKE_collection_object_add_from(bmain, sce, source_ob, obn); + + if (instance_node) { + anim_importer.read_node_transform(instance_node, obn); + /* if we also have a source_node (always ;), take its + * transformation matrix and apply it to the newly instantiated + * object to account for node hierarchy transforms in + * .dae */ + if (source_node) { + COLLADABU::Math::Matrix4 mat4 = source_node->getTransformationMatrix(); + COLLADABU::Math::Matrix4 bmat4 = + mat4.transpose(); // transpose to get blender row-major order + float mat[4][4]; + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + mat[i][j] = bmat4[i][j]; + } + } + /* calc new matrix and apply */ + mul_m4_m4m4(obn->obmat, obn->obmat, mat); + BKE_object_apply_mat4(obn, obn->obmat, 0, 0); + } + } + else { + anim_importer.read_node_transform(source_node, obn); + } + + /*DAG_relations_tag_update(CTX_data_main(mContext));*/ + + COLLADAFW::NodePointerArray &children = source_node->getChildNodes(); + if (children.getCount()) { + for (unsigned int i = 0; i < children.getCount(); i++) { + COLLADAFW::Node *child_node = children[i]; + const COLLADAFW::UniqueId &child_id = child_node->getUniqueId(); + if (object_map.find(child_id) == object_map.end()) { + continue; + } + COLLADAFW::InstanceNodePointerArray &inodes = child_node->getInstanceNodes(); + Object *new_child = NULL; + if (inodes.getCount()) { // \todo loop through instance nodes + const COLLADAFW::UniqueId &id = inodes[0]->getInstanciatedObjectId(); + fprintf(stderr, "Doing %d child nodes\n", (int)node_map.count(id)); + new_child = create_instance_node( + object_map.find(id)->second, node_map[id], child_node, sce, is_library_node); + } + else { + new_child = create_instance_node( + object_map.find(child_id)->second, child_node, NULL, sce, is_library_node); + } + bc_set_parent(new_child, obn, mContext, true); + + if (is_library_node) { + libnode_ob.push_back(new_child); + } + } + } + + return obn; +} + +/* to create constraints off node tags. Assumes only constraint data in + * current with blender profile. */ +void DocumentImporter::create_constraints(ExtraTags *et, Object *ob) +{ + if (et && et->isProfile("blender")) { + std::string name; + short type = 0; + et->setData("type", &type); + BKE_constraint_add_for_object(ob, "Test_con", type); + } +} + +void DocumentImporter::report_unknown_reference(const COLLADAFW::Node &node, + const std::string object_type) +{ + std::string id = node.getOriginalId(); + std::string name = node.getName(); + fprintf(stderr, + "error: node id=\"%s\", name=\"%s\" refers to an undefined %s.\n", + id.c_str(), + name.c_str(), + object_type.c_str()); +} + +std::vector *DocumentImporter::write_node(COLLADAFW::Node *node, + COLLADAFW::Node *parent_node, + Scene *sce, + Object *par, + bool is_library_node) +{ + Main *bmain = CTX_data_main(mContext); + Object *ob = NULL; + bool is_joint = node->getType() == COLLADAFW::Node::JOINT; + bool read_transform = true; + std::string id = node->getOriginalId(); + std::string name = node->getName(); + + /* if node has child nodes write them */ + COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes(); + + std::vector *objects_done = new std::vector(); + std::vector *root_objects = new std::vector(); + + fprintf( + stderr, "| %s id='%s', name='%s'\n", is_joint ? "JOINT" : "NODE ", id.c_str(), name.c_str()); + + if (is_joint) { + if (parent_node == NULL && !is_library_node) { + /* A Joint on root level is a skeleton without root node. + * Here we add the armature "on the fly": */ + par = bc_add_object(bmain, sce, view_layer, OB_ARMATURE, std::string("Armature").c_str()); + objects_done->push_back(par); + root_objects->push_back(par); + object_map.insert(std::pair(node->getUniqueId(), par)); + node_map[node->getUniqueId()] = node; + } + if (parent_node == NULL || parent_node->getType() != COLLADAFW::Node::JOINT) { + armature_importer.add_root_joint(node, par); + } + + if (parent_node == NULL) { + /* for skeletons without root node all has been done above. + * Skeletons with root node are handled further down. */ + goto finally; + } + } + else { + COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries(); + COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras(); + COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights(); + COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers(); + COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes(); + size_t geom_done = 0; + size_t camera_done = 0; + size_t lamp_done = 0; + size_t controller_done = 0; + size_t inst_done = 0; + + /* XXX linking object with the first , though a node may have more of + * them... maybe join multiple meshes into 1, and link object with it? not + * sure... */ + while (geom_done < geom.getCount()) { + ob = mesh_importer.create_mesh_object(node, geom[geom_done], false, uid_material_map); + if (ob == NULL) { + report_unknown_reference(*node, "instance_mesh"); + } + else { + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + geom_done++; + } + while (camera_done < camera.getCount()) { + ob = create_camera_object(camera[camera_done], sce); + if (ob == NULL) { + report_unknown_reference(*node, "instance_camera"); + } + else { + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + camera_done++; + } + while (lamp_done < lamp.getCount()) { + ob = create_light_object(lamp[lamp_done], sce); + if (ob == NULL) { + report_unknown_reference(*node, "instance_light"); + } + else { + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + lamp_done++; + } + while (controller_done < controller.getCount()) { + COLLADAFW::InstanceGeometry *geometry = (COLLADAFW::InstanceGeometry *) + controller[controller_done]; + ob = mesh_importer.create_mesh_object(node, geometry, true, uid_material_map); + if (ob == NULL) { + report_unknown_reference(*node, "instance_controller"); + } + else { + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + controller_done++; + } + /* XXX instance_node is not supported yet */ + while (inst_done < inst_node.getCount()) { + const COLLADAFW::UniqueId &node_id = inst_node[inst_done]->getInstanciatedObjectId(); + if (object_map.find(node_id) == object_map.end()) { + fprintf(stderr, + "Cannot find object for node referenced by .\n", + inst_node[inst_done]->getName().c_str()); + ob = NULL; + } + else { + std::pair::iterator, + std::multimap::iterator> + pair_iter = object_map.equal_range(node_id); + for (std::multimap::iterator it2 = pair_iter.first; + it2 != pair_iter.second; + it2++) { + Object *source_ob = (Object *)it2->second; + COLLADAFW::Node *source_node = node_map[node_id]; + ob = create_instance_node(source_ob, source_node, node, sce, is_library_node); + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + } + inst_done++; + + read_transform = false; + } + + /* if node is empty - create empty object + * XXX empty node may not mean it is empty object, not sure about this */ + if ((geom_done + camera_done + lamp_done + controller_done + inst_done) < 1) { + /* Check if Object is armature, by checking if immediate child is a JOINT node. */ + if (is_armature(node)) { + ob = bc_add_object(bmain, sce, view_layer, OB_ARMATURE, name.c_str()); + } + else { + ob = bc_add_object(bmain, sce, view_layer, OB_EMPTY, NULL); + } + objects_done->push_back(ob); + if (parent_node == NULL) { + root_objects->push_back(ob); + } + } + + /* XXX: if there're multiple instances, only one is stored */ + + if (!ob) { + goto finally; + } + + for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); + ++it) { + ob = *it; + std::string nodename = node->getName().size() ? node->getName() : node->getOriginalId(); + BKE_libblock_rename(bmain, &ob->id, (char *)nodename.c_str()); + object_map.insert(std::pair(node->getUniqueId(), ob)); + node_map[node->getUniqueId()] = node; + + if (is_library_node) { + libnode_ob.push_back(ob); + } + } + + // create_constraints(et,ob); + } + + for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); + ++it) { + ob = *it; + + if (read_transform) { + anim_importer.read_node_transform(node, ob); // overwrites location set earlier + } + + if (!is_joint) { + if (par && ob) { + ob->parent = par; + ob->partype = PAROBJECT; + ob->parsubstr[0] = 0; + + // bc_set_parent(ob, par, mContext, false); + } + } + } + + if (objects_done->size() > 0) { + ob = *objects_done->begin(); + } + else { + ob = NULL; + } + + for (unsigned int i = 0; i < child_nodes.getCount(); i++) { + std::vector *child_objects; + child_objects = write_node(child_nodes[i], node, sce, ob, is_library_node); + delete child_objects; + } + +finally: + delete objects_done; + + return root_objects; +} + +/** + * When this method is called, the writer must write the entire visual scene. + * Return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeVisualScene(const COLLADAFW::VisualScene *visualScene) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + /* This method called on post process after writeGeometry, writeMaterial, etc. */ + + /* For each in : + * create an Object + * if Mesh (previously created in writeGeometry) to which corresponds exists, + * link Object with that mesh. + * + * Update: since we cannot link a Mesh with Object in + * writeGeometry because does not reference , + * we link Objects with Meshes here. + */ + vscenes.push_back(visualScene); + + return true; +} + +/** + * When this method is called, the writer must handle all nodes contained in the + * library nodes. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeLibraryNodes(const COLLADAFW::LibraryNodes *libraryNodes) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + Scene *sce = CTX_data_scene(mContext); + + const COLLADAFW::NodePointerArray &nodes = libraryNodes->getNodes(); + + fprintf(stderr, "+-- Read Library nodes ----------\n"); + for (unsigned int i = 0; i < nodes.getCount(); i++) { + std::vector *child_objects; + child_objects = write_node(nodes[i], NULL, sce, NULL, true); + delete child_objects; + } + return true; +} + +/** + * When this method is called, the writer must write the geometry. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeGeometry(const COLLADAFW::Geometry *geom) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + return mesh_importer.write_geometry(geom); +} + +/** + * When this method is called, the writer must write the material. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeMaterial(const COLLADAFW::Material *cmat) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + Main *bmain = CTX_data_main(mContext); + const std::string &str_mat_id = cmat->getName().size() ? cmat->getName() : cmat->getOriginalId(); + Material *ma = BKE_material_add(bmain, (char *)str_mat_id.c_str()); + + this->uid_effect_map[cmat->getInstantiatedEffect()] = ma; + this->uid_material_map[cmat->getUniqueId()] = ma; + + return true; +} + +void DocumentImporter::write_profile_COMMON(COLLADAFW::EffectCommon *ef, Material *ma) +{ + MaterialNode matNode = MaterialNode(mContext, ef, ma, uid_image_map); + + /* Direct mapping to principled BSDF Shader */ + matNode.set_diffuse(ef->getDiffuse()); + matNode.set_emission(ef->getEmission()); + matNode.set_ior(ef->getIndexOfRefraction()); + matNode.set_alpha(ef->getOpaqueMode(), ef->getTransparent(), ef->getTransparency()); + + /* following mapping still needs to be verified */ +#if 0 + // needs rework to be done for 2.81 + matNode.set_shininess(ef->getShininess()); +#endif + matNode.set_reflectivity(ef->getReflectivity()); + + /* not supported by principled BSDF */ + matNode.set_ambient(ef->getAmbient()); + matNode.set_specular(ef->getSpecular()); + matNode.set_reflective(ef->getReflective()); +} + +/** + * When this method is called, the writer must write the effect. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeEffect(const COLLADAFW::Effect *effect) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + const COLLADAFW::UniqueId &uid = effect->getUniqueId(); + + if (uid_effect_map.find(uid) == uid_effect_map.end()) { + fprintf(stderr, "Couldn't find a material by UID.\n"); + return true; + } + + Material *ma = uid_effect_map[uid]; + std::map::iterator iter; + for (iter = uid_material_map.begin(); iter != uid_material_map.end(); iter++) { + if (iter->second == ma) { + this->FW_object_map[iter->first] = effect; + break; + } + } + COLLADAFW::CommonEffectPointerArray common_efs = effect->getCommonEffects(); + if (common_efs.getCount() < 1) { + fprintf(stderr, "Couldn't find .\n"); + return true; + } + /* XXX TODO: Take all s + * Currently only first is supported */ + COLLADAFW::EffectCommon *ef = common_efs[0]; + write_profile_COMMON(ef, ma); + this->FW_object_map[effect->getUniqueId()] = effect; + + return true; +} + +/** + * When this method is called, the writer must write the camera. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeCamera(const COLLADAFW::Camera *camera) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + Main *bmain = CTX_data_main(mContext); + Camera *cam = NULL; + std::string cam_id, cam_name; + + ExtraTags *et = getExtraTags(camera->getUniqueId()); + cam_id = camera->getOriginalId(); + cam_name = camera->getName(); + if (cam_name.size()) { + cam = (Camera *)BKE_camera_add(bmain, (char *)cam_name.c_str()); + } + else { + cam = (Camera *)BKE_camera_add(bmain, (char *)cam_id.c_str()); + } + + if (!cam) { + fprintf(stderr, "Cannot create camera.\n"); + return true; + } + + if (et && et->isProfile("blender")) { + et->setData("shiftx", &(cam->shiftx)); + et->setData("shifty", &(cam->shifty)); + et->setData("dof_distance", &(cam->dof.focus_distance)); + } + cam->clip_start = camera->getNearClippingPlane().getValue(); + cam->clip_end = camera->getFarClippingPlane().getValue(); + + COLLADAFW::Camera::CameraType type = camera->getCameraType(); + switch (type) { + case COLLADAFW::Camera::ORTHOGRAPHIC: { + cam->type = CAM_ORTHO; + } break; + case COLLADAFW::Camera::PERSPECTIVE: { + cam->type = CAM_PERSP; + } break; + case COLLADAFW::Camera::UNDEFINED_CAMERATYPE: { + fprintf(stderr, "Current camera type is not supported.\n"); + cam->type = CAM_PERSP; + } break; + } + + switch (camera->getDescriptionType()) { + case COLLADAFW::Camera::ASPECTRATIO_AND_Y: { + switch (cam->type) { + case CAM_ORTHO: { + double ymag = 2 * camera->getYMag().getValue(); + double aspect = camera->getAspectRatio().getValue(); + double xmag = aspect * ymag; + cam->ortho_scale = (float)xmag; + } break; + case CAM_PERSP: + default: { + double yfov = camera->getYFov().getValue(); + double aspect = camera->getAspectRatio().getValue(); + + /* NOTE: Needs more testing (As we currently have no official test data for this) */ + + double xfov = 2.0f * atanf(aspect * tanf(DEG2RADF(yfov) * 0.5f)); + cam->lens = fov_to_focallength(xfov, cam->sensor_x); + } break; + } + } break; + /* XXX correct way to do following four is probably to get also render + * size and determine proper settings from that somehow */ + case COLLADAFW::Camera::ASPECTRATIO_AND_X: + case COLLADAFW::Camera::SINGLE_X: + case COLLADAFW::Camera::X_AND_Y: { + switch (cam->type) { + case CAM_ORTHO: + cam->ortho_scale = (float)camera->getXMag().getValue() * 2; + break; + case CAM_PERSP: + default: { + double x = camera->getXFov().getValue(); + /* x is in degrees, cam->lens is in millimiters */ + cam->lens = fov_to_focallength(DEG2RADF(x), cam->sensor_x); + } break; + } + } break; + case COLLADAFW::Camera::SINGLE_Y: { + switch (cam->type) { + case CAM_ORTHO: + cam->ortho_scale = (float)camera->getYMag().getValue(); + break; + case CAM_PERSP: + default: { + double yfov = camera->getYFov().getValue(); + /* yfov is in degrees, cam->lens is in millimiters */ + cam->lens = fov_to_focallength(DEG2RADF(yfov), cam->sensor_x); + } break; + } + } break; + case COLLADAFW::Camera::UNDEFINED: + /* read nothing, use blender defaults. */ + break; + } + + this->uid_camera_map[camera->getUniqueId()] = cam; + this->FW_object_map[camera->getUniqueId()] = camera; + /* XXX import camera options */ + return true; +} + +/** + * When this method is called, the writer must write the image. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeImage(const COLLADAFW::Image *image) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + const std::string &imagepath = image->getImageURI().toNativePath(); + + char dir[FILE_MAX]; + char absolute_path[FILE_MAX]; + const char *workpath; + + BLI_split_dir_part(this->import_settings->filepath, dir, sizeof(dir)); + BLI_join_dirfile(absolute_path, sizeof(absolute_path), dir, imagepath.c_str()); + if (BLI_exists(absolute_path)) { + workpath = absolute_path; + } + else { + /* Maybe imagepath was already absolute ? */ + if (!BLI_exists(imagepath.c_str())) { + fprintf(stderr, "|! Image not found: %s\n", imagepath.c_str()); + return true; + } + workpath = imagepath.c_str(); + } + + Image *ima = BKE_image_load_exists(CTX_data_main(mContext), workpath); + if (!ima) { + fprintf(stderr, "|! Cannot create image: %s\n", workpath); + return true; + } + this->uid_image_map[image->getUniqueId()] = ima; + fprintf(stderr, "| import Image: %s\n", workpath); + return true; +} + +/** + * When this method is called, the writer must write the light. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeLight(const COLLADAFW::Light *light) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + Main *bmain = CTX_data_main(mContext); + Light *lamp = NULL; + std::string la_id, la_name; + + ExtraTags *et = getExtraTags(light->getUniqueId()); +#if 0 + TagsMap::iterator etit; + ExtraTags *et = 0; + etit = uid_tags_map.find(light->getUniqueId().toAscii()); + if (etit != uid_tags_map.end()) { + et = etit->second; + } +#endif + + la_id = light->getOriginalId(); + la_name = light->getName(); + if (la_name.size()) { + lamp = (Light *)BKE_light_add(bmain, (char *)la_name.c_str()); + } + else { + lamp = (Light *)BKE_light_add(bmain, (char *)la_id.c_str()); + } + + if (!lamp) { + fprintf(stderr, "Cannot create light.\n"); + return true; + } + + /* if we find an ExtraTags for this, use that instead. */ + if (et && et->isProfile("blender")) { + et->setData("type", &(lamp->type)); + et->setData("flag", &(lamp->flag)); + et->setData("mode", &(lamp->mode)); + et->setData("gamma", &(lamp->k)); + et->setData("red", &(lamp->r)); + et->setData("green", &(lamp->g)); + et->setData("blue", &(lamp->b)); + et->setData("shadow_r", &(lamp->shdwr)); + et->setData("shadow_g", &(lamp->shdwg)); + et->setData("shadow_b", &(lamp->shdwb)); + et->setData("energy", &(lamp->energy)); + et->setData("dist", &(lamp->dist)); + et->setData("spotsize", &(lamp->spotsize)); + lamp->spotsize = DEG2RADF(lamp->spotsize); + et->setData("spotblend", &(lamp->spotblend)); + et->setData("att1", &(lamp->att1)); + et->setData("att2", &(lamp->att2)); + et->setData("falloff_type", &(lamp->falloff_type)); + et->setData("clipsta", &(lamp->clipsta)); + et->setData("clipend", &(lamp->clipend)); + et->setData("bias", &(lamp->bias)); + et->setData("soft", &(lamp->soft)); + et->setData("bufsize", &(lamp->bufsize)); + et->setData("buffers", &(lamp->buffers)); + et->setData("area_shape", &(lamp->area_shape)); + et->setData("area_size", &(lamp->area_size)); + et->setData("area_sizey", &(lamp->area_sizey)); + et->setData("area_sizez", &(lamp->area_sizez)); + } + else { + float constatt = light->getConstantAttenuation().getValue(); + float linatt = light->getLinearAttenuation().getValue(); + float quadatt = light->getQuadraticAttenuation().getValue(); + float d = 25.0f; + float att1 = 0.0f; + float att2 = 0.0f; + float e = 1.0f; + + if (light->getColor().isValid()) { + COLLADAFW::Color col = light->getColor(); + lamp->r = col.getRed(); + lamp->g = col.getGreen(); + lamp->b = col.getBlue(); + } + + if (IS_EQ(linatt, 0.0f) && quadatt > 0.0f) { + att2 = quadatt; + d = sqrt(1.0f / quadatt); + } + /* linear light */ + else if (IS_EQ(quadatt, 0.0f) && linatt > 0.0f) { + att1 = linatt; + d = (1.0f / linatt); + } + else if (IS_EQ(constatt, 1.0f)) { + att1 = 1.0f; + } + else { + /* assuming point light (const att = 1.0); */ + att1 = 1.0f; + } + + d *= (1.0f / unit_converter.getLinearMeter()); + + lamp->energy = e; + lamp->dist = d; + + switch (light->getLightType()) { + case COLLADAFW::Light::AMBIENT_LIGHT: { + lamp->type = LA_SUN; // TODO needs more thoughts + } break; + case COLLADAFW::Light::SPOT_LIGHT: { + lamp->type = LA_SPOT; + lamp->att1 = att1; + lamp->att2 = att2; + if (IS_EQ(att1, 0.0f) && att2 > 0) { + lamp->falloff_type = LA_FALLOFF_INVSQUARE; + } + if (IS_EQ(att2, 0.0f) && att1 > 0) { + lamp->falloff_type = LA_FALLOFF_INVLINEAR; + } + lamp->spotsize = DEG2RADF(light->getFallOffAngle().getValue()); + lamp->spotblend = light->getFallOffExponent().getValue(); + } break; + case COLLADAFW::Light::DIRECTIONAL_LIGHT: { + /* our sun is very strong, so pick a smaller energy level */ + lamp->type = LA_SUN; + } break; + case COLLADAFW::Light::POINT_LIGHT: { + lamp->type = LA_LOCAL; + lamp->att1 = att1; + lamp->att2 = att2; + if (IS_EQ(att1, 0.0f) && att2 > 0) { + lamp->falloff_type = LA_FALLOFF_INVSQUARE; + } + if (IS_EQ(att2, 0.0f) && att1 > 0) { + lamp->falloff_type = LA_FALLOFF_INVLINEAR; + } + } break; + case COLLADAFW::Light::UNDEFINED: { + fprintf(stderr, "Current light type is not supported.\n"); + lamp->type = LA_LOCAL; + } break; + } + } + + this->uid_light_map[light->getUniqueId()] = lamp; + this->FW_object_map[light->getUniqueId()] = light; + return true; +} + +/* this function is called only for animations that pass COLLADAFW::validate */ +bool DocumentImporter::writeAnimation(const COLLADAFW::Animation *anim) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + return anim_importer.write_animation(anim); +} + +/* called on post-process stage after writeVisualScenes */ +bool DocumentImporter::writeAnimationList(const COLLADAFW::AnimationList *animationList) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + /* return true; */ + return anim_importer.write_animation_list(animationList); +} + +#if WITH_OPENCOLLADA_ANIMATION_CLIP +/* Since opencollada 1.6.68 + * called on post-process stage after writeVisualScenes */ +bool DocumentImporter::writeAnimationClip(const COLLADAFW::AnimationClip *animationClip) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + return true; + /* TODO: implement import of AnimationClips */ + // return animation_clip_importer.write_animation_clip(animationClip); +} +#endif + +/** + * When this method is called, the writer must write the skin controller data. + * \return The writer should return true, if writing succeeded, false otherwise. + */ +bool DocumentImporter::writeSkinControllerData(const COLLADAFW::SkinControllerData *skin) +{ + return armature_importer.write_skin_controller_data(skin); +} + +/* this is called on postprocess, before writeVisualScenes */ +bool DocumentImporter::writeController(const COLLADAFW::Controller *controller) +{ + if (mImportStage == Fetching_Controller_data) { + return true; + } + + return armature_importer.write_controller(controller); +} + +bool DocumentImporter::writeFormulas(const COLLADAFW::Formulas *formulas) +{ + return true; +} + +bool DocumentImporter::writeKinematicsScene(const COLLADAFW::KinematicsScene *kinematicsScene) +{ + return true; +} + +ExtraTags *DocumentImporter::getExtraTags(const COLLADAFW::UniqueId &uid) +{ + if (uid_tags_map.find(uid.toAscii()) == uid_tags_map.end()) { + return NULL; + } + return uid_tags_map[uid.toAscii()]; +} + +bool DocumentImporter::addExtraTags(const COLLADAFW::UniqueId &uid, ExtraTags *extra_tags) +{ + uid_tags_map[uid.toAscii()] = extra_tags; + return true; +} + +bool DocumentImporter::is_armature(COLLADAFW::Node *node) +{ + COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes(); + for (unsigned int i = 0; i < child_nodes.getCount(); i++) { + if (child_nodes[i]->getType() == COLLADAFW::Node::JOINT) { + return true; + } + else { + continue; + } + } + + /* no child is JOINT */ + return false; +} diff --git a/source/blender/io/collada/DocumentImporter.h b/source/blender/io/collada/DocumentImporter.h new file mode 100644 index 00000000000..e47c844f7c6 --- /dev/null +++ b/source/blender/io/collada/DocumentImporter.h @@ -0,0 +1,172 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __DOCUMENTIMPORTER_H__ +#define __DOCUMENTIMPORTER_H__ + +#include "COLLADAFWIWriter.h" +#include "COLLADAFWMaterial.h" +#include "COLLADAFWEffect.h" +#include "COLLADAFWColor.h" +#include "COLLADAFWImage.h" +#include "COLLADAFWInstanceGeometry.h" +#include "COLLADAFWController.h" +#include "COLLADAFWMorphController.h" +#include "COLLADAFWSkinController.h" +#include "COLLADAFWEffectCommon.h" + +#include "BKE_object.h" +#include "BKE_constraint.h" + +#include "TransformReader.h" +#include "AnimationImporter.h" +#include "ArmatureImporter.h" +#include "ControllerExporter.h" +#include "MeshImporter.h" +#include "ImportSettings.h" + +struct bContext; + +/** Importer class. */ +class DocumentImporter : COLLADAFW::IWriter { + public: + //! Enumeration to denote the stage of import + enum ImportStage { + Fetching_Scene_data, /* First pass to collect all data except controller */ + Fetching_Controller_data, /* Second pass to collect controller data */ + }; + /** Constructor */ + DocumentImporter(bContext *C, const ImportSettings *import_settings); + + /** Destructor */ + ~DocumentImporter(); + + /** Function called by blender UI */ + bool import(); + + /** these should not be here */ + Object *create_camera_object(COLLADAFW::InstanceCamera *, Scene *); + Object *create_light_object(COLLADAFW::InstanceLight *, Scene *); + Object *create_instance_node(Object *, COLLADAFW::Node *, COLLADAFW::Node *, Scene *, bool); + void create_constraints(ExtraTags *et, Object *ob); + std::vector *write_node(COLLADAFW::Node *, COLLADAFW::Node *, Scene *, Object *, bool); + void write_profile_COMMON(COLLADAFW::EffectCommon *, Material *); + + void translate_anim_recursive(COLLADAFW::Node *, COLLADAFW::Node *, Object *); + + /** + * This method will be called if an error in the loading process occurred and the loader cannot + * continue to load. The writer should undo all operations that have been performed. + * \param errorMessage: A message containing information about the error that occurred. + */ + void cancel(const COLLADAFW::String &errorMessage); + + /** This is the method called. The writer hast to prepare to receive data.*/ + void start(); + + /** This method is called after the last write* method. No other methods will be called after + * this.*/ + void finish(); + + bool writeGlobalAsset(const COLLADAFW::FileInfo *); + std::string get_import_version(const COLLADAFW::FileInfo *asset); + + bool writeScene(const COLLADAFW::Scene *); + + bool writeVisualScene(const COLLADAFW::VisualScene *); + + bool writeLibraryNodes(const COLLADAFW::LibraryNodes *); + + bool writeAnimation(const COLLADAFW::Animation *); + + bool writeAnimationList(const COLLADAFW::AnimationList *); + +#if WITH_OPENCOLLADA_ANIMATION_CLIP + // Please enable this when building with Collada 1.6.65 or newer (also in DocumentImporter.cpp) + bool writeAnimationClip(const COLLADAFW::AnimationClip *animationClip); +#endif + + bool writeGeometry(const COLLADAFW::Geometry *); + + bool writeMaterial(const COLLADAFW::Material *); + + bool writeEffect(const COLLADAFW::Effect *); + + bool writeCamera(const COLLADAFW::Camera *); + + bool writeImage(const COLLADAFW::Image *); + + bool writeLight(const COLLADAFW::Light *); + + bool writeSkinControllerData(const COLLADAFW::SkinControllerData *); + + bool writeController(const COLLADAFW::Controller *); + + bool writeFormulas(const COLLADAFW::Formulas *); + + bool writeKinematicsScene(const COLLADAFW::KinematicsScene *); + + /** Add element and data for UniqueId */ + bool addExtraTags(const COLLADAFW::UniqueId &uid, ExtraTags *extra_tags); + /** Get an extisting ExtraTags for uid */ + ExtraTags *getExtraTags(const COLLADAFW::UniqueId &uid); + + bool is_armature(COLLADAFW::Node *node); + + private: + const ImportSettings *import_settings; + + /** Current import stage we're in. */ + ImportStage mImportStage; + + bContext *mContext; + ViewLayer *view_layer; + + UnitConverter unit_converter; + ArmatureImporter armature_importer; + MeshImporter mesh_importer; + AnimationImporter anim_importer; + + /** TagsMap typedef for uid_tags_map. */ + typedef std::map TagsMap; + /** Tags map of unique id as a string and ExtraTags instance. */ + TagsMap uid_tags_map; + + UidImageMap uid_image_map; + std::map uid_material_map; + std::map uid_effect_map; + std::map uid_camera_map; + std::map uid_light_map; + std::map material_texture_mapping_map; + std::multimap object_map; + std::map node_map; + std::vector vscenes; + std::vector libnode_ob; + + std::map + root_map; // find root joint by child joint uid, for bone tree evaluation during resampling + std::map FW_object_map; + + std::string import_from_version; + + void report_unknown_reference(const COLLADAFW::Node &node, const std::string object_type); +}; + +#endif diff --git a/source/blender/io/collada/EffectExporter.cpp b/source/blender/io/collada/EffectExporter.cpp new file mode 100644 index 00000000000..a1174fdff56 --- /dev/null +++ b/source/blender/io/collada/EffectExporter.cpp @@ -0,0 +1,312 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include + +#include "COLLADASWEffectProfile.h" +#include "COLLADAFWColorOrTexture.h" + +#include "EffectExporter.h" +#include "DocumentExporter.h" +#include "MaterialExporter.h" + +#include "collada_internal.h" +#include "collada_utils.h" + +extern "C" { +#include "DNA_mesh_types.h" +#include "DNA_world_types.h" + +#include "BKE_collection.h" +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_material.h" +} + +static std::string getActiveUVLayerName(Object *ob) +{ + Mesh *me = (Mesh *)ob->data; + + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + if (num_layers) { + return std::string(bc_CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV)); + } + + return ""; +} + +EffectsExporter::EffectsExporter(COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings, + KeyImageMap &key_image_map) + : COLLADASW::LibraryEffects(sw), export_settings(export_settings), key_image_map(key_image_map) +{ +} + +bool EffectsExporter::hasEffects(Scene *sce) +{ + FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { + int a; + for (a = 0; a < ob->totcol; a++) { + Material *ma = BKE_object_material_get(ob, a + 1); + + // no material, but check all of the slots + if (!ma) { + continue; + } + + return true; + } + } + FOREACH_SCENE_OBJECT_END; + return false; +} + +void EffectsExporter::exportEffects(bContext *C, Scene *sce) +{ + if (hasEffects(sce)) { + this->mContext = C; + this->scene = sce; + openLibrary(); + MaterialFunctor mf; + mf.forEachMaterialInExportSet( + sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); + } +} + +void EffectsExporter::set_shader_type(COLLADASW::EffectProfile &ep, Material *ma) +{ + /* XXX check if BLINN and PHONG can be supported as well */ + ep.setShaderType(COLLADASW::EffectProfile::LAMBERT); +} + +void EffectsExporter::set_transparency(COLLADASW::EffectProfile &ep, Material *ma) +{ + double alpha = bc_get_alpha(ma); + if (alpha < 1) { + // workaround use to avoid wrong handling of by other tools + COLLADASW::ColorOrTexture cot = bc_get_cot(0, 0, 0, alpha); + ep.setTransparent(cot, false, "alpha"); + ep.setOpaque(COLLADASW::EffectProfile::A_ONE); + } +} + +void EffectsExporter::set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma) +{ + COLLADASW::ColorOrTexture cot = bc_get_base_color(ma); + ep.setDiffuse(cot, false, "diffuse"); +} + +void EffectsExporter::set_ambient(COLLADASW::EffectProfile &ep, Material *ma) +{ + COLLADASW::ColorOrTexture cot = bc_get_ambient(ma); + ep.setAmbient(cot, false, "ambient"); +} +void EffectsExporter::set_specular(COLLADASW::EffectProfile &ep, Material *ma) +{ + COLLADASW::ColorOrTexture cot = bc_get_specular(ma); + ep.setSpecular(cot, false, "specular"); +} +void EffectsExporter::set_reflective(COLLADASW::EffectProfile &ep, Material *ma) +{ + COLLADASW::ColorOrTexture cot = bc_get_reflective(ma); + ep.setReflective(cot, false, "reflective"); +} + +void EffectsExporter::set_reflectivity(COLLADASW::EffectProfile &ep, Material *ma) +{ + double reflectivity = bc_get_reflectivity(ma); + if (reflectivity > 0.0) { + ep.setReflectivity(reflectivity, false, "specular"); + } +} + +void EffectsExporter::set_emission(COLLADASW::EffectProfile &ep, Material *ma) +{ + COLLADASW::ColorOrTexture cot = bc_get_emission(ma); + ep.setEmission(cot, false, "emission"); +} + +void EffectsExporter::set_ior(COLLADASW::EffectProfile &ep, Material *ma) +{ + double alpha = bc_get_ior(ma); + ep.setIndexOfRefraction(alpha, false, "ior"); +} + +void EffectsExporter::set_shininess(COLLADASW::EffectProfile &ep, Material *ma) +{ + double shininess = bc_get_shininess(ma); + ep.setShininess(shininess, false, "shininess"); +} + +void EffectsExporter::get_images(Material *ma, KeyImageMap &material_image_map) +{ + if (!ma->use_nodes) { + return; + } + + MaterialNode material = MaterialNode(mContext, ma, key_image_map); + Image *image = material.get_diffuse_image(); + if (image == nullptr) { + return; + } + + std::string uid(id_name(image)); + std::string key = translate_id(uid); + + if (material_image_map.find(key) == material_image_map.end()) { + material_image_map[key] = image; + key_image_map[key] = image; + } +} + +void EffectsExporter::create_image_samplers(COLLADASW::EffectProfile &ep, + KeyImageMap &material_image_map, + std::string &active_uv) +{ + KeyImageMap::iterator iter; + + for (iter = material_image_map.begin(); iter != material_image_map.end(); iter++) { + + Image *image = iter->second; + std::string uid(id_name(image)); + std::string key = translate_id(uid); + + COLLADASW::Sampler *sampler = new COLLADASW::Sampler( + COLLADASW::Sampler::SAMPLER_TYPE_2D, + key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX, + key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); + + sampler->setImageId(key); + + ep.setDiffuse(createTexture(image, active_uv, sampler), false, "diffuse"); + } +} + +void EffectsExporter::operator()(Material *ma, Object *ob) +{ + KeyImageMap material_image_map; + + openEffect(get_effect_id(ma)); + + COLLADASW::EffectProfile ep(mSW); + ep.setProfileType(COLLADASW::EffectProfile::COMMON); + ep.openProfile(); + set_shader_type(ep, ma); // creates a Lambert Shader for now + + COLLADASW::ColorOrTexture cot; + + set_diffuse_color(ep, ma); + set_emission(ep, ma); + set_ior(ep, ma); + set_reflectivity(ep, ma); + set_transparency(ep, ma); + + /* TODO: */ + // set_shininess(ep, ma); shininess not supported for lambert + // set_ambient(ep, ma); + // set_specular(ep, ma); + + get_images(ma, material_image_map); + std::string active_uv(getActiveUVLayerName(ob)); + create_image_samplers(ep, material_image_map, active_uv); + +#if 0 + unsigned int a, b; + for (a = 0, b = 0; a < tex_indices.size(); a++) { + MTex *t = ma->mtex[tex_indices[a]]; + Image *ima = t->tex->ima; + + // Image not set for texture + if (!ima) { + continue; + } + + std::string key(id_name(ima)); + key = translate_id(key); + + // create only one / pair for each unique image + if (im_samp_map.find(key) == im_samp_map.end()) { + // + COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D, + key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX, + key + COLLADASW::Sampler::SURFACE_SID_SUFFIX); + sampler.setImageId(key); + // copy values to arrays since they will live longer + samplers[a] = sampler; + + // store pointers so they can be used later when we create s + samp_surf[b] = &samplers[a]; + //samp_surf[b][1] = &surfaces[a]; + + im_samp_map[key] = b; + b++; + } + } + + for (a = 0; a < tex_indices.size(); a++) { + MTex *t = ma->mtex[tex_indices[a]]; + Image *ima = t->tex->ima; + + if (!ima) { + continue; + } + + std::string key(id_name(ima)); + key = translate_id(key); + int i = im_samp_map[key]; + std::string uvname = strlen(t->uvname) ? t->uvname : active_uv; + COLLADASW::Sampler *sampler = (COLLADASW::Sampler *) + samp_surf[i]; // possibly uninitialized memory ... + writeTextures(ep, key, sampler, t, ima, uvname); + } +#endif + + // performs the actual writing + ep.addProfileElements(); + ep.addExtraTechniques(mSW); + + ep.closeProfile(); + closeEffect(); +} + +COLLADASW::ColorOrTexture EffectsExporter::createTexture(Image *ima, + std::string &uv_layer_name, + COLLADASW::Sampler *sampler + /*COLLADASW::Surface *surface*/) +{ + + COLLADASW::Texture texture(translate_id(id_name(ima))); + texture.setTexcoord(uv_layer_name); + // texture.setSurface(*surface); + texture.setSampler(*sampler); + + COLLADASW::ColorOrTexture cot(texture); + return cot; +} + +COLLADASW::ColorOrTexture EffectsExporter::getcol(float r, float g, float b, float a) +{ + COLLADASW::Color color(r, g, b, a); + COLLADASW::ColorOrTexture cot(color); + return cot; +} diff --git a/source/blender/io/collada/EffectExporter.h b/source/blender/io/collada/EffectExporter.h new file mode 100644 index 00000000000..57df844233c --- /dev/null +++ b/source/blender/io/collada/EffectExporter.h @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __EFFECTEXPORTER_H__ +#define __EFFECTEXPORTER_H__ + +#include +#include + +#include "COLLADASWColorOrTexture.h" +#include "COLLADASWStreamWriter.h" +#include "COLLADASWSampler.h" +#include "COLLADASWLibraryEffects.h" + +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "ExportSettings.h" +#include "collada_utils.h" + +class EffectsExporter : COLLADASW::LibraryEffects { + public: + EffectsExporter(COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings, + KeyImageMap &key_image_map); + void exportEffects(bContext *C, Scene *sce); + + void operator()(Material *ma, Object *ob); + + COLLADASW::ColorOrTexture createTexture(Image *ima, + std::string &uv_layer_name, + COLLADASW::Sampler *sampler + /*COLLADASW::Surface *surface*/); + + COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a); + + private: + void set_shader_type(COLLADASW::EffectProfile &ep, Material *ma); + + void set_diffuse_color(COLLADASW::EffectProfile &ep, Material *ma); + void set_emission(COLLADASW::EffectProfile &ep, Material *ma); + void set_ior(COLLADASW::EffectProfile &ep, Material *ma); + void set_shininess(COLLADASW::EffectProfile &ep, Material *ma); + void set_reflectivity(COLLADASW::EffectProfile &ep, Material *ma); + void set_transparency(COLLADASW::EffectProfile &ep, Material *ma); + void set_ambient(COLLADASW::EffectProfile &ep, Material *ma); + void set_specular(COLLADASW::EffectProfile &ep, Material *ma); + void set_reflective(COLLADASW::EffectProfile &ep, Material *ma); + + void get_images(Material *ma, KeyImageMap &uid_image_map); + void create_image_samplers(COLLADASW::EffectProfile &ep, + KeyImageMap &uid_image_map, + std::string &active_uv); + + void writeTextures(COLLADASW::EffectProfile &ep, + std::string &key, + COLLADASW::Sampler *sampler, + MTex *t, + Image *ima, + std::string &uvname); + + bool hasEffects(Scene *sce); + + BCExportSettings &export_settings; + KeyImageMap &key_image_map; + Scene *scene; + bContext *mContext; +}; + +#endif diff --git a/source/blender/io/collada/ErrorHandler.cpp b/source/blender/io/collada/ErrorHandler.cpp new file mode 100644 index 00000000000..286bcbfb759 --- /dev/null +++ b/source/blender/io/collada/ErrorHandler.cpp @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ +#include "ErrorHandler.h" +#include + +#include "COLLADASaxFWLIError.h" +#include "COLLADASaxFWLSaxParserError.h" +#include "COLLADASaxFWLSaxFWLError.h" + +#include "GeneratedSaxParserParserError.h" + +#include + +#include "BLI_utildefines.h" + +//-------------------------------------------------------------------- +ErrorHandler::ErrorHandler() : mError(false) +{ +} + +//-------------------------------------------------------------------- +ErrorHandler::~ErrorHandler() +{ +} + +//-------------------------------------------------------------------- +bool ErrorHandler::handleError(const COLLADASaxFWL::IError *error) +{ + /* This method must return false when Collada should continue. + * See https://github.com/KhronosGroup/OpenCOLLADA/issues/442 + */ + bool isError = true; + std::string error_context; + std::string error_message; + + if (error->getErrorClass() == COLLADASaxFWL::IError::ERROR_SAXPARSER) { + error_context = "Schema validation"; + + COLLADASaxFWL::SaxParserError *saxParserError = (COLLADASaxFWL::SaxParserError *)error; + const GeneratedSaxParser::ParserError &parserError = saxParserError->getError(); + error_message = parserError.getErrorMessage(); + + if (parserError.getErrorType() == + GeneratedSaxParser::ParserError::ERROR_VALIDATION_MIN_OCCURS_UNMATCHED) { + if (STREQ(parserError.getElement(), "effect")) { + isError = false; + } + } + + else if (parserError.getErrorType() == + GeneratedSaxParser::ParserError:: + ERROR_VALIDATION_SEQUENCE_PREVIOUS_SIBLING_NOT_PRESENT) { + if (!(STREQ(parserError.getElement(), "extra") && + STREQ(parserError.getAdditionalText().c_str(), "sibling: fx_profile_abstract"))) { + isError = false; + } + } + + else if (parserError.getErrorType() == + GeneratedSaxParser::ParserError::ERROR_COULD_NOT_OPEN_FILE) { + isError = true; + error_context = "File access"; + } + + else if (parserError.getErrorType() == + GeneratedSaxParser::ParserError::ERROR_REQUIRED_ATTRIBUTE_MISSING) { + isError = true; + } + + else { + isError = (parserError.getSeverity() != + GeneratedSaxParser::ParserError::Severity::SEVERITY_ERROR_NONCRITICAL); + } + } + else if (error->getErrorClass() == COLLADASaxFWL::IError::ERROR_SAXFWL) { + error_context = "Sax FWL"; + COLLADASaxFWL::SaxFWLError *saxFWLError = (COLLADASaxFWL::SaxFWLError *)error; + error_message = saxFWLError->getErrorMessage(); + + /* + * Accept non critical errors as warnings (i.e. texture not found) + * This makes the importer more graceful, so it now imports what makes sense. + */ + + isError = (saxFWLError->getSeverity() != COLLADASaxFWL::IError::SEVERITY_ERROR_NONCRITICAL); + } + else { + error_context = "OpenCollada"; + error_message = error->getFullErrorMessage(); + isError = true; + } + + std::string severity = (isError) ? "Error" : "Warning"; + std::cout << error_context << " (" << severity << "): " << error_message << std::endl; + if (isError) { + std::cout << "The Collada import has been forced to stop." << std::endl; + std::cout << "Please fix the reported error and then try again."; + mError = true; + } + return isError; +} diff --git a/source/blender/io/collada/ErrorHandler.h b/source/blender/io/collada/ErrorHandler.h new file mode 100644 index 00000000000..f040855244d --- /dev/null +++ b/source/blender/io/collada/ErrorHandler.h @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __ERRORHANDLER_H__ +#define __ERRORHANDLER_H__ + +#include +#include +#include +#include // sort() + +#include "COLLADASaxFWLIErrorHandler.h" + +/** \brief Handler class for parser errors + */ +class ErrorHandler : public COLLADASaxFWL::IErrorHandler { + public: + /** Constructor. */ + ErrorHandler(); + + /** Destructor. */ + virtual ~ErrorHandler(); + /** handle any error thrown by the parser. */ + bool virtual handleError(const COLLADASaxFWL::IError *error); + /** True if there was an error during parsing. */ + bool hasError() + { + return mError; + } + + private: + /** Disable default copy ctor. */ + ErrorHandler(const ErrorHandler &pre); + /** Disable default assignment operator. */ + const ErrorHandler &operator=(const ErrorHandler &pre); + /** Hold error status. */ + bool mError; +}; + +#endif /* __ERRORHANDLER_H__ */ diff --git a/source/blender/io/collada/ExportSettings.cpp b/source/blender/io/collada/ExportSettings.cpp new file mode 100644 index 00000000000..da3c0de0fdf --- /dev/null +++ b/source/blender/io/collada/ExportSettings.cpp @@ -0,0 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "ExportSettings.h" diff --git a/source/blender/io/collada/ExportSettings.h b/source/blender/io/collada/ExportSettings.h new file mode 100644 index 00000000000..1e158418120 --- /dev/null +++ b/source/blender/io/collada/ExportSettings.h @@ -0,0 +1,295 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __EXPORTSETTINGS_H__ +#define __EXPORTSETTINGS_H__ + +#ifdef __cplusplus +# include +# include "BCMath.h" + +extern "C" { +#endif + +#include "BLI_linklist.h" +#include "BlenderContext.h" + +typedef enum BC_export_mesh_type { + BC_MESH_TYPE_VIEW, + BC_MESH_TYPE_RENDER, +} BC_export_mesh_type; + +typedef enum BC_export_transformation_type { + BC_TRANSFORMATION_TYPE_MATRIX, + BC_TRANSFORMATION_TYPE_DECOMPOSED, +} BC_export_transformation_type; + +typedef enum BC_export_animation_type { + BC_ANIMATION_EXPORT_SAMPLES, + BC_ANIMATION_EXPORT_KEYS, +} BC_export_animation_type; + +typedef enum BC_ui_export_section { + BC_UI_SECTION_MAIN, + BC_UI_SECTION_GEOMETRY, + BC_UI_SECTION_ARMATURE, + BC_UI_SECTION_ANIMATION, + BC_UI_SECTION_COLLADA, +} BC_ui_export_section; + +typedef struct ExportSettings { + bool apply_modifiers; + BC_global_forward_axis global_forward; + BC_global_up_axis global_up; + bool apply_global_orientation; + + BC_export_mesh_type export_mesh_type; + + bool selected; + bool include_children; + bool include_armatures; + bool include_shapekeys; + bool deform_bones_only; + bool include_animations; + bool include_all_actions; + int sampling_rate; + bool keep_smooth_curves; + bool keep_keyframes; + bool keep_flat_curves; + + bool active_uv_only; + BC_export_animation_type export_animation_type; + bool use_texture_copies; + + bool triangulate; + bool use_object_instantiation; + bool use_blender_profile; + bool sort_by_name; + BC_export_transformation_type object_transformation_type; + BC_export_transformation_type animation_transformation_type; + + bool open_sim; + bool limit_precision; + bool keep_bind_info; + + char *filepath; + LinkNode *export_set; +} ExportSettings; + +#ifdef __cplusplus +} + +void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer); + +class BCExportSettings { + + private: + const ExportSettings &export_settings; + BlenderContext &blender_context; + const BCMatrix global_transform; + + public: + BCExportSettings(ExportSettings *exportSettings, BlenderContext &blenderContext) + : export_settings(*exportSettings), + blender_context(blenderContext), + global_transform(BCMatrix(exportSettings->global_forward, exportSettings->global_up)) + + { + } + + const BCMatrix &get_global_transform() + { + return global_transform; + } + + bool get_apply_modifiers() + { + return export_settings.apply_modifiers; + } + + BC_global_forward_axis get_global_forward() + { + return export_settings.global_forward; + } + + BC_global_up_axis get_global_up() + { + return export_settings.global_up; + } + + bool get_apply_global_orientation() + { + return export_settings.apply_global_orientation; + } + + BC_export_mesh_type get_export_mesh_type() + { + return export_settings.export_mesh_type; + } + + bool get_selected() + { + return export_settings.selected; + } + + bool get_include_children() + { + return export_settings.include_children; + } + + bool get_include_armatures() + { + return export_settings.include_armatures; + } + + bool get_include_shapekeys() + { + return export_settings.include_shapekeys; + } + + bool get_deform_bones_only() + { + return export_settings.deform_bones_only; + } + + bool get_include_animations() + { + return export_settings.include_animations; + } + + bool get_include_all_actions() + { + return export_settings.include_all_actions; + } + + int get_sampling_rate() + { + return export_settings.sampling_rate; + } + + bool get_keep_smooth_curves() + { + return export_settings.keep_smooth_curves; + } + + bool get_keep_keyframes() + { + return export_settings.keep_keyframes; + } + + bool get_keep_flat_curves() + { + return export_settings.keep_flat_curves; + } + + bool get_active_uv_only() + { + return export_settings.active_uv_only; + } + + BC_export_animation_type get_export_animation_type() + { + return export_settings.export_animation_type; + } + + bool get_use_texture_copies() + { + return export_settings.use_texture_copies; + } + + bool get_triangulate() + { + return export_settings.triangulate; + } + + bool get_use_object_instantiation() + { + return export_settings.use_object_instantiation; + } + + bool get_use_blender_profile() + { + return export_settings.use_blender_profile; + } + + bool get_sort_by_name() + { + return export_settings.sort_by_name; + } + + BC_export_transformation_type get_object_transformation_type() + { + return export_settings.object_transformation_type; + } + + BC_export_transformation_type get_animation_transformation_type() + { + return export_settings.animation_transformation_type; + } + + bool get_open_sim() + { + return export_settings.open_sim; + } + + bool get_limit_precision() + { + return export_settings.limit_precision; + } + + bool get_keep_bind_info() + { + return export_settings.keep_bind_info; + } + + char *get_filepath() + { + return export_settings.filepath; + } + + LinkNode *get_export_set() + { + return export_settings.export_set; + } + + BlenderContext &get_blender_context() + { + return blender_context; + } + + Scene *get_scene() + { + return blender_context.get_scene(); + } + + ViewLayer *get_view_layer() + { + return blender_context.get_view_layer(); + } + + bool is_export_root(Object *ob) + { + return bc_is_base_node(get_export_set(), ob, get_view_layer()); + } +}; + +#endif + +#endif diff --git a/source/blender/io/collada/ExtraHandler.cpp b/source/blender/io/collada/ExtraHandler.cpp new file mode 100644 index 00000000000..4875ee72b0f --- /dev/null +++ b/source/blender/io/collada/ExtraHandler.cpp @@ -0,0 +1,93 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include "BLI_string.h" + +#include "ExtraHandler.h" + +ExtraHandler::ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp) : currentExtraTags(0) +{ + this->dimp = dimp; + this->aimp = aimp; +} + +ExtraHandler::~ExtraHandler() +{ +} + +bool ExtraHandler::elementBegin(const char *elementName, const char **attributes) +{ + /* \todo attribute handling for profile tags */ + currentElement = std::string(elementName); + // addToSidTree(attributes[0], attributes[1]); + return true; +} + +bool ExtraHandler::elementEnd(const char *elementName) +{ + return true; +} + +bool ExtraHandler::textData(const char *text, size_t textLength) +{ + char buf[1024]; + + if (currentElement.length() == 0 || currentExtraTags == 0) { + return false; + } + + BLI_strncpy(buf, text, textLength + 1); + currentExtraTags->addTag(currentElement, std::string(buf)); + return true; +} + +bool ExtraHandler::parseElement(const char *profileName, + const unsigned long &elementHash, + const COLLADAFW::UniqueId &uniqueId) +{ + /* implement for backwards compatibility, new version added object parameter */ + return parseElement(profileName, elementHash, uniqueId, NULL); +} + +bool ExtraHandler::parseElement(const char *profileName, + const unsigned long &elementHash, + const COLLADAFW::UniqueId &uniqueId, + COLLADAFW::Object *object) +{ + if (BLI_strcaseeq(profileName, "blender")) { +#if 0 + printf("In parseElement for supported profile %s for id %s\n", + profileName, + uniqueId.toAscii().c_str()); +#endif + currentUid = uniqueId; + ExtraTags *et = dimp->getExtraTags(uniqueId); + if (!et) { + et = new ExtraTags(std::string(profileName)); + dimp->addExtraTags(uniqueId, et); + } + currentExtraTags = et; + return true; + } + // printf("In parseElement for unsupported profile %s for id %s\n", profileName, + // uniqueId.toAscii().c_str()); + return false; +} diff --git a/source/blender/io/collada/ExtraHandler.h b/source/blender/io/collada/ExtraHandler.h new file mode 100644 index 00000000000..021eb8e9663 --- /dev/null +++ b/source/blender/io/collada/ExtraHandler.h @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __EXTRAHANDLER_H__ +#define __EXTRAHANDLER_H__ + +#include +#include +#include +#include // sort() + +#include "COLLADASaxFWLIExtraDataCallbackHandler.h" +#include "COLLADASaxFWLFilePartLoader.h" +#include "COLLADASWInstanceController.h" + +#include "DocumentImporter.h" +#include "AnimationImporter.h" + +/** \brief Handler class for \ data, through which different + * profiles can be handled + */ +class ExtraHandler : public COLLADASaxFWL::IExtraDataCallbackHandler { + public: + /** Constructor. */ + ExtraHandler(DocumentImporter *dimp, AnimationImporter *aimp); + + /** Destructor. */ + virtual ~ExtraHandler(); + + /** Handle the beginning of an element. */ + bool elementBegin(const char *elementName, const char **attributes); + + /** Handle the end of an element. */ + bool elementEnd(const char *elementName); + + /** Receive the data in text format. */ + bool textData(const char *text, size_t textLength); + + /** Method to ask, if the current callback handler want to read the data of the given extra + * element. */ + bool parseElement(const char *profileName, + const unsigned long &elementHash, + const COLLADAFW::UniqueId &uniqueId, + COLLADAFW::Object *object); + + /** For backwards compatibility with older OpenCollada, new version added object parameter */ + bool parseElement(const char *profileName, + const unsigned long &elementHash, + const COLLADAFW::UniqueId &uniqueId); + + private: + /** Disable default copy constructor. */ + ExtraHandler(const ExtraHandler &pre); + /** Disable default assignment operator. */ + const ExtraHandler &operator=(const ExtraHandler &pre); + + /** Handle to DocumentImporter for interface to extra element data saving. */ + DocumentImporter *dimp; + AnimationImporter *aimp; + /** Holds Id of element for which XML elements are handled. */ + COLLADAFW::UniqueId currentUid; + ExtraTags *currentExtraTags; + std::string currentElement; +}; + +#endif /* __EXTRAHANDLER_H__ */ diff --git a/source/blender/io/collada/ExtraTags.cpp b/source/blender/io/collada/ExtraTags.cpp new file mode 100644 index 00000000000..496ba3891f7 --- /dev/null +++ b/source/blender/io/collada/ExtraTags.cpp @@ -0,0 +1,126 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include +#include "BLI_string.h" + +#include + +#include "ExtraTags.h" + +ExtraTags::ExtraTags(std::string profile) +{ + this->profile = profile; + this->tags = std::map(); +} + +ExtraTags::~ExtraTags() +{ +} + +bool ExtraTags::isProfile(std::string profile) +{ + return this->profile == profile; +} + +bool ExtraTags::addTag(std::string tag, std::string data) +{ + tags[tag] = data; + + return true; +} + +int ExtraTags::asInt(std::string tag, bool *ok) +{ + if (tags.find(tag) == tags.end()) { + *ok = false; + return -1; + } + *ok = true; + return atoi(tags[tag].c_str()); +} + +float ExtraTags::asFloat(std::string tag, bool *ok) +{ + if (tags.find(tag) == tags.end()) { + *ok = false; + return -1.0f; + } + *ok = true; + return (float)atof(tags[tag].c_str()); +} + +std::string ExtraTags::asString(std::string tag, bool *ok) +{ + if (tags.find(tag) == tags.end()) { + *ok = false; + return ""; + } + *ok = true; + return tags[tag]; +} + +bool ExtraTags::setData(std::string tag, short *data) +{ + bool ok = false; + int tmp = asInt(tag, &ok); + if (ok) { + *data = (short)tmp; + } + return ok; +} + +bool ExtraTags::setData(std::string tag, int *data) +{ + bool ok = false; + int tmp = asInt(tag, &ok); + if (ok) { + *data = tmp; + } + return ok; +} + +bool ExtraTags::setData(std::string tag, float *data) +{ + bool ok = false; + float tmp = asFloat(tag, &ok); + if (ok) { + *data = tmp; + } + return ok; +} + +bool ExtraTags::setData(std::string tag, char *data) +{ + bool ok = false; + int tmp = asInt(tag, &ok); + if (ok) { + *data = (char)tmp; + } + return ok; +} + +std::string ExtraTags::setData(std::string tag, std::string &data) +{ + bool ok = false; + std::string tmp = asString(tag, &ok); + return (ok) ? tmp : data; +} diff --git a/source/blender/io/collada/ExtraTags.h b/source/blender/io/collada/ExtraTags.h new file mode 100644 index 00000000000..9191182c757 --- /dev/null +++ b/source/blender/io/collada/ExtraTags.h @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __EXTRATAGS_H__ +#define __EXTRATAGS_H__ + +#include +#include +#include + +/** \brief Class for saving \ tags for a specific UniqueId. + */ +class ExtraTags { + public: + /** Constructor. */ + ExtraTags(const std::string profile); + + /** Destructor. */ + virtual ~ExtraTags(); + + /** Handle the beginning of an element. */ + bool addTag(std::string tag, std::string data); + + /** Set given short pointer to value of tag, if it exists. */ + bool setData(std::string tag, short *data); + + /** Set given int pointer to value of tag, if it exists. */ + bool setData(std::string tag, int *data); + + /** Set given float pointer to value of tag, if it exists. */ + bool setData(std::string tag, float *data); + + /** Set given char pointer to value of tag, if it exists. */ + bool setData(std::string tag, char *data); + std::string setData(std::string tag, std::string &data); + + /** Return true if the extra tags is for specified profile. */ + bool isProfile(std::string profile); + + private: + /** Disable default copy constructor. */ + ExtraTags(const ExtraTags &pre); + /** Disable default assignment operator. */ + const ExtraTags &operator=(const ExtraTags &pre); + + /** The profile for which the tags are. */ + std::string profile; + + /** Map of tag and text pairs. */ + std::map tags; + + /** Get text data for tag as an int. */ + int asInt(std::string tag, bool *ok); + /** Get text data for tag as a float. */ + float asFloat(std::string tag, bool *ok); + /** Get text data for tag as a string. */ + std::string asString(std::string tag, bool *ok); +}; + +#endif /* __EXTRATAGS_H__ */ diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp new file mode 100644 index 00000000000..640bf3c0633 --- /dev/null +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -0,0 +1,718 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +#include "COLLADASWPrimitves.h" +#include "COLLADASWSource.h" +#include "COLLADASWVertices.h" +#include "COLLADABUUtils.h" + +#include "GeometryExporter.h" + +#include "DNA_meshdata_types.h" + +extern "C" { +#include "BLI_utildefines.h" + +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +} + +#include "collada_internal.h" +#include "collada_utils.h" + +void GeometryExporter::exportGeom() +{ + Scene *sce = blender_context.get_scene(); + openLibrary(); + + GeometryFunctor gf; + gf.forEachMeshObjectInExportSet( + sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); +} + +void GeometryExporter::operator()(Object *ob) +{ + bool use_instantiation = this->export_settings.get_use_object_instantiation(); + Mesh *me = bc_get_mesh_copy(blender_context, + ob, + this->export_settings.get_export_mesh_type(), + this->export_settings.get_apply_modifiers(), + this->export_settings.get_triangulate()); + + std::string geom_id = get_geometry_id(ob, use_instantiation); + std::vector nor; + std::vector norind; + + /* Skip if linked geometry was already exported from another reference */ + if (use_instantiation && exportedGeometry.find(geom_id) != exportedGeometry.end()) { + return; + } + + std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob); + geom_name = encode_xml(geom_name); + + exportedGeometry.insert(geom_id); + + bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); + + create_normals(nor, norind, me); + + /* openMesh(geoId, geoName, meshId) */ + openMesh(geom_id, geom_name); + + /* writes for vertex coords */ + createVertsSource(geom_id, me); + + /* writes for normal coords */ + createNormalsSource(geom_id, me, nor); + + bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV); + + /* writes for uv coords if mesh has uv coords */ + if (has_uvs) { + createTexcoordsSource(geom_id, me); + } + + if (has_color) { + createVertexColorSource(geom_id, me); + } + /* */ + + COLLADASW::Vertices verts(mSW); + verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX)); + COLLADASW::InputList &input_list = verts.getInputList(); + COLLADASW::Input input(COLLADASW::InputSemantic::POSITION, + getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); + input_list.push_back(input); + verts.add(); + + createLooseEdgeList(ob, me, geom_id); + + /* Only create Polylists if number of faces > 0 */ + if (me->totface > 0) { + /* XXX slow */ + if (ob->totcol) { + for (int a = 0; a < ob->totcol; a++) { + create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind); + } + } + else { + create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind); + } + } + + closeMesh(); + + closeGeometry(); + + if (this->export_settings.get_include_shapekeys()) { + Key *key = BKE_key_from_object(ob); + if (key) { + KeyBlock *kb = (KeyBlock *)key->block.first; + /* skip the basis */ + kb = kb->next; + for (; kb; kb = kb->next) { + BKE_keyblock_convert_to_mesh(kb, me); + export_key_mesh(ob, me, kb); + } + } + } + + BKE_id_free(NULL, me); +} + +void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) +{ + std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name); + std::vector nor; + std::vector norind; + + if (exportedGeometry.find(geom_id) != exportedGeometry.end()) { + return; + } + + std::string geom_name = kb->name; + + exportedGeometry.insert(geom_id); + + bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL); + + create_normals(nor, norind, me); + + // openMesh(geoId, geoName, meshId) + openMesh(geom_id, geom_name); + + /* writes for vertex coords */ + createVertsSource(geom_id, me); + + /* writes for normal coords */ + createNormalsSource(geom_id, me, nor); + + bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV); + + /* writes for uv coords if mesh has uv coords */ + if (has_uvs) { + createTexcoordsSource(geom_id, me); + } + + if (has_color) { + createVertexColorSource(geom_id, me); + } + + /* */ + + COLLADASW::Vertices verts(mSW); + verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX)); + COLLADASW::InputList &input_list = verts.getInputList(); + COLLADASW::Input input(COLLADASW::InputSemantic::POSITION, + getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); + input_list.push_back(input); + verts.add(); + + // createLooseEdgeList(ob, me, geom_id, norind); + + /* XXX slow */ + if (ob->totcol) { + for (int a = 0; a < ob->totcol; a++) { + create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind); + } + } + else { + create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind); + } + + closeMesh(); + + closeGeometry(); +} + +void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id) +{ + + MEdge *medges = me->medge; + int totedges = me->totedge; + int edges_in_linelist = 0; + std::vector edge_list; + int index; + + /* Find all loose edges in Mesh + * and save vertex indices in edge_list */ + for (index = 0; index < totedges; index++) { + MEdge *edge = &medges[index]; + + if (edge->flag & ME_LOOSEEDGE) { + edges_in_linelist += 1; + edge_list.push_back(edge->v1); + edge_list.push_back(edge->v2); + } + } + + if (edges_in_linelist > 0) { + /* Create the list of loose edges */ + COLLADASW::Lines lines(mSW); + + lines.setCount(edges_in_linelist); + + COLLADASW::InputList &til = lines.getInputList(); + + /* creates in for vertices */ + COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX, + getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), + 0); + til.push_back(input1); + + lines.prepareToAppendValues(); + + for (index = 0; index < edges_in_linelist; index++) { + lines.appendValues(edge_list[2 * index + 1]); + lines.appendValues(edge_list[2 * index]); + } + lines.finish(); + } +} + +static void prepareToAppendValues(bool is_triangulated, + COLLADASW::PrimitivesBase &primitive_list, + std::vector &vcount_list) +{ + /* performs the actual writing */ + if (is_triangulated) { + ((COLLADASW::Triangles &)primitive_list).prepareToAppendValues(); + } + else { + /* sets */ + primitive_list.setVCountList(vcount_list); + ((COLLADASW::Polylist &)primitive_list).prepareToAppendValues(); + } +} + +static void finish_and_delete_primitive_List(bool is_triangulated, + COLLADASW::PrimitivesBase *primitive_list) +{ + if (is_triangulated) { + ((COLLADASW::Triangles *)primitive_list)->finish(); + } + else { + ((COLLADASW::Polylist *)primitive_list)->finish(); + } + delete primitive_list; +} + +static COLLADASW::PrimitivesBase *create_primitive_list(bool is_triangulated, + COLLADASW::StreamWriter *mSW) +{ + COLLADASW::PrimitivesBase *primitive_list; + + if (is_triangulated) { + primitive_list = new COLLADASW::Triangles(mSW); + } + else { + primitive_list = new COLLADASW::Polylist(mSW); + } + return primitive_list; +} + +static bool collect_vertex_counts_per_poly(Mesh *me, + int material_index, + std::vector &vcount_list) +{ + MPoly *mpolys = me->mpoly; + int totpolys = me->totpoly; + bool is_triangulated = true; + + int i; + /* Expecting that p->mat_nr is always 0 if the mesh has no materials assigned */ + for (i = 0; i < totpolys; i++) { + MPoly *p = &mpolys[i]; + if (p->mat_nr == material_index) { + int vertex_count = p->totloop; + vcount_list.push_back(vertex_count); + if (vertex_count != 3) { + is_triangulated = false; + } + } + } + return is_triangulated; +} + +std::string GeometryExporter::makeVertexColorSourceId(std::string &geom_id, char *layer_name) +{ + std::string result = getIdBySemantics(geom_id, COLLADASW::InputSemantic::COLOR) + "-" + + layer_name; + return result; +} + +/* powerful because it handles both cases when there is material and when there's not */ +void GeometryExporter::create_mesh_primitive_list(short material_index, + bool has_uvs, + bool has_color, + Object *ob, + Mesh *me, + std::string &geom_id, + std::vector &norind) +{ + + MPoly *mpolys = me->mpoly; + MLoop *mloops = me->mloop; + int totpolys = me->totpoly; + + std::vector vcount_list; + + bool is_triangulated = collect_vertex_counts_per_poly(me, material_index, vcount_list); + int polygon_count = vcount_list.size(); + + /* no faces using this material */ + if (polygon_count == 0) { + fprintf( + stderr, "%s: material with index %d is not used.\n", id_name(ob).c_str(), material_index); + return; + } + + Material *ma = ob->totcol ? BKE_object_material_get(ob, material_index + 1) : NULL; + COLLADASW::PrimitivesBase *primitive_list = create_primitive_list(is_triangulated, mSW); + + /* sets count attribute in */ + primitive_list->setCount(polygon_count); + + /* sets material name */ + if (ma) { + std::string material_id = get_material_id(ma); + std::ostringstream ostr; + ostr << translate_id(material_id); + primitive_list->setMaterial(ostr.str()); + } + + COLLADASW::Input vertex_input(COLLADASW::InputSemantic::VERTEX, + getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX), + 0); + COLLADASW::Input normals_input(COLLADASW::InputSemantic::NORMAL, + getUrlBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL), + 1); + + COLLADASW::InputList &til = primitive_list->getInputList(); + til.push_back(vertex_input); + til.push_back(normals_input); + + /* if mesh has uv coords writes for TEXCOORD */ + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); + for (int i = 0; i < num_layers; i++) { + int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, i); + if (!this->export_settings.get_active_uv_only() || layer_index == active_uv_index) { + + // char *name = CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, i); + COLLADASW::Input texcoord_input( + COLLADASW::InputSemantic::TEXCOORD, + makeUrl(makeTexcoordSourceId(geom_id, i, this->export_settings.get_active_uv_only())), + 2, // this is only until we have optimized UV sets + (this->export_settings.get_active_uv_only()) ? 0 : layer_index - 1 /* set (0,1,2,...) */ + ); + til.push_back(texcoord_input); + } + } + + int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + if (totlayer_mcol > 0) { + int map_index = 0; + + for (int a = 0; a < totlayer_mcol; a++) { + char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); + COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR, + makeUrl(makeVertexColorSourceId(geom_id, layer_name)), + (has_uvs) ? 3 : 2, // all color layers have same index order + map_index // set number equals color map index + ); + til.push_back(input4); + map_index++; + } + } + + /* performs the actual writing */ + prepareToAppendValues(is_triangulated, *primitive_list, vcount_list); + + /*

*/ + int texindex = 0; + for (int i = 0; i < totpolys; i++) { + MPoly *p = &mpolys[i]; + int loop_count = p->totloop; + + if (p->mat_nr == material_index) { + MLoop *l = &mloops[p->loopstart]; + BCPolygonNormalsIndices normal_indices = norind[i]; + + for (int j = 0; j < loop_count; j++) { + primitive_list->appendValues(l[j].v); + primitive_list->appendValues(normal_indices[j]); + if (has_uvs) { + primitive_list->appendValues(texindex + j); + } + + if (has_color) { + primitive_list->appendValues(texindex + j); + } + } + } + + texindex += loop_count; + } + + finish_and_delete_primitive_List(is_triangulated, primitive_list); +} + +/* creates for positions */ +void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me) +{ +#if 0 + int totverts = dm->getNumVerts(dm); + MVert *verts = dm->getVertArray(dm); +#endif + int totverts = me->totvert; + MVert *verts = me->mvert; + + COLLADASW::FloatSourceF source(mSW); + source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION)); + source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) + + ARRAY_ID_SUFFIX); + source.setAccessorCount(totverts); + source.setAccessorStride(3); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + /* main function, it creates , */ + source.prepareToAppendValues(); + /* appends data to */ + int i = 0; + for (i = 0; i < totverts; i++) { + Vector co; + if (export_settings.get_apply_global_orientation()) { + bc_add_global_transform(co, verts[i].co, export_settings.get_global_transform()); + } + else { + copy_v3_v3(co, verts[i].co); + } + source.appendValues(co[0], co[1], co[2]); + } + + source.finish(); +} + +void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me) +{ + /* Find number of vertex color layers */ + int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL); + if (totlayer_mcol == 0) { + return; + } + + int map_index = 0; + for (int a = 0; a < totlayer_mcol; a++) { + + map_index++; + MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, a); + + COLLADASW::FloatSourceF source(mSW); + + char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a); + std::string layer_id = makeVertexColorSourceId(geom_id, layer_name); + source.setId(layer_id); + + source.setNodeName(layer_name); + + source.setArrayId(layer_id + ARRAY_ID_SUFFIX); + source.setAccessorCount(me->totloop); + source.setAccessorStride(4); + + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("R"); + param.push_back("G"); + param.push_back("B"); + param.push_back("A"); + + source.prepareToAppendValues(); + + MPoly *mpoly; + int i; + for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) { + MLoopCol *mlc = mloopcol + mpoly->loopstart; + for (int j = 0; j < mpoly->totloop; j++, mlc++) { + source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f); + } + } + + source.finish(); + } +} + +std::string GeometryExporter::makeTexcoordSourceId(std::string &geom_id, + int layer_index, + bool is_single_layer) +{ + char suffix[20]; + if (is_single_layer) { + suffix[0] = '\0'; + } + else { + sprintf(suffix, "-%d", layer_index); + } + return getIdBySemantics(geom_id, COLLADASW::InputSemantic::TEXCOORD) + suffix; +} + +/* creates for texcoords */ +void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me) +{ + + int totpoly = me->totpoly; + int totuv = me->totloop; + MPoly *mpolys = me->mpoly; + + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + + /* write for each layer + * each will get id like meshName + "map-channel-1" */ + int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); + for (int a = 0; a < num_layers; a++) { + int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, a); + if (!this->export_settings.get_active_uv_only() || layer_index == active_uv_index) { + MLoopUV *mloops = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a); + + COLLADASW::FloatSourceF source(mSW); + std::string layer_id = makeTexcoordSourceId( + geom_id, a, this->export_settings.get_active_uv_only()); + source.setId(layer_id); + source.setArrayId(layer_id + ARRAY_ID_SUFFIX); + + source.setAccessorCount(totuv); + source.setAccessorStride(2); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("S"); + param.push_back("T"); + + source.prepareToAppendValues(); + + for (int index = 0; index < totpoly; index++) { + MPoly *mpoly = mpolys + index; + MLoopUV *mloop = mloops + mpoly->loopstart; + for (int j = 0; j < mpoly->totloop; j++) { + source.appendValues(mloop[j].uv[0], mloop[j].uv[1]); + } + } + + source.finish(); + } + } +} + +bool operator<(const Normal &a, const Normal &b) +{ + /* only needed to sort normal vectors and find() them later in a map.*/ + return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z))); +} + +/* creates for normals */ +void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::vector &nor) +{ +#if 0 + int totverts = dm->getNumVerts(dm); + MVert *verts = dm->getVertArray(dm); +#endif + + COLLADASW::FloatSourceF source(mSW); + source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL)); + source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL) + ARRAY_ID_SUFFIX); + source.setAccessorCount((unsigned long)nor.size()); + source.setAccessorStride(3); + COLLADASW::SourceBase::ParameterNameList ¶m = source.getParameterNameList(); + param.push_back("X"); + param.push_back("Y"); + param.push_back("Z"); + + source.prepareToAppendValues(); + + std::vector::iterator it; + for (it = nor.begin(); it != nor.end(); it++) { + Normal &n = *it; + + Vector no{n.x, n.y, n.z}; + if (export_settings.get_apply_global_orientation()) { + bc_add_global_transform(no, export_settings.get_global_transform()); + } + source.appendValues(no[0], no[1], no[2]); + } + + source.finish(); +} + +void GeometryExporter::create_normals(std::vector &normals, + std::vector &polygons_normals, + Mesh *me) +{ + std::map shared_normal_indices; + int last_normal_index = -1; + + MVert *verts = me->mvert; + MLoop *mloops = me->mloop; + float(*lnors)[3] = NULL; + bool use_custom_normals = false; + + BKE_mesh_calc_normals_split(me); + if (CustomData_has_layer(&me->ldata, CD_NORMAL)) { + lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL); + use_custom_normals = true; + } + + for (int poly_index = 0; poly_index < me->totpoly; poly_index++) { + MPoly *mpoly = &me->mpoly[poly_index]; + bool use_vertex_normals = use_custom_normals || mpoly->flag & ME_SMOOTH; + + if (!use_vertex_normals) { + /* For flat faces use face normal as vertex normal: */ + + float vector[3]; + BKE_mesh_calc_poly_normal(mpoly, mloops + mpoly->loopstart, verts, vector); + + Normal n = {vector[0], vector[1], vector[2]}; + normals.push_back(n); + last_normal_index++; + } + + BCPolygonNormalsIndices poly_indices; + for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) { + unsigned int loop_idx = mpoly->loopstart + loop_index; + if (use_vertex_normals) { + float normalized[3]; + + if (use_custom_normals) { + normalize_v3_v3(normalized, lnors[loop_idx]); + } + else { + normal_short_to_float_v3(normalized, verts[mloops[loop_index].v].no); + normalize_v3(normalized); + } + Normal n = {normalized[0], normalized[1], normalized[2]}; + + if (shared_normal_indices.find(n) != shared_normal_indices.end()) { + poly_indices.add_index(shared_normal_indices[n]); + } + else { + last_normal_index++; + poly_indices.add_index(last_normal_index); + shared_normal_indices[n] = last_normal_index; + normals.push_back(n); + } + } + else { + poly_indices.add_index(last_normal_index); + } + } + + polygons_normals.push_back(poly_indices); + } +} + +std::string GeometryExporter::getIdBySemantics(std::string geom_id, + COLLADASW::InputSemantic::Semantics type, + std::string other_suffix) +{ + return geom_id + getSuffixBySemantic(type) + other_suffix; +} + +COLLADASW::URI GeometryExporter::getUrlBySemantics(std::string geom_id, + COLLADASW::InputSemantic::Semantics type, + std::string other_suffix) +{ + + std::string id(getIdBySemantics(geom_id, type, other_suffix)); + return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); +} + +COLLADASW::URI GeometryExporter::makeUrl(std::string id) +{ + return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id); +} diff --git a/source/blender/io/collada/GeometryExporter.h b/source/blender/io/collada/GeometryExporter.h new file mode 100644 index 00000000000..8c7a38fc407 --- /dev/null +++ b/source/blender/io/collada/GeometryExporter.h @@ -0,0 +1,140 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __GEOMETRYEXPORTER_H__ +#define __GEOMETRYEXPORTER_H__ + +#include +#include +#include + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryGeometries.h" +#include "COLLADASWInputList.h" + +#include "DNA_mesh_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_key_types.h" + +#include "ExportSettings.h" +#include "collada_utils.h" +#include "BlenderContext.h" +#include "BKE_key.h" + +class Normal { + public: + float x; + float y; + float z; + + friend bool operator<(const Normal &, const Normal &); +}; + +bool operator<(const Normal &, const Normal &); + +/* TODO: optimize UV sets by making indexed list with duplicates removed */ +class GeometryExporter : COLLADASW::LibraryGeometries { + struct Face { + unsigned int v1, v2, v3, v4; + }; + + public: + /* TODO: optimize UV sets by making indexed list with duplicates removed */ + GeometryExporter(BlenderContext &blender_context, + COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings) + : COLLADASW::LibraryGeometries(sw), + blender_context(blender_context), + export_settings(export_settings) + { + } + + void exportGeom(); + + void operator()(Object *ob); + + void createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id); + + /* powerful because it handles both cases when there is material and when there's not */ + void create_mesh_primitive_list(short material_index, + bool has_uvs, + bool has_color, + Object *ob, + Mesh *me, + std::string &geom_id, + std::vector &norind); + + /* creates for positions */ + void createVertsSource(std::string geom_id, Mesh *me); + + void createVertexColorSource(std::string geom_id, Mesh *me); + + std::string makeTexcoordSourceId(std::string &geom_id, int layer_index, bool is_single_layer); + + /* creates for texcoords */ + void createTexcoordsSource(std::string geom_id, Mesh *me); + void createTesselatedTexcoordsSource(std::string geom_id, Mesh *me); + + /* creates for normals */ + void createNormalsSource(std::string geom_id, Mesh *me, std::vector &nor); + + void create_normals(std::vector &nor, + std::vector &ind, + Mesh *me); + + std::string getIdBySemantics(std::string geom_id, + COLLADASW::InputSemantic::Semantics type, + std::string other_suffix = ""); + std::string makeVertexColorSourceId(std::string &geom_id, char *layer_name); + + COLLADASW::URI getUrlBySemantics(std::string geom_id, + COLLADASW::InputSemantic::Semantics type, + std::string other_suffix = ""); + + COLLADASW::URI makeUrl(std::string id); + + void export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb); + + private: + std::set exportedGeometry; + BlenderContext &blender_context; + BCExportSettings &export_settings; + + Mesh *get_mesh(Scene *sce, Object *ob, int apply_modifiers); +}; + +struct GeometryFunctor { + /* f should have + * void operator()(Object *ob) */ + template + void forEachMeshObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) + { + LinkNode *node; + for (node = export_set; node; node = node->next) { + Object *ob = (Object *)node->link; + if (ob->type == OB_MESH) { + f(ob); + } + } + } +}; + +#endif diff --git a/source/blender/io/collada/ImageExporter.cpp b/source/blender/io/collada/ImageExporter.cpp new file mode 100644 index 00000000000..6e31e17fb26 --- /dev/null +++ b/source/blender/io/collada/ImageExporter.cpp @@ -0,0 +1,169 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "COLLADABUURI.h" +#include "COLLADASWImage.h" + +extern "C" { +#include "DNA_texture_types.h" +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_mesh.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "IMB_imbuf_types.h" +} + +#include "ImageExporter.h" +#include "MaterialExporter.h" + +ImagesExporter::ImagesExporter(COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings, + KeyImageMap &key_image_map) + : COLLADASW::LibraryImages(sw), export_settings(export_settings), key_image_map(key_image_map) +{ + /* pass */ +} + +void ImagesExporter::export_UV_Image(Image *image, bool use_copies) +{ + std::string name(id_name(image)); + std::string translated_name(translate_id(name)); + + ImBuf *imbuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (!imbuf) { + fprintf(stderr, "Collada export: image does not exist:\n%s\n", image->name); + return; + } + + bool is_dirty = BKE_image_is_dirty(image); + + ImageFormatData imageFormat; + BKE_imbuf_to_image_format(&imageFormat, imbuf); + + short image_source = image->source; + bool is_generated = image_source == IMA_SRC_GENERATED; + bool is_packed = BKE_image_has_packedfile(image); + + char export_path[FILE_MAX]; + char source_path[FILE_MAX]; + char export_dir[FILE_MAX]; + char export_file[FILE_MAX]; + + /* Destination folder for exported assets */ + BLI_split_dir_part(this->export_settings.get_filepath(), export_dir, sizeof(export_dir)); + + if (is_generated || is_dirty || use_copies || is_packed) { + + /* make absolute destination path */ + + BLI_strncpy(export_file, name.c_str(), sizeof(export_file)); + BKE_image_path_ensure_ext_from_imformat(export_file, &imageFormat); + + BLI_join_dirfile(export_path, sizeof(export_path), export_dir, export_file); + + /* make dest directory if it doesn't exist */ + BLI_make_existing_file(export_path); + } + + if (is_generated || is_dirty || is_packed) { + + /* This image in its current state only exists in Blender memory. + * So we have to export it. The export will keep the image state intact, + * so the exported file will not be associated with the image. */ + + if (BKE_imbuf_write_as(imbuf, export_path, &imageFormat, true) == 0) { + fprintf(stderr, "Collada export: Cannot export image to:\n%s\n", export_path); + return; + } + BLI_strncpy(export_path, export_file, sizeof(export_path)); + } + else { + + /* make absolute source path */ + BLI_strncpy(source_path, image->name, sizeof(source_path)); + BLI_path_abs(source_path, ID_BLEND_PATH_FROM_GLOBAL(&image->id)); + BLI_cleanup_path(NULL, source_path); + + if (use_copies) { + + /* This image is already located on the file system. + * But we want to create copies here. + * To move images into the same export directory. + * Note: If an image is already located in the export folder, + * then skip the copy (as it would result in a file copy error). */ + + if (BLI_path_cmp(source_path, export_path) != 0) { + if (BLI_copy(source_path, export_path) != 0) { + fprintf(stderr, + "Collada export: Cannot copy image:\n source:%s\ndest :%s\n", + source_path, + export_path); + return; + } + } + + BLI_strncpy(export_path, export_file, sizeof(export_path)); + } + else { + + /* Do not make any copies, but use the source path directly as reference + * to the original image */ + + BLI_strncpy(export_path, source_path, sizeof(export_path)); + } + } + + /* Set name also to mNameNC. + * This helps other viewers import files exported from Blender better. */ + COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(export_path)), + translated_name, + translated_name); + img.add(mSW); + fprintf(stdout, "Collada export: Added image: %s\n", export_file); + + BKE_image_release_ibuf(image, imbuf, NULL); +} + +void ImagesExporter::exportImages(Scene *sce) +{ + bool use_texture_copies = this->export_settings.get_use_texture_copies(); + openLibrary(); + + KeyImageMap::iterator iter; + for (iter = key_image_map.begin(); iter != key_image_map.end(); iter++) { + + Image *image = iter->second; + std::string uid(id_name(image)); + std::string key = translate_id(uid); + + export_UV_Image(image, use_texture_copies); + } + + closeLibrary(); +} diff --git a/source/blender/io/collada/ImageExporter.h b/source/blender/io/collada/ImageExporter.h new file mode 100644 index 00000000000..b72d2709382 --- /dev/null +++ b/source/blender/io/collada/ImageExporter.h @@ -0,0 +1,51 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __IMAGEEXPORTER_H__ +#define __IMAGEEXPORTER_H__ + +#include +#include + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryImages.h" + +#include "DNA_material_types.h" +#include "DNA_image_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "ExportSettings.h" +#include "collada_utils.h" + +class ImagesExporter : COLLADASW::LibraryImages { + public: + ImagesExporter(COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings, + KeyImageMap &key_image_map); + void exportImages(Scene *sce); + + private: + BCExportSettings &export_settings; + KeyImageMap &key_image_map; + void export_UV_Image(Image *image, bool use_texture_copies); +}; + +#endif diff --git a/source/blender/io/collada/ImportSettings.cpp b/source/blender/io/collada/ImportSettings.cpp new file mode 100644 index 00000000000..049ee1d0975 --- /dev/null +++ b/source/blender/io/collada/ImportSettings.cpp @@ -0,0 +1,21 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "ImportSettings.h" diff --git a/source/blender/io/collada/ImportSettings.h b/source/blender/io/collada/ImportSettings.h new file mode 100644 index 00000000000..608d8bff882 --- /dev/null +++ b/source/blender/io/collada/ImportSettings.h @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __IMPORTSETTINGS_H__ +#define __IMPORTSETTINGS_H__ + +typedef struct ImportSettings { + bool import_units; + bool find_chains; + bool auto_connect; + bool fix_orientation; + int min_chain_length; + char *filepath; + bool keep_bind_info; +} ImportSettings; + +#endif diff --git a/source/blender/io/collada/InstanceWriter.cpp b/source/blender/io/collada/InstanceWriter.cpp new file mode 100644 index 00000000000..c9390d23fe7 --- /dev/null +++ b/source/blender/io/collada/InstanceWriter.cpp @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include + +#include "COLLADASWInstanceMaterial.h" + +extern "C" { +#include "BKE_customdata.h" +#include "BKE_material.h" +#include "DNA_mesh_types.h" +} + +#include "InstanceWriter.h" +#include "collada_internal.h" +#include "collada_utils.h" + +void InstanceWriter::add_material_bindings(COLLADASW::BindMaterial &bind_material, + Object *ob, + bool active_uv_only) +{ + for (int a = 0; a < ob->totcol; a++) { + Material *ma = BKE_object_material_get(ob, a + 1); + + COLLADASW::InstanceMaterialList &iml = bind_material.getInstanceMaterialList(); + + if (ma) { + std::string matid(get_material_id(ma)); + matid = translate_id(matid); + std::ostringstream ostr; + ostr << matid; + COLLADASW::InstanceMaterial im(ostr.str(), + COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid)); + + // create for each uv map + Mesh *me = (Mesh *)ob->data; + + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + + int map_index = 0; + int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); + for (int b = 0; b < num_layers; b++) { + if (!active_uv_only || b == active_uv_index) { + char *name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, b); + im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", map_index++)); + } + } + + iml.push_back(im); + } + } +} diff --git a/source/blender/io/collada/InstanceWriter.h b/source/blender/io/collada/InstanceWriter.h new file mode 100644 index 00000000000..cfec1cf7006 --- /dev/null +++ b/source/blender/io/collada/InstanceWriter.h @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __INSTANCEWRITER_H__ +#define __INSTANCEWRITER_H__ + +#include "COLLADASWBindMaterial.h" + +#include "DNA_object_types.h" + +class InstanceWriter { + protected: + void add_material_bindings(COLLADASW::BindMaterial &bind_material, + Object *ob, + bool active_uv_only); +}; + +#endif diff --git a/source/blender/io/collada/LightExporter.cpp b/source/blender/io/collada/LightExporter.cpp new file mode 100644 index 00000000000..463981ceefa --- /dev/null +++ b/source/blender/io/collada/LightExporter.cpp @@ -0,0 +1,156 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +#include "COLLADASWColor.h" +#include "COLLADASWLight.h" + +#include "BLI_math.h" + +#include "LightExporter.h" +#include "collada_internal.h" + +template +void forEachLightObjectInExportSet(Scene *sce, Functor &f, LinkNode *export_set) +{ + LinkNode *node; + for (node = export_set; node; node = node->next) { + Object *ob = (Object *)node->link; + + if (ob->type == OB_LAMP && ob->data) { + f(ob); + } + } +} + +LightsExporter::LightsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings) + : COLLADASW::LibraryLights(sw), export_settings(export_settings) +{ +} + +void LightsExporter::exportLights(Scene *sce) +{ + openLibrary(); + + forEachLightObjectInExportSet(sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); +} + +void LightsExporter::operator()(Object *ob) +{ + Light *la = (Light *)ob->data; + std::string la_id(get_light_id(ob)); + std::string la_name(id_name(la)); + COLLADASW::Color col(la->r * la->energy, la->g * la->energy, la->b * la->energy); + float d, constatt, linatt, quadatt; + + d = la->dist; + + constatt = 1.0f; + + if (la->falloff_type == LA_FALLOFF_INVLINEAR) { + linatt = 1.0f / d; + quadatt = 0.0f; + } + else { + linatt = 0.0f; + quadatt = 1.0f / (d * d); + } + + // sun + if (la->type == LA_SUN) { + COLLADASW::DirectionalLight cla(mSW, la_id, la_name); + cla.setColor(col, false, "color"); + cla.setConstantAttenuation(constatt); + exportBlenderProfile(cla, la); + addLight(cla); + } + + // spot + else if (la->type == LA_SPOT) { + COLLADASW::SpotLight cla(mSW, la_id, la_name); + cla.setColor(col, false, "color"); + cla.setFallOffAngle(RAD2DEGF(la->spotsize), false, "fall_off_angle"); + cla.setFallOffExponent(la->spotblend, false, "fall_off_exponent"); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + exportBlenderProfile(cla, la); + addLight(cla); + } + // lamp + else if (la->type == LA_LOCAL) { + COLLADASW::PointLight cla(mSW, la_id, la_name); + cla.setColor(col, false, "color"); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + exportBlenderProfile(cla, la); + addLight(cla); + } + // area light is not supported + // it will be exported as a local lamp + else { + COLLADASW::PointLight cla(mSW, la_id, la_name); + cla.setColor(col, false, "color"); + cla.setConstantAttenuation(constatt); + cla.setLinearAttenuation(linatt); + cla.setQuadraticAttenuation(quadatt); + exportBlenderProfile(cla, la); + addLight(cla); + } +} + +bool LightsExporter::exportBlenderProfile(COLLADASW::Light &cla, Light *la) +{ + cla.addExtraTechniqueParameter("blender", "type", la->type); + cla.addExtraTechniqueParameter("blender", "flag", la->flag); + cla.addExtraTechniqueParameter("blender", "mode", la->mode); + cla.addExtraTechniqueParameter("blender", "gamma", la->k, "blender_gamma"); + cla.addExtraTechniqueParameter("blender", "red", la->r); + cla.addExtraTechniqueParameter("blender", "green", la->g); + cla.addExtraTechniqueParameter("blender", "blue", la->b); + cla.addExtraTechniqueParameter("blender", "shadow_r", la->shdwr, "blender_shadow_r"); + cla.addExtraTechniqueParameter("blender", "shadow_g", la->shdwg, "blender_shadow_g"); + cla.addExtraTechniqueParameter("blender", "shadow_b", la->shdwb, "blender_shadow_b"); + cla.addExtraTechniqueParameter("blender", "energy", la->energy, "blender_energy"); + cla.addExtraTechniqueParameter("blender", "dist", la->dist, "blender_dist"); + cla.addExtraTechniqueParameter("blender", "spotsize", RAD2DEGF(la->spotsize)); + cla.addExtraTechniqueParameter("blender", "spotblend", la->spotblend); + cla.addExtraTechniqueParameter("blender", "att1", la->att1); + cla.addExtraTechniqueParameter("blender", "att2", la->att2); + // \todo figure out how we can have falloff curve supported here + cla.addExtraTechniqueParameter("blender", "falloff_type", la->falloff_type); + cla.addExtraTechniqueParameter("blender", "clipsta", la->clipsta); + cla.addExtraTechniqueParameter("blender", "clipend", la->clipend); + cla.addExtraTechniqueParameter("blender", "bias", la->bias); + cla.addExtraTechniqueParameter("blender", "soft", la->soft); + cla.addExtraTechniqueParameter("blender", "bufsize", la->bufsize); + cla.addExtraTechniqueParameter("blender", "samp", la->samp); + cla.addExtraTechniqueParameter("blender", "buffers", la->buffers); + cla.addExtraTechniqueParameter("blender", "area_shape", la->area_shape); + cla.addExtraTechniqueParameter("blender", "area_size", la->area_size); + cla.addExtraTechniqueParameter("blender", "area_sizey", la->area_sizey); + cla.addExtraTechniqueParameter("blender", "area_sizez", la->area_sizez); + + return true; +} diff --git a/source/blender/io/collada/LightExporter.h b/source/blender/io/collada/LightExporter.h new file mode 100644 index 00000000000..045ccfe1ce8 --- /dev/null +++ b/source/blender/io/collada/LightExporter.h @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __LIGHTEXPORTER_H__ +#define __LIGHTEXPORTER_H__ + +#include "COLLADASWStreamWriter.h" +#include "COLLADASWLibraryLights.h" + +#include "DNA_light_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "ExportSettings.h" + +class LightsExporter : COLLADASW::LibraryLights { + public: + LightsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); + void exportLights(Scene *sce); + void operator()(Object *ob); + + private: + bool exportBlenderProfile(COLLADASW::Light &cla, Light *la); + BCExportSettings &export_settings; +}; + +#endif diff --git a/source/blender/io/collada/MaterialExporter.cpp b/source/blender/io/collada/MaterialExporter.cpp new file mode 100644 index 00000000000..488d1833e48 --- /dev/null +++ b/source/blender/io/collada/MaterialExporter.cpp @@ -0,0 +1,75 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "MaterialExporter.h" +#include "COLLADABUUtils.h" +#include "collada_internal.h" + +MaterialsExporter::MaterialsExporter(COLLADASW::StreamWriter *sw, + BCExportSettings &export_settings) + : COLLADASW::LibraryMaterials(sw), export_settings(export_settings) +{ + /* pass */ +} + +void MaterialsExporter::exportMaterials(Scene *sce) +{ + if (hasMaterials(sce)) { + openLibrary(); + + MaterialFunctor mf; + mf.forEachMaterialInExportSet( + sce, *this, this->export_settings.get_export_set()); + + closeLibrary(); + } +} + +bool MaterialsExporter::hasMaterials(Scene *sce) +{ + LinkNode *node; + for (node = this->export_settings.get_export_set(); node; node = node->next) { + Object *ob = (Object *)node->link; + int a; + for (a = 0; a < ob->totcol; a++) { + Material *ma = BKE_object_material_get(ob, a + 1); + + // no material, but check all of the slots + if (!ma) { + continue; + } + + return true; + } + } + return false; +} + +void MaterialsExporter::operator()(Material *ma, Object *ob) +{ + std::string mat_name = encode_xml(id_name(ma)); + std::string mat_id = get_material_id(ma); + std::string eff_id = get_effect_id(ma); + + openMaterial(mat_id, mat_name); + addInstanceEffect(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, eff_id)); + + closeMaterial(); +} diff --git a/source/blender/io/collada/MaterialExporter.h b/source/blender/io/collada/MaterialExporter.h new file mode 100644 index 00000000000..be0d939b68a --- /dev/null +++ b/source/blender/io/collada/MaterialExporter.h @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __MATERIALEXPORTER_H__ +#define __MATERIALEXPORTER_H__ + +#include +#include + +#include "COLLADASWLibraryMaterials.h" +#include "COLLADASWStreamWriter.h" + +extern "C" { +#include "BKE_material.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +} + +#include "GeometryExporter.h" +#include "collada_internal.h" +#include "ExportSettings.h" +#include "Materials.h" + +class MaterialsExporter : COLLADASW::LibraryMaterials { + public: + MaterialsExporter(COLLADASW::StreamWriter *sw, BCExportSettings &export_settings); + void exportMaterials(Scene *sce); + void operator()(Material *ma, Object *ob); + + private: + bool hasMaterials(Scene *sce); + BCExportSettings &export_settings; +}; + +// used in forEachMaterialInScene +template class ForEachMaterialFunctor { + std::vector + mMat; // contains list of material names, to avoid duplicate calling of f + Functor *f; + + public: + ForEachMaterialFunctor(Functor *f) : f(f) + { + } + + void operator()(Object *ob) + { + int a; + for (a = 0; a < ob->totcol; a++) { + + Material *ma = BKE_object_material_get(ob, a + 1); + + if (!ma) { + continue; + } + + std::string translated_id = translate_id(id_name(ma)); + if (find(mMat.begin(), mMat.end(), translated_id) == mMat.end()) { + (*this->f)(ma, ob); + + mMat.push_back(translated_id); + } + } + } +}; + +struct MaterialFunctor { + // calls f for each unique material linked to each object in sce + // f should have + // void operator()(Material *ma) + template + void forEachMaterialInExportSet(Scene *sce, Functor &f, LinkNode *export_set) + { + ForEachMaterialFunctor matfunc(&f); + GeometryFunctor gf; + gf.forEachMeshObjectInExportSet>(sce, matfunc, export_set); + } +}; + +#endif diff --git a/source/blender/io/collada/Materials.cpp b/source/blender/io/collada/Materials.cpp new file mode 100644 index 00000000000..06f54884668 --- /dev/null +++ b/source/blender/io/collada/Materials.cpp @@ -0,0 +1,396 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "Materials.h" + +MaterialNode::MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map) + : mContext(C), material(ma), effect(nullptr), key_image_map(&key_image_map) +{ + bNodeTree *new_ntree = prepare_material_nodetree(); + setShaderType(); + if (new_ntree) { + shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, ""); + output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, ""); + add_link(shader_node, 0, output_node, 0); + } +} + +MaterialNode::MaterialNode(bContext *C, + COLLADAFW::EffectCommon *ef, + Material *ma, + UidImageMap &uid_image_map) + : mContext(C), material(ma), effect(ef), uid_image_map(&uid_image_map) +{ + prepare_material_nodetree(); + setShaderType(); + + std::map nmap; +#if 0 + nmap["main"] = add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300); + nmap["emission"] = add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission"); + nmap["add"] = add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400); + nmap["transparent"] = add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200); + nmap["mix"] = add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency"); + nmap["out"] = add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300); + nmap["out"]->flag &= ~NODE_SELECT; + + add_link(ntree, nmap["emission"], 0, nmap["add"], 0); + add_link(ntree, nmap["main"], 0, nmap["add"], 1); + add_link(ntree, nmap["add"], 0, nmap["mix"], 1); + add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2); + + add_link(ntree, nmap["mix"], 0, nmap["out"], 0); + // experimental, probably not used. + make_group(C, ntree, nmap); +#else + shader_node = add_node(SH_NODE_BSDF_PRINCIPLED, 0, 300, ""); + output_node = add_node(SH_NODE_OUTPUT_MATERIAL, 300, 300, ""); + add_link(shader_node, 0, output_node, 0); +#endif +} + +void MaterialNode::setShaderType() +{ +#if 0 + COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType(); + // Currently we only support PBR based shaders + // TODO: simulate the effects with PBR + + // blinn + if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) { + ma->spec_shader = MA_SPEC_BLINN; + ma->spec = ef->getShininess().getFloatValue(); + } + // phong + else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) { + ma->spec_shader = MA_SPEC_PHONG; + ma->har = ef->getShininess().getFloatValue(); + } + // lambert + else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) { + ma->diff_shader = MA_DIFF_LAMBERT; + } + // default - lambert + else { + ma->diff_shader = MA_DIFF_LAMBERT; + fprintf(stderr, "Current shader type is not supported, default to lambert.\n"); + } +#endif +} + +// returns null if material already has a node tree +bNodeTree *MaterialNode::prepare_material_nodetree() +{ + if (material->nodetree) { + ntree = material->nodetree; + return NULL; + } + + material->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree"); + material->use_nodes = true; + ntree = material->nodetree; + return ntree; +} + +bNode *MaterialNode::add_node(int node_type, int locx, int locy, std::string label) +{ + bNode *node = nodeAddStaticNode(mContext, ntree, node_type); + if (node) { + if (label.length() > 0) { + strcpy(node->label, label.c_str()); + } + node->locx = locx; + node->locy = locy; + node->flag |= NODE_SELECT; + } + node_map[label] = node; + return node; +} + +void MaterialNode::add_link(bNode *from_node, int from_index, bNode *to_node, int to_index) +{ + bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); + bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); + + nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); +} + +void MaterialNode::set_reflectivity(COLLADAFW::FloatOrParam &val) +{ + float reflectivity = val.getFloatValue(); + if (reflectivity >= 0) { + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Metallic"); + ((bNodeSocketValueFloat *)socket->default_value)->value = reflectivity; + material->metallic = reflectivity; + } +} + +#if 0 +// needs rework to be done for 2.81 +void MaterialNode::set_shininess(COLLADAFW::FloatOrParam &val) +{ + float roughness = val.getFloatValue(); + if (roughness >= 0) { + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Roughness"); + ((bNodeSocketValueFloat *)socket->default_value)->value = roughness; + } +} +#endif + +void MaterialNode::set_ior(COLLADAFW::FloatOrParam &val) +{ + float ior = val.getFloatValue(); + if (ior < 0) { + fprintf(stderr, + "IOR of negative value is not allowed for materials (using Blender default value " + "instead)"); + return; + } + + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "IOR"); + ((bNodeSocketValueFloat *)socket->default_value)->value = ior; +} + +void MaterialNode::set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, + COLLADAFW::ColorOrTexture &cot, + COLLADAFW::FloatOrParam &val) +{ + /* Handling the alpha value according to the Collada 1.4 reference guide + * see page 7-5 Determining Transparency (Opacity) + */ + + if (effect == nullptr) { + return; + } + + if (cot.isColor() || !cot.isValid()) { + // transparent_cot is either a color or not defined + + float transparent_alpha; + if (cot.isValid()) { + COLLADAFW::Color col = cot.getColor(); + transparent_alpha = col.getAlpha(); + } + else { + // no transparent color defined + transparent_alpha = 1; + } + + float transparency_alpha = val.getFloatValue(); + if (transparency_alpha < 0) { + // transparency is not defined + transparency_alpha = 1; // set to opaque + } + + float alpha = transparent_alpha * transparency_alpha; + if (mode == COLLADAFW::EffectCommon::RGB_ZERO) { + alpha = 1 - alpha; + } + + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); + ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; + material->a = alpha; + } + else if (cot.isTexture()) { + int locy = -300 * (node_map.size() - 2); + add_texture_node(cot, -300, locy, "Alpha"); + } +} + +void MaterialNode::set_diffuse(COLLADAFW::ColorOrTexture &cot) +{ + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Base Color"); + float *fcol = (float *)socket->default_value; + + fcol[0] = material->r = col.getRed(); + fcol[1] = material->g = col.getGreen(); + fcol[2] = material->b = col.getBlue(); + fcol[3] = material->a = col.getAlpha(); + } + else if (cot.isTexture()) { + bNode *texture_node = add_texture_node(cot, -300, locy, "Base Color"); + if (texture_node != NULL) { + add_link(texture_node, 0, shader_node, 0); + } + } +} + +Image *MaterialNode::get_diffuse_image() +{ + bNode *shader = ntreeFindType(ntree, SH_NODE_BSDF_PRINCIPLED); + if (shader == nullptr) { + return nullptr; + } + + bNodeSocket *in_socket = nodeFindSocket(shader, SOCK_IN, "Base Color"); + if (in_socket == nullptr) { + return nullptr; + } + + bNodeLink *link = in_socket->link; + if (link == nullptr) { + return nullptr; + } + + bNode *texture = link->fromnode; + if (texture == nullptr) { + return nullptr; + } + + if (texture->type != SH_NODE_TEX_IMAGE) { + return nullptr; + } + + Image *image = (Image *)texture->id; + return image; +} + +static bNodeSocket *set_color(bNode *node, COLLADAFW::Color col) +{ + bNodeSocket *socket = (bNodeSocket *)BLI_findlink(&node->outputs, 0); + float *fcol = (float *)socket->default_value; + fcol[0] = col.getRed(); + fcol[1] = col.getGreen(); + fcol[2] = col.getBlue(); + + return socket; +} + +void MaterialNode::set_ambient(COLLADAFW::ColorOrTexture &cot) +{ + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + bNode *node = add_node(SH_NODE_RGB, -300, locy, "Ambient"); + set_color(node, col); + // TODO: Connect node + } + // texture + else if (cot.isTexture()) { + add_texture_node(cot, -300, locy, "Ambient"); + // TODO: Connect node + } +} + +void MaterialNode::set_reflective(COLLADAFW::ColorOrTexture &cot) +{ + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + bNode *node = add_node(SH_NODE_RGB, -300, locy, "Reflective"); + set_color(node, col); + // TODO: Connect node + } + // texture + else if (cot.isTexture()) { + add_texture_node(cot, -300, locy, "Reflective"); + // TODO: Connect node + } +} + +void MaterialNode::set_emission(COLLADAFW::ColorOrTexture &cot) +{ + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Emission"); + float *fcol = (float *)socket->default_value; + + fcol[0] = col.getRed(); + fcol[1] = col.getGreen(); + fcol[2] = col.getBlue(); + fcol[3] = col.getAlpha(); + } + else if (cot.isTexture()) { + bNode *texture_node = add_texture_node(cot, -300, locy, "Emission"); + if (texture_node != NULL) { + add_link(texture_node, 0, shader_node, 0); + } + } +} + +void MaterialNode::set_opacity(COLLADAFW::ColorOrTexture &cot) +{ + if (effect == nullptr) { + return; + } + + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = effect->getTransparent().getColor(); + float alpha = effect->getTransparency().getFloatValue(); + + if (col.isValid()) { + alpha *= col.getAlpha(); // Assuming A_ONE opaque mode + } + + bNodeSocket *socket = nodeFindSocket(shader_node, SOCK_IN, "Alpha"); + ((bNodeSocketValueFloat *)socket->default_value)->value = alpha; + } + // texture + else if (cot.isTexture()) { + add_texture_node(cot, -300, locy, "Alpha"); + // TODO: Connect node + } +} + +void MaterialNode::set_specular(COLLADAFW::ColorOrTexture &cot) +{ + int locy = -300 * (node_map.size() - 2); + if (cot.isColor()) { + COLLADAFW::Color col = cot.getColor(); + bNode *node = add_node(SH_NODE_RGB, -300, locy, "Specular"); + set_color(node, col); + // TODO: Connect node + } + // texture + else if (cot.isTexture()) { + add_texture_node(cot, -300, locy, "Specular"); + // TODO: Connect node + } +} + +bNode *MaterialNode::add_texture_node(COLLADAFW::ColorOrTexture &cot, + int locx, + int locy, + std::string label) +{ + if (effect == nullptr) { + return nullptr; + } + + UidImageMap &image_map = *uid_image_map; + + COLLADAFW::Texture ctex = cot.getTexture(); + + COLLADAFW::SamplerPointerArray &samp_array = effect->getSamplerPointerArray(); + COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()]; + + const COLLADAFW::UniqueId &ima_uid = sampler->getSourceImage(); + + if (image_map.find(ima_uid) == image_map.end()) { + fprintf(stderr, "Couldn't find an image by UID.\n"); + return NULL; + } + + Image *ima = image_map[ima_uid]; + bNode *texture_node = add_node(SH_NODE_TEX_IMAGE, locx, locy, label); + texture_node->id = &ima->id; + return texture_node; +} diff --git a/source/blender/io/collada/Materials.h b/source/blender/io/collada/Materials.h new file mode 100644 index 00000000000..0a4f2ee61a5 --- /dev/null +++ b/source/blender/io/collada/Materials.h @@ -0,0 +1,76 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __MATERIALS_H__ +#define __MATERIALS_H__ + +#include +#include + +extern "C" { +#include "BKE_context.h" +#include "BKE_node.h" +#include "BLI_listbase.h" +#include "DNA_material_types.h" +#include "DNA_node_types.h" +} + +#include "collada_utils.h" +#include "COLLADAFWEffectCommon.h" + +typedef std::map NodeMap; + +class MaterialNode { + + private: + bContext *mContext; + Material *material; + COLLADAFW::EffectCommon *effect; + UidImageMap *uid_image_map = nullptr; + KeyImageMap *key_image_map = nullptr; + + NodeMap node_map; + bNodeTree *ntree; + + bNode *shader_node; + bNode *output_node; + + bNodeTree *prepare_material_nodetree(); + bNode *add_node(int node_type, int locx, int locy, std::string label); + void add_link(bNode *from_node, int from_index, bNode *to_node, int to_index); + bNode *add_texture_node(COLLADAFW::ColorOrTexture &cot, int locx, int locy, std::string label); + void setShaderType(); + + public: + MaterialNode(bContext *C, COLLADAFW::EffectCommon *ef, Material *ma, UidImageMap &uid_image_map); + MaterialNode(bContext *C, Material *ma, KeyImageMap &key_image_map); + Image *get_diffuse_image(); + + void set_diffuse(COLLADAFW::ColorOrTexture &cot); + void set_specular(COLLADAFW::ColorOrTexture &cot); + void set_ambient(COLLADAFW::ColorOrTexture &cot); + void set_reflective(COLLADAFW::ColorOrTexture &cot); + void set_emission(COLLADAFW::ColorOrTexture &cot); + void set_opacity(COLLADAFW::ColorOrTexture &cot); + void set_reflectivity(COLLADAFW::FloatOrParam &val); + void set_shininess(COLLADAFW::FloatOrParam &val); + void set_ior(COLLADAFW::FloatOrParam &val); + void set_alpha(COLLADAFW::EffectCommon::OpaqueMode mode, + COLLADAFW::ColorOrTexture &cot, + COLLADAFW::FloatOrParam &val); +}; + +#endif /* __MATERIALS_H__ */ diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp new file mode 100644 index 00000000000..bc6dd4202b1 --- /dev/null +++ b/source/blender/io/collada/MeshImporter.cpp @@ -0,0 +1,1208 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include +#include + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWMeshVertexData.h" +#include "COLLADAFWPolygons.h" + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "BKE_customdata.h" +#include "BKE_displist.h" +#include "BKE_global.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_edgehash.h" +} + +#include "ArmatureImporter.h" +#include "MeshImporter.h" +#include "collada_utils.h" + +// get node name, or fall back to original id if not present (name is optional) +template static const std::string bc_get_dae_name(T *node) +{ + return node->getName().size() ? node->getName() : node->getOriginalId(); +} + +static const char *bc_primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type) +{ + switch (type) { + case COLLADAFW::MeshPrimitive::LINES: + return "LINES"; + case COLLADAFW::MeshPrimitive::LINE_STRIPS: + return "LINESTRIPS"; + case COLLADAFW::MeshPrimitive::POLYGONS: + return "POLYGONS"; + case COLLADAFW::MeshPrimitive::POLYLIST: + return "POLYLIST"; + case COLLADAFW::MeshPrimitive::TRIANGLES: + return "TRIANGLES"; + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: + return "TRIANGLE_FANS"; + case COLLADAFW::MeshPrimitive::TRIANGLE_STRIPS: + return "TRIANGLE_STRIPS"; + case COLLADAFW::MeshPrimitive::POINTS: + return "POINTS"; + case COLLADAFW::MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE: + return "UNDEFINED_PRIMITIVE_TYPE"; + } + return "UNKNOWN"; +} + +static const char *bc_geomTypeToStr(COLLADAFW::Geometry::GeometryType type) +{ + switch (type) { + case COLLADAFW::Geometry::GEO_TYPE_MESH: + return "MESH"; + case COLLADAFW::Geometry::GEO_TYPE_SPLINE: + return "SPLINE"; + case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH: + return "CONVEX_MESH"; + case COLLADAFW::Geometry::GEO_TYPE_UNKNOWN: + default: + return "UNKNOWN"; + } +} + +UVDataWrapper::UVDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) +{ +} + +#ifdef COLLADA_DEBUG +void WVDataWrapper::print() +{ + fprintf(stderr, "UVs:\n"); + switch (mVData->getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); + if (values->getCount()) { + for (int i = 0; i < values->getCount(); i += 2) { + fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i + 1]); + } + } + } break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); + if (values->getCount()) { + for (int i = 0; i < values->getCount(); i += 2) { + fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i + 1]); + } + } + } break; + } + fprintf(stderr, "\n"); +} +#endif + +void UVDataWrapper::getUV(int uv_index, float *uv) +{ + int stride = mVData->getStride(0); + if (stride == 0) { + stride = 2; + } + + switch (mVData->getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); + if (values->empty()) { + return; + } + uv[0] = (*values)[uv_index * stride]; + uv[1] = (*values)[uv_index * stride + 1]; + + } break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); + if (values->empty()) { + return; + } + uv[0] = (float)(*values)[uv_index * stride]; + uv[1] = (float)(*values)[uv_index * stride + 1]; + + } break; + case COLLADAFW::MeshVertexData::DATA_TYPE_UNKNOWN: + default: + fprintf(stderr, "MeshImporter.getUV(): unknown data type\n"); + } +} + +VCOLDataWrapper::VCOLDataWrapper(COLLADAFW::MeshVertexData &vdata) : mVData(&vdata) +{ +} + +void VCOLDataWrapper::get_vcol(int v_index, MLoopCol *mloopcol) +{ + int stride = mVData->getStride(0); + if (stride == 0) { + stride = 3; + } + + switch (mVData->getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getFloatValues(); + if (values->empty() || values->getCount() <= (v_index * stride + 2)) { + return; // xxx need to create an error instead + } + + mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]); + mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]); + mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]); + } break; + + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { + COLLADAFW::ArrayPrimitiveType *values = mVData->getDoubleValues(); + if (values->empty() || values->getCount() <= (v_index * stride + 2)) { + return; // xxx need to create an error instead + } + + mloopcol->r = unit_float_to_uchar_clamp((*values)[v_index * stride]); + mloopcol->g = unit_float_to_uchar_clamp((*values)[v_index * stride + 1]); + mloopcol->b = unit_float_to_uchar_clamp((*values)[v_index * stride + 2]); + } break; + default: + fprintf(stderr, "VCOLDataWrapper.getvcol(): unknown data type\n"); + } +} + +MeshImporter::MeshImporter( + UnitConverter *unitconv, ArmatureImporter *arm, Main *bmain, Scene *sce, ViewLayer *view_layer) + : unitconverter(unitconv), + m_bmain(bmain), + scene(sce), + view_layer(view_layer), + armature_importer(arm) +{ + /* pass */ +} + +bool MeshImporter::set_poly_indices( + MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count) +{ + mpoly->loopstart = loop_index; + mpoly->totloop = loop_count; + bool broken_loop = false; + for (int index = 0; index < loop_count; index++) { + + /* Test if loop defines a hole */ + if (!broken_loop) { + for (int i = 0; i < index; i++) { + if (indices[i] == indices[index]) { + // duplicate index -> not good + broken_loop = true; + } + } + } + + mloop->v = indices[index]; + mloop++; + } + return broken_loop; +} + +void MeshImporter::set_vcol(MLoopCol *mlc, + VCOLDataWrapper &vob, + int loop_index, + COLLADAFW::IndexList &index_list, + int count) +{ + int index; + for (index = 0; index < count; index++, mlc++) { + int v_index = index_list.getIndex(index + loop_index); + vob.get_vcol(v_index, mlc); + } +} + +void MeshImporter::set_face_uv(MLoopUV *mloopuv, + UVDataWrapper &uvs, + int start_index, + COLLADAFW::IndexList &index_list, + int count) +{ + // per face vertex indices, this means for quad we have 4 indices, not 8 + COLLADAFW::UIntValuesArray &indices = index_list.getIndices(); + + for (int index = 0; index < count; index++) { + int uv_index = indices[index + start_index]; + uvs.getUV(uv_index, mloopuv[index].uv); + } +} + +#ifdef COLLADA_DEBUG +void MeshImporter::print_index_list(COLLADAFW::IndexList &index_list) +{ + fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str()); + for (int i = 0; i < index_list.getIndicesCount(); i += 2) { + fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1)); + } + fprintf(stderr, "\n"); +} +#endif + +/* checks if mesh has supported primitive types: lines, polylist, triangles, triangle_fans */ +bool MeshImporter::is_nice_mesh(COLLADAFW::Mesh *mesh) +{ + COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); + + const std::string &name = bc_get_dae_name(mesh); + + for (unsigned int i = 0; i < prim_arr.getCount(); i++) { + + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType(); + + const char *type_str = bc_primTypeToStr(type); + + // OpenCollada passes POLYGONS type for + if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) { + + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; + COLLADAFW::Polygons::VertexCountArray &vca = mpvc->getGroupedVerticesVertexCountArray(); + + int hole_count = 0; + int nonface_count = 0; + + for (unsigned int j = 0; j < vca.getCount(); j++) { + int count = vca[j]; + if (abs(count) < 3) { + nonface_count++; + } + + if (count < 0) { + hole_count++; + } + } + + if (hole_count > 0) { + fprintf(stderr, + "WARNING: Primitive %s in %s: %d holes not imported (unsupported)\n", + type_str, + name.c_str(), + hole_count); + } + + if (nonface_count > 0) { + fprintf(stderr, + "WARNING: Primitive %s in %s: %d faces with vertex count < 3 (rejected)\n", + type_str, + name.c_str(), + nonface_count); + } + } + + else if (type == COLLADAFW::MeshPrimitive::LINES) { + // TODO: Add Checker for line syntax here + } + + else if (type != COLLADAFW::MeshPrimitive::TRIANGLES && + type != COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { + fprintf(stderr, "ERROR: Primitive type %s is not supported.\n", type_str); + return false; + } + } + + return true; +} + +void MeshImporter::read_vertices(COLLADAFW::Mesh *mesh, Mesh *me) +{ + // vertices + COLLADAFW::MeshVertexData &pos = mesh->getPositions(); + if (pos.empty()) { + return; + } + + int stride = pos.getStride(0); + if (stride == 0) { + stride = 3; + } + + me->totvert = pos.getFloatValues()->getCount() / stride; + me->mvert = (MVert *)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert); + + MVert *mvert; + int i; + + for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) { + get_vector(mvert->co, pos, i, stride); + } +} + +// ===================================================================== +// condition 1: The Primitive has normals +// condition 2: The number of normals equals the number of faces. +// return true if both conditions apply. +// return false otherwise. +// ===================================================================== +bool MeshImporter::primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp) +{ + + bool has_useable_normals = false; + + int normals_count = mp->getNormalIndices().getCount(); + if (normals_count > 0) { + int index_count = mp->getPositionIndices().getCount(); + if (index_count == normals_count) { + has_useable_normals = true; + } + else { + fprintf(stderr, + "Warning: Number of normals %d is different from the number of vertices %d, " + "skipping normals\n", + normals_count, + index_count); + } + } + + return has_useable_normals; +} + +// ===================================================================== +// Assume that only TRIANGLES, TRIANGLE_FANS, POLYLIST and POLYGONS +// have faces. (to be verified) +// ===================================================================== +bool MeshImporter::primitive_has_faces(COLLADAFW::MeshPrimitive *mp) +{ + + bool has_faces = false; + int type = mp->getPrimitiveType(); + switch (type) { + case COLLADAFW::MeshPrimitive::TRIANGLES: + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: + case COLLADAFW::MeshPrimitive::POLYLIST: + case COLLADAFW::MeshPrimitive::POLYGONS: { + has_faces = true; + break; + } + default: { + has_faces = false; + break; + } + } + return has_faces; +} + +static std::string extract_vcolname(const COLLADAFW::String &collada_id) +{ + std::string colname = collada_id; + int spos = colname.find("-mesh-colors-"); + if (spos != std::string::npos) { + colname = colname.substr(spos + 13); + } + return colname; +} + +// ================================================================= +// Return the number of faces by summing up +// the facecounts of the parts. +// hint: This is done because mesh->getFacesCount() does +// count loose edges as extra faces, which is not what we want here. +// ================================================================= +void MeshImporter::allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me) +{ + COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives(); + int total_poly_count = 0; + int total_loop_count = 0; + + // collect edge_count and face_count from all parts + for (int i = 0; i < prim_arr.getCount(); i++) { + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + int type = mp->getPrimitiveType(); + switch (type) { + case COLLADAFW::MeshPrimitive::TRIANGLES: + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: + case COLLADAFW::MeshPrimitive::POLYLIST: + case COLLADAFW::MeshPrimitive::POLYGONS: { + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; + size_t prim_poly_count = mpvc->getFaceCount(); + + size_t prim_loop_count = 0; + for (int index = 0; index < prim_poly_count; index++) { + int vcount = get_vertex_count(mpvc, index); + if (vcount > 0) { + prim_loop_count += vcount; + total_poly_count++; + } + else { + // TODO: this is a hole and not another polygon! + } + } + + total_loop_count += prim_loop_count; + + break; + } + default: + break; + } + } + + // Add the data containers + if (total_poly_count > 0) { + me->totpoly = total_poly_count; + me->totloop = total_loop_count; + me->mpoly = (MPoly *)CustomData_add_layer(&me->pdata, CD_MPOLY, CD_CALLOC, NULL, me->totpoly); + me->mloop = (MLoop *)CustomData_add_layer(&me->ldata, CD_MLOOP, CD_CALLOC, NULL, me->totloop); + + unsigned int totuvset = collada_mesh->getUVCoords().getInputInfosArray().getCount(); + for (int i = 0; i < totuvset; i++) { + if (collada_mesh->getUVCoords().getLength(i) == 0) { + totuvset = 0; + break; + } + } + + if (totuvset > 0) { + for (int i = 0; i < totuvset; i++) { + COLLADAFW::MeshVertexData::InputInfos *info = + collada_mesh->getUVCoords().getInputInfosArray()[i]; + COLLADAFW::String &uvname = info->mName; + // Allocate space for UV_data + CustomData_add_layer_named( + &me->ldata, CD_MLOOPUV, CD_DEFAULT, NULL, me->totloop, uvname.c_str()); + } + // activate the first uv map + me->mloopuv = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, 0); + } + + int totcolset = collada_mesh->getColors().getInputInfosArray().getCount(); + if (totcolset > 0) { + for (int i = 0; i < totcolset; i++) { + COLLADAFW::MeshVertexData::InputInfos *info = + collada_mesh->getColors().getInputInfosArray()[i]; + COLLADAFW::String colname = extract_vcolname(info->mName); + CustomData_add_layer_named( + &me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop, colname.c_str()); + } + me->mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, 0); + } + } +} + +unsigned int MeshImporter::get_vertex_count(COLLADAFW::Polygons *mp, int index) +{ + int type = mp->getPrimitiveType(); + int result; + switch (type) { + case COLLADAFW::MeshPrimitive::TRIANGLES: + case COLLADAFW::MeshPrimitive::TRIANGLE_FANS: { + result = 3; + break; + } + case COLLADAFW::MeshPrimitive::POLYLIST: + case COLLADAFW::MeshPrimitive::POLYGONS: { + result = mp->getGroupedVerticesVertexCountArray()[index]; + break; + } + default: { + result = -1; + break; + } + } + return result; +} + +unsigned int MeshImporter::get_loose_edge_count(COLLADAFW::Mesh *mesh) +{ + COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); + int loose_edge_count = 0; + + // collect edge_count and face_count from all parts + for (int i = 0; i < prim_arr.getCount(); i++) { + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + int type = mp->getPrimitiveType(); + switch (type) { + case COLLADAFW::MeshPrimitive::LINES: { + size_t prim_totface = mp->getFaceCount(); + loose_edge_count += prim_totface; + break; + } + default: + break; + } + } + return loose_edge_count; +} + +// ================================================================= +// This function is copied from source/blender/editors/mesh/mesh_data.c +// +// TODO: (As discussed with sergey-) : +// Maybe move this function to blenderkernel/intern/mesh.c +// and add definition to BKE_mesh.c +// ================================================================= +void MeshImporter::mesh_add_edges(Mesh *mesh, int len) +{ + CustomData edata; + MEdge *medge; + int totedge; + + if (len == 0) { + return; + } + + totedge = mesh->totedge + len; + + /* update customdata */ + CustomData_copy(&mesh->edata, &edata, CD_MASK_MESH.emask, CD_DEFAULT, totedge); + CustomData_copy_data(&mesh->edata, &edata, 0, 0, mesh->totedge); + + if (!CustomData_has_layer(&edata, CD_MEDGE)) { + CustomData_add_layer(&edata, CD_MEDGE, CD_CALLOC, NULL, totedge); + } + + CustomData_free(&mesh->edata, mesh->totedge); + mesh->edata = edata; + BKE_mesh_update_customdata_pointers(mesh, false); /* new edges don't change tessellation */ + + /* set default flags */ + medge = &mesh->medge[mesh->totedge]; + for (int i = 0; i < len; i++, medge++) { + medge->flag = ME_EDGEDRAW | ME_EDGERENDER | SELECT; + } + + mesh->totedge = totedge; +} + +// ================================================================= +// Read all loose edges. +// Important: This function assumes that all edges from existing +// faces have already been generated and added to me->medge +// So this function MUST be called after read_faces() (see below) +// ================================================================= +void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) +{ + unsigned int loose_edge_count = get_loose_edge_count(mesh); + if (loose_edge_count > 0) { + + unsigned int face_edge_count = me->totedge; + /* unsigned int total_edge_count = loose_edge_count + face_edge_count; */ /* UNUSED */ + + mesh_add_edges(me, loose_edge_count); + MEdge *med = me->medge + face_edge_count; + + COLLADAFW::MeshPrimitiveArray &prim_arr = mesh->getMeshPrimitives(); + + for (int index = 0; index < prim_arr.getCount(); index++) { + COLLADAFW::MeshPrimitive *mp = prim_arr[index]; + + int type = mp->getPrimitiveType(); + if (type == COLLADAFW::MeshPrimitive::LINES) { + unsigned int edge_count = mp->getFaceCount(); + unsigned int *indices = mp->getPositionIndices().getData(); + + for (int j = 0; j < edge_count; j++, med++) { + med->bweight = 0; + med->crease = 0; + med->flag |= ME_LOOSEEDGE; + med->v1 = indices[2 * j]; + med->v2 = indices[2 * j + 1]; + } + } + } + } +} + +// ======================================================================= +// Read all faces from TRIANGLES, TRIANGLE_FANS, POLYLIST, POLYGON +// Important: This function MUST be called before read_lines() +// Otherwise we will loose all edges from faces (see read_lines() above) +// +// TODO: import uv set names +// ======================================================================== +void MeshImporter::read_polys(COLLADAFW::Mesh *collada_mesh, Mesh *me) +{ + unsigned int i; + + allocate_poly_data(collada_mesh, me); + + UVDataWrapper uvs(collada_mesh->getUVCoords()); + VCOLDataWrapper vcol(collada_mesh->getColors()); + + MPoly *mpoly = me->mpoly; + MLoop *mloop = me->mloop; + int loop_index = 0; + + MaterialIdPrimitiveArrayMap mat_prim_map; + + COLLADAFW::MeshPrimitiveArray &prim_arr = collada_mesh->getMeshPrimitives(); + COLLADAFW::MeshVertexData &nor = collada_mesh->getNormals(); + + for (i = 0; i < prim_arr.getCount(); i++) { + + COLLADAFW::MeshPrimitive *mp = prim_arr[i]; + + // faces + size_t prim_totpoly = mp->getFaceCount(); + unsigned int *position_indices = mp->getPositionIndices().getData(); + unsigned int *normal_indices = mp->getNormalIndices().getData(); + + bool mp_has_normals = primitive_has_useable_normals(mp); + bool mp_has_faces = primitive_has_faces(mp); + + int collada_meshtype = mp->getPrimitiveType(); + + // since we cannot set mpoly->mat_nr here, we store a portion of me->mpoly in Primitive + Primitive prim = {mpoly, 0}; + + // If MeshPrimitive is TRIANGLE_FANS we split it into triangles + // The first trifan vertex will be the first vertex in every triangle + // XXX The proper function of TRIANGLE_FANS is not tested!!! + // XXX In particular the handling of the normal_indices looks very wrong to me + if (collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLE_FANS) { + unsigned int grouped_vertex_count = mp->getGroupedVertexElementsCount(); + for (unsigned int group_index = 0; group_index < grouped_vertex_count; group_index++) { + unsigned int first_vertex = position_indices[0]; // Store first trifan vertex + unsigned int first_normal = normal_indices[0]; // Store first trifan vertex normal + unsigned int vertex_count = mp->getGroupedVerticesVertexCount(group_index); + + for (unsigned int vertex_index = 0; vertex_index < vertex_count - 2; vertex_index++) { + // For each triangle store indices of its 3 vertices + unsigned int triangle_vertex_indices[3] = { + first_vertex, position_indices[1], position_indices[2]}; + set_poly_indices(mpoly, mloop, loop_index, triangle_vertex_indices, 3); + + if (mp_has_normals) { // vertex normals, same implementation as for the triangles + // the same for vertces normals + unsigned int vertex_normal_indices[3] = { + first_normal, normal_indices[1], normal_indices[2]}; + if (!is_flat_face(vertex_normal_indices, nor, 3)) { + mpoly->flag |= ME_SMOOTH; + } + normal_indices++; + } + + mpoly++; + mloop += 3; + loop_index += 3; + prim.totpoly++; + } + + // Moving cursor to the next triangle fan. + if (mp_has_normals) { + normal_indices += 2; + } + + position_indices += 2; + } + } + + if (collada_meshtype == COLLADAFW::MeshPrimitive::POLYLIST || + collada_meshtype == COLLADAFW::MeshPrimitive::POLYGONS || + collada_meshtype == COLLADAFW::MeshPrimitive::TRIANGLES) { + COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons *)mp; + unsigned int start_index = 0; + + COLLADAFW::IndexListArray &index_list_array_uvcoord = mp->getUVCoordIndicesArray(); + COLLADAFW::IndexListArray &index_list_array_vcolor = mp->getColorIndicesArray(); + + int invalid_loop_holes = 0; + for (unsigned int j = 0; j < prim_totpoly; j++) { + + // Vertices in polygon: + int vcount = get_vertex_count(mpvc, j); + if (vcount < 0) { + continue; // TODO: add support for holes + } + + bool broken_loop = set_poly_indices(mpoly, mloop, loop_index, position_indices, vcount); + if (broken_loop) { + invalid_loop_holes += 1; + } + + for (unsigned int uvset_index = 0; uvset_index < index_list_array_uvcoord.getCount(); + uvset_index++) { + // get mtface by face index and uv set index + COLLADAFW::IndexList &index_list = *index_list_array_uvcoord[uvset_index]; + MLoopUV *mloopuv = (MLoopUV *)CustomData_get_layer_named( + &me->ldata, CD_MLOOPUV, index_list.getName().c_str()); + if (mloopuv == NULL) { + fprintf(stderr, + "Collada import: Mesh [%s] : Unknown reference to TEXCOORD [#%s].\n", + me->id.name, + index_list.getName().c_str()); + } + else { + set_face_uv(mloopuv + loop_index, + uvs, + start_index, + *index_list_array_uvcoord[uvset_index], + vcount); + } + } + + if (mp_has_normals) { + if (!is_flat_face(normal_indices, nor, vcount)) { + mpoly->flag |= ME_SMOOTH; + } + } + + if (mp->hasColorIndices()) { + int vcolor_count = index_list_array_vcolor.getCount(); + + for (unsigned int vcolor_index = 0; vcolor_index < vcolor_count; vcolor_index++) { + + COLLADAFW::IndexList &color_index_list = *mp->getColorIndices(vcolor_index); + COLLADAFW::String colname = extract_vcolname(color_index_list.getName()); + MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_named( + &me->ldata, CD_MLOOPCOL, colname.c_str()); + if (mloopcol == NULL) { + fprintf(stderr, + "Collada import: Mesh [%s] : Unknown reference to VCOLOR [#%s].\n", + me->id.name, + color_index_list.getName().c_str()); + } + else { + set_vcol(mloopcol + loop_index, vcol, start_index, color_index_list, vcount); + } + } + } + + mpoly++; + mloop += vcount; + loop_index += vcount; + start_index += vcount; + prim.totpoly++; + + if (mp_has_normals) { + normal_indices += vcount; + } + + position_indices += vcount; + } + + if (invalid_loop_holes > 0) { + fprintf(stderr, + "Collada import: Mesh [%s] : contains %d unsupported loops (holes).\n", + me->id.name, + invalid_loop_holes); + } + } + + else if (collada_meshtype == COLLADAFW::MeshPrimitive::LINES) { + continue; // read the lines later after all the rest is done + } + + if (mp_has_faces) { + mat_prim_map[mp->getMaterialId()].push_back(prim); + } + } + + geom_uid_mat_mapping_map[collada_mesh->getUniqueId()] = mat_prim_map; +} + +void MeshImporter::get_vector(float v[3], COLLADAFW::MeshVertexData &arr, int i, int stride) +{ + i *= stride; + + switch (arr.getType()) { + case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT: { + COLLADAFW::ArrayPrimitiveType *values = arr.getFloatValues(); + if (values->empty()) { + return; + } + + v[0] = (*values)[i++]; + v[1] = (*values)[i++]; + if (stride >= 3) { + v[2] = (*values)[i]; + } + else { + v[2] = 0.0f; + } + + } break; + case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE: { + COLLADAFW::ArrayPrimitiveType *values = arr.getDoubleValues(); + if (values->empty()) { + return; + } + + v[0] = (float)(*values)[i++]; + v[1] = (float)(*values)[i++]; + if (stride >= 3) { + v[2] = (float)(*values)[i]; + } + else { + v[2] = 0.0f; + } + } break; + default: + break; + } +} + +bool MeshImporter::is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData &nor, int count) +{ + float a[3], b[3]; + + get_vector(a, nor, *nind, 3); + normalize_v3(a); + + nind++; + + for (int i = 1; i < count; i++, nind++) { + get_vector(b, nor, *nind, 3); + normalize_v3(b); + + float dp = dot_v3v3(a, b); + + if (dp < 0.99999f || dp > 1.00001f) { + return false; + } + } + + return true; +} + +Object *MeshImporter::get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid) +{ + if (uid_object_map.find(geom_uid) != uid_object_map.end()) { + return uid_object_map[geom_uid]; + } + return NULL; +} + +Mesh *MeshImporter::get_mesh_by_geom_uid(const COLLADAFW::UniqueId &mesh_uid) +{ + if (uid_mesh_map.find(mesh_uid) != uid_mesh_map.end()) { + return uid_mesh_map[mesh_uid]; + } + return NULL; +} + +std::string *MeshImporter::get_geometry_name(const std::string &mesh_name) +{ + if (this->mesh_geom_map.find(mesh_name) != this->mesh_geom_map.end()) { + return &this->mesh_geom_map[mesh_name]; + } + return NULL; +} + +/** + * this function checks if both objects have the same + * materials assigned to Object (in the same order) + * returns true if condition matches, otherwise false; + */ +static bool bc_has_same_material_configuration(Object *ob1, Object *ob2) +{ + if (ob1->totcol != ob2->totcol) { + return false; // not same number of materials + } + if (ob1->totcol == 0) { + return false; // no material at all + } + + for (int index = 0; index < ob1->totcol; index++) { + if (ob1->matbits[index] != ob2->matbits[index]) { + return false; // shouldn't happen + } + if (ob1->matbits[index] == 0) { + return false; // shouldn't happen + } + if (ob1->mat[index] != ob2->mat[index]) { + return false; // different material assignment + } + } + return true; +} + +/** + * + * Caution here: This code assumes that all materials are assigned to Object + * and no material is assigned to Data. + * That is true right after the objects have been imported. + * + */ +static void bc_copy_materials_to_data(Object *ob, Mesh *me) +{ + for (int index = 0; index < ob->totcol; index++) { + ob->matbits[index] = 0; + me->mat[index] = ob->mat[index]; + } +} + +/** + * + * Remove all references to materials from the object + * + */ +static void bc_remove_materials_from_object(Object *ob, Mesh *me) +{ + for (int index = 0; index < ob->totcol; index++) { + ob->matbits[index] = 0; + ob->mat[index] = NULL; + } +} + +/** + * Returns the list of Users of the given Mesh object. + * Note: This function uses the object user flag to control + * which objects have already been processed. + */ +std::vector MeshImporter::get_all_users_of(Mesh *reference_mesh) +{ + std::vector mesh_users; + for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); + ++it) { + Object *ob = (*it); + if (bc_is_marked(ob)) { + bc_remove_mark(ob); + Mesh *me = (Mesh *)ob->data; + if (me == reference_mesh) { + mesh_users.push_back(ob); + } + } + } + return mesh_users; +} + +/** + * + * During import all materials have been assigned to Object. + * Now we iterate over the imported objects and optimize + * the assignments as follows: + * + * for each imported geometry: + * if number of users is 1: + * get the user (object) + * move the materials from Object to Data + * else: + * determine which materials are assigned to the first user + * check if all other users have the same materials in the same order + * if the check is positive: + * Add the materials of the first user to the geometry + * adjust all other users accordingly. + * + */ +void MeshImporter::optimize_material_assignements() +{ + for (std::vector::iterator it = imported_objects.begin(); it != imported_objects.end(); + ++it) { + Object *ob = (*it); + Mesh *me = (Mesh *)ob->data; + if (ID_REAL_USERS(&me->id) == 1) { + bc_copy_materials_to_data(ob, me); + bc_remove_materials_from_object(ob, me); + bc_remove_mark(ob); + } + else if (ID_REAL_USERS(&me->id) > 1) { + bool can_move = true; + std::vector mesh_users = get_all_users_of(me); + if (mesh_users.size() > 1) { + Object *ref_ob = mesh_users[0]; + for (int index = 1; index < mesh_users.size(); index++) { + if (!bc_has_same_material_configuration(ref_ob, mesh_users[index])) { + can_move = false; + break; + } + } + if (can_move) { + bc_copy_materials_to_data(ref_ob, me); + for (int index = 0; index < mesh_users.size(); index++) { + Object *object = mesh_users[index]; + bc_remove_materials_from_object(object, me); + bc_remove_mark(object); + } + } + } + } + } +} + +/** + * We do not know in advance which objects will share geometries. + * And we do not know either if the objects which share geometries + * come along with different materials. So we first create the objects + * and assign the materials to Object, then in a later cleanup we decide + * which materials shall be moved to the created geometries. Also see + * optimize_material_assignements() above. + */ +void MeshImporter::assign_material_to_geom( + COLLADAFW::MaterialBinding cmaterial, + std::map &uid_material_map, + Object *ob, + const COLLADAFW::UniqueId *geom_uid, + short mat_index) +{ + const COLLADAFW::UniqueId &ma_uid = cmaterial.getReferencedMaterial(); + + // do we know this material? + if (uid_material_map.find(ma_uid) == uid_material_map.end()) { + + fprintf(stderr, "Cannot find material by UID.\n"); + return; + } + + // first time we get geom_uid, ma_uid pair. Save for later check. + materials_mapped_to_geom.insert( + std::pair(*geom_uid, ma_uid)); + + Material *ma = uid_material_map[ma_uid]; + + // Attention! This temporarily assigns material to object on purpose! + // See note above. + ob->actcol = 0; + BKE_object_material_assign(m_bmain, ob, ma, mat_index + 1, BKE_MAT_ASSIGN_OBJECT); + + MaterialIdPrimitiveArrayMap &mat_prim_map = geom_uid_mat_mapping_map[*geom_uid]; + COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId(); + + // assign material indices to mesh faces + if (mat_prim_map.find(mat_id) != mat_prim_map.end()) { + + std::vector &prims = mat_prim_map[mat_id]; + + std::vector::iterator it; + + for (it = prims.begin(); it != prims.end(); it++) { + Primitive &prim = *it; + MPoly *mpoly = prim.mpoly; + + for (int i = 0; i < prim.totpoly; i++, mpoly++) { + mpoly->mat_nr = mat_index; + } + } + } +} + +Object *MeshImporter::create_mesh_object( + COLLADAFW::Node *node, + COLLADAFW::InstanceGeometry *geom, + bool isController, + std::map &uid_material_map) +{ + const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId(); + + // check if node instantiates controller or geometry + if (isController) { + + geom_uid = armature_importer->get_geometry_uid(*geom_uid); + + if (!geom_uid) { + fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n"); + return NULL; + } + } + else { + + if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) { + // this could happen if a mesh was not created + // (e.g. if it contains unsupported geometry) + fprintf(stderr, "Couldn't find a mesh by UID.\n"); + return NULL; + } + } + if (!uid_mesh_map[*geom_uid]) { + return NULL; + } + + // name Object + const std::string &id = node->getName().size() ? node->getName() : node->getOriginalId(); + const char *name = (id.length()) ? id.c_str() : NULL; + + // add object + Object *ob = bc_add_object(m_bmain, scene, view_layer, OB_MESH, name); + bc_set_mark(ob); // used later for material assignment optimization + + // store object pointer for ArmatureImporter + uid_object_map[*geom_uid] = ob; + imported_objects.push_back(ob); + + // replace ob->data freeing the old one + Mesh *old_mesh = (Mesh *)ob->data; + Mesh *new_mesh = uid_mesh_map[*geom_uid]; + + BKE_mesh_assign_object(m_bmain, ob, new_mesh); + BKE_mesh_calc_normals(new_mesh); + + /* Because BKE_mesh_assign_object would have already decreased it... */ + id_us_plus(&old_mesh->id); + + BKE_id_free_us(m_bmain, old_mesh); + + COLLADAFW::MaterialBindingArray &mat_array = geom->getMaterialBindings(); + + // loop through geom's materials + for (unsigned int i = 0; i < mat_array.getCount(); i++) { + + if (mat_array[i].getReferencedMaterial().isValid()) { + assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid, i); + } + else { + fprintf(stderr, "invalid referenced material for %s\n", mat_array[i].getName().c_str()); + } + } + + // clean up the mesh + BKE_mesh_validate((Mesh *)ob->data, false, false); + + return ob; +} + +// create a mesh storing a pointer in a map so it can be retrieved later by geometry UID +bool MeshImporter::write_geometry(const COLLADAFW::Geometry *geom) +{ + + if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) { + // TODO: report warning + fprintf(stderr, "Mesh type %s is not supported\n", bc_geomTypeToStr(geom->getType())); + return true; + } + + COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh *)geom; + + if (!is_nice_mesh(mesh)) { + fprintf(stderr, "Ignoring mesh %s\n", bc_get_dae_name(mesh).c_str()); + return true; + } + + const std::string &str_geom_id = mesh->getName().size() ? mesh->getName() : + mesh->getOriginalId(); + Mesh *me = BKE_mesh_add(m_bmain, (char *)str_geom_id.c_str()); + id_us_min(&me->id); // is already 1 here, but will be set later in BKE_mesh_assign_object + + // store the Mesh pointer to link it later with an Object + // mesh_geom_map needed to map mesh to its geometry name (for shape key naming) + this->uid_mesh_map[mesh->getUniqueId()] = me; + this->mesh_geom_map[std::string(me->id.name)] = str_geom_id; + + read_vertices(mesh, me); + read_polys(mesh, me); + BKE_mesh_calc_edges(me, false, false); + // read_lines() must be called after the face edges have been generated. + // Otherwise the loose edges will be silently deleted again. + read_lines(mesh, me); + + return true; +} diff --git a/source/blender/io/collada/MeshImporter.h b/source/blender/io/collada/MeshImporter.h new file mode 100644 index 00000000000..9517587013d --- /dev/null +++ b/source/blender/io/collada/MeshImporter.h @@ -0,0 +1,182 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __MESHIMPORTER_H__ +#define __MESHIMPORTER_H__ + +#include +#include + +#include "COLLADAFWIndexList.h" +#include "COLLADAFWPolygons.h" +#include "COLLADAFWInstanceGeometry.h" +#include "COLLADAFWMaterialBinding.h" +#include "COLLADAFWMesh.h" +#include "COLLADAFWMeshVertexData.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWTextureCoordinateBinding.h" +#include "COLLADAFWTypes.h" +#include "COLLADAFWUniqueId.h" + +#include "ArmatureImporter.h" +#include "collada_utils.h" + +extern "C" { +#include "BLI_edgehash.h" +#include "DNA_material_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +} + +/* only for ArmatureImporter to "see" MeshImporter::get_object_by_geom_uid */ +class MeshImporterBase { + public: + virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid) = 0; + virtual Mesh *get_mesh_by_geom_uid(const COLLADAFW::UniqueId &mesh_uid) = 0; + virtual std::string *get_geometry_name(const std::string &mesh_name) = 0; +}; + +class UVDataWrapper { + COLLADAFW::MeshVertexData *mVData; + + public: + UVDataWrapper(COLLADAFW::MeshVertexData &vdata); + +#ifdef COLLADA_DEBUG + void print(); +#endif + + void getUV(int uv_index, float *uv); +}; + +class VCOLDataWrapper { + COLLADAFW::MeshVertexData *mVData; + + public: + VCOLDataWrapper(COLLADAFW::MeshVertexData &vdata); + void get_vcol(int v_index, MLoopCol *mloopcol); +}; + +class MeshImporter : public MeshImporterBase { + private: + UnitConverter *unitconverter; + + Main *m_bmain; + Scene *scene; + ViewLayer *view_layer; + + ArmatureImporter *armature_importer; + + std::map mesh_geom_map; /* needed for correct shape key naming */ + std::map uid_mesh_map; /* geometry unique id-to-mesh map */ + std::map uid_object_map; /* geom uid-to-object */ + std::vector imported_objects; /* list of imported objects */ + + /* this structure is used to assign material indices to polygons + * it holds a portion of Mesh faces and corresponds to a DAE primitive list + * (, , etc.) */ + struct Primitive { + MPoly *mpoly; + unsigned int totpoly; + }; + typedef std::map> MaterialIdPrimitiveArrayMap; + /* crazy name! */ + std::map geom_uid_mat_mapping_map; + /* < materials that have already been mapped to a geometry. + * A pair/of geom uid and mat uid, one geometry can have several materials */ + std::multimap materials_mapped_to_geom; + + bool set_poly_indices( + MPoly *mpoly, MLoop *mloop, int loop_index, unsigned int *indices, int loop_count); + + void set_face_uv(MLoopUV *mloopuv, + UVDataWrapper &uvs, + int loop_index, + COLLADAFW::IndexList &index_list, + int count); + + void set_vcol(MLoopCol *mloopcol, + VCOLDataWrapper &vob, + int loop_index, + COLLADAFW::IndexList &index_list, + int count); + +#ifdef COLLADA_DEBUG + void print_index_list(COLLADAFW::IndexList &index_list); +#endif + + bool is_nice_mesh(COLLADAFW::Mesh *mesh); + + void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me); + + bool primitive_has_useable_normals(COLLADAFW::MeshPrimitive *mp); + bool primitive_has_faces(COLLADAFW::MeshPrimitive *mp); + + static void mesh_add_edges(Mesh *mesh, int len); + + unsigned int get_loose_edge_count(COLLADAFW::Mesh *mesh); + + CustomData create_edge_custom_data(EdgeHash *eh); + + void allocate_poly_data(COLLADAFW::Mesh *collada_mesh, Mesh *me); + + /* TODO: import uv set names */ + void read_polys(COLLADAFW::Mesh *mesh, Mesh *me); + void read_lines(COLLADAFW::Mesh *mesh, Mesh *me); + unsigned int get_vertex_count(COLLADAFW::Polygons *mp, int index); + + void get_vector(float v[3], COLLADAFW::MeshVertexData &arr, int i, int stride); + + bool is_flat_face(unsigned int *nind, COLLADAFW::MeshVertexData &nor, int count); + + std::vector get_all_users_of(Mesh *reference_mesh); + + public: + MeshImporter(UnitConverter *unitconv, + ArmatureImporter *arm, + Main *bmain, + Scene *sce, + ViewLayer *view_layer); + + virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId &geom_uid); + + virtual Mesh *get_mesh_by_geom_uid(const COLLADAFW::UniqueId &geom_uid); + + void optimize_material_assignements(); + + void assign_material_to_geom(COLLADAFW::MaterialBinding cmaterial, + std::map &uid_material_map, + Object *ob, + const COLLADAFW::UniqueId *geom_uid, + short mat_index); + + Object *create_mesh_object(COLLADAFW::Node *node, + COLLADAFW::InstanceGeometry *geom, + bool isController, + std::map &uid_material_map); + + /* create a mesh storing a pointer in a map so it can be retrieved later by geometry UID */ + bool write_geometry(const COLLADAFW::Geometry *geom); + std::string *get_geometry_name(const std::string &mesh_name); +}; + +#endif diff --git a/source/blender/io/collada/SceneExporter.cpp b/source/blender/io/collada/SceneExporter.cpp new file mode 100644 index 00000000000..42901bd2a4a --- /dev/null +++ b/source/blender/io/collada/SceneExporter.cpp @@ -0,0 +1,242 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +extern "C" { +#include "BLI_utildefines.h" +#include "BKE_collection.h" +#include "BKE_object.h" +#include "BLI_listbase.h" +#include "BKE_lib_id.h" +} + +#include "SceneExporter.h" +#include "collada_utils.h" +#include "BCSampleData.h" + +void SceneExporter::exportScene() +{ + Scene *scene = blender_context.get_scene(); + + /* */ + std::string name = id_name(scene); + openVisualScene(translate_id(name), encode_xml(name)); + exportHierarchy(); + closeVisualScene(); + closeLibrary(); +} + +void SceneExporter::exportHierarchy() +{ + LinkNode *node; + ColladaBaseNodes base_objects; + + /* Ensure all objects in the export_set are marked */ + for (node = this->export_settings.get_export_set(); node; node = node->next) { + Object *ob = (Object *)node->link; + ob->id.tag |= LIB_TAG_DOIT; + } + + /* Now find all exportable base objects (highest in export hierarchy) */ + for (node = this->export_settings.get_export_set(); node; node = node->next) { + Object *ob = (Object *)node->link; + if (this->export_settings.is_export_root(ob)) { + switch (ob->type) { + case OB_MESH: + case OB_CAMERA: + case OB_LAMP: + case OB_EMPTY: + case OB_GPENCIL: + case OB_ARMATURE: + base_objects.add(ob); + break; + } + } + } + + /* And now export the base objects: */ + for (int index = 0; index < base_objects.size(); index++) { + Object *ob = base_objects.get(index); + writeNode(ob); + if (bc_is_marked(ob)) { + bc_remove_mark(ob); + } + } +} + +void SceneExporter::writeNodeList(std::vector &child_objects, Object *parent) +{ + /* TODO: Handle the case where a parent is not exported + * Actually i am not even sure if this can be done at all + * in a good way. + * I really prefer to enforce the export of hidden + * elements in an object hierarchy. When the children of + * the hidden elements are exported as well. */ + for (int i = 0; i < child_objects.size(); i++) { + Object *child = child_objects[i]; + writeNode(child); + if (bc_is_marked(child)) { + bc_remove_mark(child); + } + } +} + +void SceneExporter::writeNode(Object *ob) +{ + ViewLayer *view_layer = blender_context.get_view_layer(); + + std::vector child_objects; + bc_get_children(child_objects, ob, view_layer); + bool can_export = bc_is_in_Export_set(this->export_settings.get_export_set(), ob, view_layer); + + /* Add associated armature first if available */ + bool armature_exported = false; + Object *ob_arm = bc_get_assigned_armature(ob); + + if (ob_arm != NULL) { + armature_exported = bc_is_in_Export_set( + this->export_settings.get_export_set(), ob_arm, view_layer); + if (armature_exported && bc_is_marked(ob_arm)) { + writeNode(ob_arm); + bc_remove_mark(ob_arm); + armature_exported = true; + } + } + + if (can_export) { + COLLADASW::Node colladaNode(mSW); + colladaNode.setNodeId(translate_id(id_name(ob))); + colladaNode.setNodeName(encode_xml(id_name(ob))); + colladaNode.setType(COLLADASW::Node::NODE); + + colladaNode.start(); + if (ob->type == OB_MESH && armature_exported) { + /* for skinned mesh we write obmat in */ + TransformWriter::add_node_transform_identity(colladaNode, this->export_settings); + } + else { + TransformWriter::add_node_transform_ob(colladaNode, ob, this->export_settings); + } + + /* */ + if (ob->type == OB_MESH) { + bool instance_controller_created = false; + if (armature_exported) { + instance_controller_created = arm_exporter->add_instance_controller(ob); + } + if (!instance_controller_created) { + COLLADASW::InstanceGeometry instGeom(mSW); + instGeom.setUrl(COLLADASW::URI( + COLLADABU::Utils::EMPTY_STRING, + get_geometry_id(ob, this->export_settings.get_use_object_instantiation()))); + instGeom.setName(encode_xml(id_name(ob))); + InstanceWriter::add_material_bindings( + instGeom.getBindMaterial(), ob, this->export_settings.get_active_uv_only()); + instGeom.add(); + } + } + + /* */ + else if (ob->type == OB_ARMATURE) { + arm_exporter->add_armature_bones(ob, view_layer, this, child_objects); + } + + /* */ + else if (ob->type == OB_CAMERA) { + COLLADASW::InstanceCamera instCam( + mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob))); + instCam.add(); + } + + /* */ + else if (ob->type == OB_LAMP) { + COLLADASW::InstanceLight instLa( + mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob))); + instLa.add(); + } + + /* empty object */ + else if (ob->type == OB_EMPTY) { /* TODO: handle groups (OB_DUPLICOLLECTION */ + if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->instance_collection) { + Collection *collection = ob->instance_collection; + /* printf("group detected '%s'\n", group->id.name + 2); */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, object) { + printf("\t%s\n", object->id.name); + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } + + if (BLI_listbase_is_empty(&ob->constraints) == false) { + bConstraint *con = (bConstraint *)ob->constraints.first; + while (con) { + std::string con_name(encode_xml(con->name)); + std::string con_tag = con_name + "_constraint"; + printf("%s\n", con_name.c_str()); + printf("%s\n\n", con_tag.c_str()); + colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "type", con->type); + colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "enforce", con->enforce); + colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "flag", con->flag); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "headtail", con->headtail); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "lin_error", con->lin_error); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "own_space", con->ownspace); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "rot_error", con->rot_error); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "tar_space", con->tarspace); + colladaNode.addExtraTechniqueChildParameter( + "blender", con_tag, "lin_error", con->lin_error); + + /* not ideal: add the target object name as another parameter. + * No real mapping in the .dae + * Need support for multiple target objects also. */ + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + ListBase targets = {NULL, NULL}; + if (cti && cti->get_constraint_targets) { + + bConstraintTarget *ct; + Object *obtar; + + cti->get_constraint_targets(con, &targets); + + for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) { + obtar = ct->tar; + std::string tar_id((obtar) ? id_name(obtar) : ""); + colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "target_id", tar_id); + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(con, &targets, 1); + } + } + + con = con->next; + } + } + } + bc_remove_mark(ob); + writeNodeList(child_objects, ob); + colladaNode.end(); + } + else { + writeNodeList(child_objects, ob); + } +} diff --git a/source/blender/io/collada/SceneExporter.h b/source/blender/io/collada/SceneExporter.h new file mode 100644 index 00000000000..a61d045ad5d --- /dev/null +++ b/source/blender/io/collada/SceneExporter.h @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __SCENEEXPORTER_H__ +#define __SCENEEXPORTER_H__ + +#include +#include +#include + +extern "C" { +#include "DNA_scene_types.h" +#include "DNA_object_types.h" +#include "DNA_collection_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_mesh_types.h" +#include "DNA_image_types.h" +#include "DNA_material_types.h" +#include "DNA_texture_types.h" +#include "DNA_anim_types.h" +#include "DNA_action_types.h" +#include "DNA_curve_types.h" +#include "DNA_constraint_types.h" +#include "DNA_armature_types.h" +#include "DNA_modifier_types.h" +#include "DNA_userdef_types.h" + +#include "BKE_fcurve.h" +#include "BKE_animsys.h" +#include "BLI_path_util.h" +#include "BKE_constraint.h" +#include "BLI_fileops.h" +#include "ED_keyframing.h" +} + +#include "COLLADASWAsset.h" +#include "COLLADASWLibraryVisualScenes.h" +#include "COLLADASWNode.h" +#include "COLLADASWSource.h" +#include "COLLADASWInstanceGeometry.h" +#include "COLLADASWInputList.h" +#include "COLLADASWPrimitves.h" +#include "COLLADASWVertices.h" +#include "COLLADASWLibraryAnimations.h" +#include "COLLADASWLibraryImages.h" +#include "COLLADASWLibraryEffects.h" +#include "COLLADASWImage.h" +#include "COLLADASWEffectProfile.h" +#include "COLLADASWColorOrTexture.h" +#include "COLLADASWParamTemplate.h" +#include "COLLADASWParamBase.h" +#include "COLLADASWSurfaceInitOption.h" +#include "COLLADASWSampler.h" +#include "COLLADASWScene.h" +#include "COLLADASWTechnique.h" +#include "COLLADASWTexture.h" +#include "COLLADASWLibraryMaterials.h" +#include "COLLADASWBindMaterial.h" +#include "COLLADASWInstanceCamera.h" +#include "COLLADASWInstanceLight.h" +#include "COLLADASWConstants.h" +#include "COLLADASWLibraryControllers.h" +#include "COLLADASWInstanceController.h" +#include "COLLADASWInstanceNode.h" +#include "COLLADASWBaseInputElement.h" + +#include "ArmatureExporter.h" +#include "ExportSettings.h" + +extern void bc_get_children(std::vector &child_set, Object *ob, ViewLayer *view_layer); + +class SceneExporter : COLLADASW::LibraryVisualScenes, + protected TransformWriter, + protected InstanceWriter { + public: + SceneExporter(BlenderContext &blender_context, + COLLADASW::StreamWriter *sw, + ArmatureExporter *arm, + BCExportSettings &export_settings) + : COLLADASW::LibraryVisualScenes(sw), + blender_context(blender_context), + arm_exporter(arm), + export_settings(export_settings) + { + } + + void exportScene(); + + private: + BlenderContext &blender_context; + friend class ArmatureExporter; + ArmatureExporter *arm_exporter; + BCExportSettings &export_settings; + + void exportHierarchy(); + void writeNodeList(std::vector &child_objects, Object *parent); + void writeNode(Object *ob); +}; + +#endif diff --git a/source/blender/io/collada/SkinInfo.cpp b/source/blender/io/collada/SkinInfo.cpp new file mode 100644 index 00000000000..d8804a1e831 --- /dev/null +++ b/source/blender/io/collada/SkinInfo.cpp @@ -0,0 +1,357 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include + +#if !defined(WIN32) +# include +#endif + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_compiler_attrs.h" + +#include "DNA_armature_types.h" +#include "DNA_modifier_types.h" +#include "DNA_scene_types.h" + +#include "BKE_action.h" +#include "BKE_object.h" +#include "BKE_object_deform.h" + +#include "ED_mesh.h" +#include "ED_object.h" + +#include "SkinInfo.h" +#include "collada_utils.h" + +/* use name, or fall back to original id if name not present (name is optional) */ +template static const char *bc_get_joint_name(T *node) +{ + const std::string &id = node->getName(); + return id.size() ? id.c_str() : node->getOriginalId().c_str(); +} + +/* This is used to store data passed in write_controller_data. + * Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members + * so that arrays don't get freed until we free them explicitly. */ +SkinInfo::SkinInfo() +{ + /* pass */ +} + +SkinInfo::SkinInfo(const SkinInfo &skin) + : weights(skin.weights), + joint_data(skin.joint_data), + unit_converter(skin.unit_converter), + ob_arm(skin.ob_arm), + controller_uid(skin.controller_uid), + parent(skin.parent) +{ + copy_m4_m4(bind_shape_matrix, (float(*)[4])skin.bind_shape_matrix); + + transfer_uint_array_data_const(skin.joints_per_vertex, joints_per_vertex); + transfer_uint_array_data_const(skin.weight_indices, weight_indices); + transfer_int_array_data_const(skin.joint_indices, joint_indices); +} + +SkinInfo::SkinInfo(UnitConverter *conv) : unit_converter(conv), ob_arm(NULL), parent(NULL) +{ +} + +/* nobody owns the data after this, so it should be freed manually with releaseMemory */ +template void SkinInfo::transfer_array_data(T &src, T &dest) +{ + dest.setData(src.getData(), src.getCount()); + src.yieldOwnerShip(); + dest.yieldOwnerShip(); +} + +/* when src is const we cannot src.yieldOwnerShip, this is used by copy constructor */ +void SkinInfo::transfer_int_array_data_const(const COLLADAFW::IntValuesArray &src, + COLLADAFW::IntValuesArray &dest) +{ + dest.setData((int *)src.getData(), src.getCount()); + dest.yieldOwnerShip(); +} + +void SkinInfo::transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray &src, + COLLADAFW::UIntValuesArray &dest) +{ + dest.setData((unsigned int *)src.getData(), src.getCount()); + dest.yieldOwnerShip(); +} + +void SkinInfo::borrow_skin_controller_data(const COLLADAFW::SkinControllerData *skin) +{ + transfer_array_data((COLLADAFW::UIntValuesArray &)skin->getJointsPerVertex(), joints_per_vertex); + transfer_array_data((COLLADAFW::UIntValuesArray &)skin->getWeightIndices(), weight_indices); + transfer_array_data((COLLADAFW::IntValuesArray &)skin->getJointIndices(), joint_indices); + // transfer_array_data(skin->getWeights(), weights); + + /* cannot transfer data for FloatOrDoubleArray, copy values manually */ + const COLLADAFW::FloatOrDoubleArray &weight = skin->getWeights(); + for (unsigned int i = 0; i < weight.getValuesCount(); i++) { + weights.push_back(bc_get_float_value(weight, i)); + } + + unit_converter->dae_matrix_to_mat4_(bind_shape_matrix, skin->getBindShapeMatrix()); +} + +void SkinInfo::free() +{ + joints_per_vertex.releaseMemory(); + weight_indices.releaseMemory(); + joint_indices.releaseMemory(); + // weights.releaseMemory(); +} + +/* using inverse bind matrices to construct armature + * it is safe to invert them to get the original matrices + * because if they are inverse matrices, they can be inverted */ +void SkinInfo::add_joint(const COLLADABU::Math::Matrix4 &matrix) +{ + JointData jd; + unit_converter->dae_matrix_to_mat4_(jd.inv_bind_mat, matrix); + joint_data.push_back(jd); +} + +void SkinInfo::set_controller(const COLLADAFW::SkinController *co) +{ + controller_uid = co->getUniqueId(); + + /* fill in joint UIDs */ + const COLLADAFW::UniqueIdArray &joint_uids = co->getJoints(); + for (unsigned int i = 0; i < joint_uids.getCount(); i++) { + joint_data[i].joint_uid = joint_uids[i]; + + /* store armature pointer */ + // JointData& jd = joint_index_to_joint_info_map[i]; + // jd.ob_arm = ob_arm; + + /* now we'll be able to get inv bind matrix from joint id */ + // joint_id_to_joint_index_map[joint_ids[i]] = i; + } +} + +/* called from write_controller */ +Object *SkinInfo::create_armature(Main *bmain, Scene *scene, ViewLayer *view_layer) +{ + ob_arm = bc_add_object(bmain, scene, view_layer, OB_ARMATURE, NULL); + return ob_arm; +} + +Object *SkinInfo::set_armature(Object *ob_arm) +{ + if (this->ob_arm) { + return this->ob_arm; + } + + this->ob_arm = ob_arm; + return ob_arm; +} + +bool SkinInfo::get_joint_inv_bind_matrix(float inv_bind_mat[4][4], COLLADAFW::Node *node) +{ + const COLLADAFW::UniqueId &uid = node->getUniqueId(); + std::vector::iterator it; + for (it = joint_data.begin(); it != joint_data.end(); it++) { + if ((*it).joint_uid == uid) { + copy_m4_m4(inv_bind_mat, (*it).inv_bind_mat); + return true; + } + } + + return false; +} + +Object *SkinInfo::BKE_armature_from_object() +{ + return ob_arm; +} + +const COLLADAFW::UniqueId &SkinInfo::get_controller_uid() +{ + return controller_uid; +} + +/* check if this skin controller references a joint or any descendant of it + * + * some nodes may not be referenced by SkinController, + * in this case to determine if the node belongs to this armature, + * we need to search down the tree */ +bool SkinInfo::uses_joint_or_descendant(COLLADAFW::Node *node) +{ + const COLLADAFW::UniqueId &uid = node->getUniqueId(); + std::vector::iterator it; + for (it = joint_data.begin(); it != joint_data.end(); it++) { + if ((*it).joint_uid == uid) { + return true; + } + } + + COLLADAFW::NodePointerArray &children = node->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (uses_joint_or_descendant(children[i])) { + return true; + } + } + + return false; +} + +void SkinInfo::link_armature(bContext *C, + Object *ob, + std::map &joint_by_uid, + TransformReader *tm) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + ModifierData *md = ED_object_modifier_add(NULL, bmain, scene, ob, NULL, eModifierType_Armature); + ArmatureModifierData *amd = (ArmatureModifierData *)md; + amd->object = ob_arm; + +#if 1 + /* XXX Why do we enforce objects to be children of Armatures if they weren't so before ?*/ + if (!BKE_object_is_child_recursive(ob_arm, ob)) { + bc_set_parent(ob, ob_arm, C); + } +#else + Object workob; + ob->parent = ob_arm; + ob->partype = PAROBJECT; + + BKE_object_workob_calc_parent(scene, ob, &workob); + invert_m4_m4(ob->parentinv, workob.obmat); + + DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY); +#endif + copy_m4_m4(ob->obmat, bind_shape_matrix); + BKE_object_apply_mat4(ob, ob->obmat, 0, 0); + + amd->deformflag = ARM_DEF_VGROUP; + + /* create all vertex groups */ + std::vector::iterator it; + int joint_index; + for (it = joint_data.begin(), joint_index = 0; it != joint_data.end(); it++, joint_index++) { + const char *name = "Group"; + + /* skip joints that have invalid UID */ + if ((*it).joint_uid == COLLADAFW::UniqueId::INVALID) { + continue; + } + + /* name group by joint node name */ + + if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) { + name = bc_get_joint_name(joint_by_uid[(*it).joint_uid]); + } + + BKE_object_defgroup_add_name(ob, name); + } + + /* - number of joints per vertex - joints_per_vertex + * - [[bone index, weight index] * joints per vertex] * vertices - weight indices + * ^ bone index can be -1 meaning weight toward bind shape, how to express this in Blender? + * + * for each vertex in weight indices + * for each bone index in vertex + * add vertex to group at group index + * treat group index -1 specially + * + * get def group by index with BLI_findlink */ + + for (unsigned int vertex = 0, weight = 0; vertex < joints_per_vertex.getCount(); vertex++) { + + unsigned int limit = weight + joints_per_vertex[vertex]; + for (; weight < limit; weight++) { + int joint = joint_indices[weight], joint_weight = weight_indices[weight]; + + /* -1 means "weight towards the bind shape", we just don't assign it to any group */ + if (joint != -1) { + bDeformGroup *def = (bDeformGroup *)BLI_findlink(&ob->defbase, joint); + + ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE); + } + } + } +} + +bPoseChannel *SkinInfo::get_pose_channel_from_node(COLLADAFW::Node *node) +{ + return BKE_pose_channel_find_name(ob_arm->pose, bc_get_joint_name(node)); +} + +void SkinInfo::set_parent(Object *_parent) +{ + parent = _parent; +} + +Object *SkinInfo::get_parent() +{ + return parent; +} + +void SkinInfo::find_root_joints(const std::vector &root_joints, + std::map &joint_by_uid, + std::vector &result) +{ + std::vector::const_iterator it; + /* for each root_joint */ + for (it = root_joints.begin(); it != root_joints.end(); it++) { + COLLADAFW::Node *root = *it; + std::vector::iterator ji; + /* for each joint_data in this skin */ + for (ji = joint_data.begin(); ji != joint_data.end(); ji++) { + if (joint_by_uid.find((*ji).joint_uid) != joint_by_uid.end()) { + /* get joint node from joint map */ + COLLADAFW::Node *joint = joint_by_uid[(*ji).joint_uid]; + + /* find if joint node is in the tree belonging to the root_joint */ + if (find_node_in_tree(joint, root)) { + if (std::find(result.begin(), result.end(), root) == result.end()) { + result.push_back(root); + } + } + } + } + } +} + +bool SkinInfo::find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root) +{ + if (node == tree_root) { + return true; + } + + COLLADAFW::NodePointerArray &children = tree_root->getChildNodes(); + for (unsigned int i = 0; i < children.getCount(); i++) { + if (find_node_in_tree(node, children[i])) { + return true; + } + } + + return false; +} diff --git a/source/blender/io/collada/SkinInfo.h b/source/blender/io/collada/SkinInfo.h new file mode 100644 index 00000000000..255d6d9b1f3 --- /dev/null +++ b/source/blender/io/collada/SkinInfo.h @@ -0,0 +1,130 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __SKININFO_H__ +#define __SKININFO_H__ + +#include +#include + +#include "COLLADAFWUniqueId.h" +#include "COLLADAFWTypes.h" +#include "COLLADAFWNode.h" +#include "COLLADAFWSkinController.h" +#include "COLLADAFWSkinControllerData.h" + +#include "DNA_object_types.h" +#include "BKE_context.h" + +#include "TransformReader.h" +#include "collada_internal.h" + +// This is used to store data passed in write_controller_data. +// Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members +// so that arrays don't get freed until we free them explicitly. +class SkinInfo { + private: + // to build armature bones from inverse bind matrices + struct JointData { + float inv_bind_mat[4][4]; // joint inverse bind matrix + COLLADAFW::UniqueId joint_uid; // joint node UID + // Object *ob_arm; // armature object + }; + + float bind_shape_matrix[4][4]; + + // data from COLLADAFW::SkinControllerData, each array should be freed + COLLADAFW::UIntValuesArray joints_per_vertex; + COLLADAFW::UIntValuesArray weight_indices; + COLLADAFW::IntValuesArray joint_indices; + // COLLADAFW::FloatOrDoubleArray weights; + std::vector weights; + + std::vector joint_data; // index to this vector is joint index + + UnitConverter *unit_converter; + + Object *ob_arm; + COLLADAFW::UniqueId controller_uid; + Object *parent; + + public: + SkinInfo(); + SkinInfo(const SkinInfo &skin); + SkinInfo(UnitConverter *conv); + + // nobody owns the data after this, so it should be freed manually with releaseMemory + template void transfer_array_data(T &src, T &dest); + + // when src is const we cannot src.yieldOwnerShip, this is used by copy constructor + void transfer_int_array_data_const(const COLLADAFW::IntValuesArray &src, + COLLADAFW::IntValuesArray &dest); + + void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray &src, + COLLADAFW::UIntValuesArray &dest); + + void borrow_skin_controller_data(const COLLADAFW::SkinControllerData *skin); + + void free(); + + // using inverse bind matrices to construct armature + // it is safe to invert them to get the original matrices + // because if they are inverse matrices, they can be inverted + void add_joint(const COLLADABU::Math::Matrix4 &matrix); + + void set_controller(const COLLADAFW::SkinController *co); + + // called from write_controller + Object *create_armature(Main *bmain, Scene *scene, ViewLayer *view_layer); + + Object *set_armature(Object *ob_arm); + + bool get_joint_inv_bind_matrix(float inv_bind_mat[4][4], COLLADAFW::Node *node); + + Object *BKE_armature_from_object(); + + const COLLADAFW::UniqueId &get_controller_uid(); + + // check if this skin controller references a joint or any descendant of it + // + // some nodes may not be referenced by SkinController, + // in this case to determine if the node belongs to this armature, + // we need to search down the tree + bool uses_joint_or_descendant(COLLADAFW::Node *node); + + void link_armature(bContext *C, + Object *ob, + std::map &joint_by_uid, + TransformReader *tm); + + bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node); + + void set_parent(Object *_parent); + + Object *get_parent(); + + void find_root_joints(const std::vector &root_joints, + std::map &joint_by_uid, + std::vector &result); + + bool find_node_in_tree(COLLADAFW::Node *node, COLLADAFW::Node *tree_root); +}; + +#endif diff --git a/source/blender/io/collada/TransformReader.cpp b/source/blender/io/collada/TransformReader.cpp new file mode 100644 index 00000000000..8ee31f80405 --- /dev/null +++ b/source/blender/io/collada/TransformReader.cpp @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "TransformReader.h" + +TransformReader::TransformReader(UnitConverter *conv) : unit_converter(conv) +{ + /* pass */ +} + +void TransformReader::get_node_mat(float mat[4][4], + COLLADAFW::Node *node, + std::map *animation_map, + Object *ob) +{ + get_node_mat(mat, node, animation_map, ob, NULL); +} + +void TransformReader::get_node_mat(float mat[4][4], + COLLADAFW::Node *node, + std::map *animation_map, + Object *ob, + float parent_mat[4][4]) +{ + float cur[4][4]; + float copy[4][4]; + + unit_m4(mat); + + for (unsigned int i = 0; i < node->getTransformations().getCount(); i++) { + + COLLADAFW::Transformation *tm = node->getTransformations()[i]; + COLLADAFW::Transformation::TransformationType type = tm->getTransformationType(); + + switch (type) { + case COLLADAFW::Transformation::MATRIX: + // When matrix AND Trans/Rot/Scale are defined for a node, + // then this is considered as redundant information. + // So if we find a Matrix we use that and return. + dae_matrix_to_mat4(tm, mat); + if (parent_mat) { + mul_m4_m4m4(mat, parent_mat, mat); + } + return; + case COLLADAFW::Transformation::TRANSLATE: + dae_translate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::ROTATE: + dae_rotate_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::SCALE: + dae_scale_to_mat4(tm, cur); + break; + case COLLADAFW::Transformation::LOOKAT: + fprintf(stderr, "|! LOOKAT transformations are not supported yet.\n"); + break; + case COLLADAFW::Transformation::SKEW: + fprintf(stderr, "|! SKEW transformations are not supported yet.\n"); + break; + } + + copy_m4_m4(copy, mat); + mul_m4_m4m4(mat, copy, cur); + + if (animation_map) { + // AnimationList that drives this Transformation + const COLLADAFW::UniqueId &anim_list_id = tm->getAnimationList(); + + // store this so later we can link animation data with ob + Animation anim = {ob, node, tm}; + (*animation_map)[anim_list_id] = anim; + } + } + + if (parent_mat) { + mul_m4_m4m4(mat, parent_mat, mat); + } +} + +void TransformReader::dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) +{ + COLLADAFW::Rotate *ro = (COLLADAFW::Rotate *)tm; + COLLADABU::Math::Vector3 &axis = ro->getRotationAxis(); + const float angle = (float)DEG2RAD(ro->getRotationAngle()); + const float ax[] = {(float)axis[0], (float)axis[1], (float)axis[2]}; + // float quat[4]; + // axis_angle_to_quat(quat, axis, angle); + // quat_to_mat4(m, quat); + axis_angle_to_mat4(m, ax, angle); +} + +void TransformReader::dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) +{ + COLLADAFW::Translate *tra = (COLLADAFW::Translate *)tm; + COLLADABU::Math::Vector3 &t = tra->getTranslation(); + + unit_m4(m); + + m[3][0] = (float)t[0]; + m[3][1] = (float)t[1]; + m[3][2] = (float)t[2]; +} + +void TransformReader::dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) +{ + COLLADABU::Math::Vector3 &s = ((COLLADAFW::Scale *)tm)->getScale(); + float size[3] = {(float)s[0], (float)s[1], (float)s[2]}; + size_to_mat4(m, size); +} + +void TransformReader::dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]) +{ + unit_converter->dae_matrix_to_mat4_(m, ((COLLADAFW::Matrix *)tm)->getMatrix()); +} + +void TransformReader::dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]) +{ + dae_vector3_to_v3(((COLLADAFW::Translate *)tm)->getTranslation(), v); +} + +void TransformReader::dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]) +{ + dae_vector3_to_v3(((COLLADAFW::Scale *)tm)->getScale(), v); +} + +void TransformReader::dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]) +{ + v[0] = v3.x; + v[1] = v3.y; + v[2] = v3.z; +} diff --git a/source/blender/io/collada/TransformReader.h b/source/blender/io/collada/TransformReader.h new file mode 100644 index 00000000000..2cf3ee795ae --- /dev/null +++ b/source/blender/io/collada/TransformReader.h @@ -0,0 +1,72 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __TRANSFORMREADER_H__ +#define __TRANSFORMREADER_H__ + +#include "COLLADAFWNode.h" +#include "COLLADAFWTransformation.h" +#include "COLLADAFWTranslate.h" +#include "COLLADAFWRotate.h" +#include "COLLADAFWScale.h" +#include "COLLADAFWMatrix.h" +#include "COLLADAFWUniqueId.h" +#include "Math/COLLADABUMathVector3.h" + +#include "DNA_object_types.h" +#include "BLI_math.h" + +#include "collada_internal.h" + +// struct Object; + +class TransformReader { + protected: + UnitConverter *unit_converter; + + public: + struct Animation { + Object *ob; + COLLADAFW::Node *node; + COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id + }; + + TransformReader(UnitConverter *conv); + + void get_node_mat(float mat[4][4], + COLLADAFW::Node *node, + std::map *animation_map, + Object *ob); + void get_node_mat(float mat[4][4], + COLLADAFW::Node *node, + std::map *animation_map, + Object *ob, + float parent_mat[4][4]); + + void dae_rotate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); + void dae_translate_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); + void dae_scale_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); + void dae_matrix_to_mat4(COLLADAFW::Transformation *tm, float m[4][4]); + void dae_translate_to_v3(COLLADAFW::Transformation *tm, float v[3]); + void dae_scale_to_v3(COLLADAFW::Transformation *tm, float v[3]); + void dae_vector3_to_v3(const COLLADABU::Math::Vector3 &v3, float v[3]); +}; + +#endif diff --git a/source/blender/io/collada/TransformWriter.cpp b/source/blender/io/collada/TransformWriter.cpp new file mode 100644 index 00000000000..0a66db72cb9 --- /dev/null +++ b/source/blender/io/collada/TransformWriter.cpp @@ -0,0 +1,141 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#include "BLI_math.h" +#include "BLI_sys_types.h" + +#include "BKE_object.h" + +#include "TransformWriter.h" + +void TransformWriter::add_joint_transform(COLLADASW::Node &node, + float mat[4][4], + float parent_mat[4][4], + BCExportSettings &export_settings, + bool has_restmat) +{ + float local[4][4]; + + if (parent_mat) { + float invpar[4][4]; + invert_m4_m4(invpar, parent_mat); + mul_m4_m4m4(local, invpar, mat); + } + else { + copy_m4_m4(local, mat); + } + + if (!has_restmat && export_settings.get_apply_global_orientation()) { + bc_apply_global_transform(local, export_settings.get_global_transform()); + } + + double dmat[4][4]; + UnitConverter *converter = new UnitConverter(); + converter->mat4_to_dae_double(dmat, local); + delete converter; + + if (export_settings.get_object_transformation_type() == BC_TRANSFORMATION_TYPE_MATRIX) { + node.addMatrix("transform", dmat); + } + else { + float loc[3], rot[3], scale[3]; + bc_decompose(local, loc, rot, NULL, scale); + add_transform(node, loc, rot, scale); + } +} + +void TransformWriter::add_node_transform_ob(COLLADASW::Node &node, + Object *ob, + BCExportSettings &export_settings) +{ + bool limit_precision = export_settings.get_limit_precision(); + + /* Export the local Matrix (relative to the object parent, + * be it an object, bone or vertex(-tices)). */ + Matrix f_obmat; + BKE_object_matrix_local_get(ob, f_obmat); + + if (export_settings.get_apply_global_orientation()) { + bc_apply_global_transform(f_obmat, export_settings.get_global_transform()); + } + else { + bc_add_global_transform(f_obmat, export_settings.get_global_transform()); + } + + switch (export_settings.get_object_transformation_type()) { + case BC_TRANSFORMATION_TYPE_MATRIX: { + UnitConverter converter; + double d_obmat[4][4]; + converter.mat4_to_dae_double(d_obmat, f_obmat); + + if (limit_precision) { + BCMatrix::sanitize(d_obmat, LIMITTED_PRECISION); + } + node.addMatrix("transform", d_obmat); + break; + } + case BC_TRANSFORMATION_TYPE_DECOMPOSED: { + float loc[3], rot[3], scale[3]; + bc_decompose(f_obmat, loc, rot, NULL, scale); + if (limit_precision) { + bc_sanitize_v3(loc, LIMITTED_PRECISION); + bc_sanitize_v3(rot, LIMITTED_PRECISION); + bc_sanitize_v3(scale, LIMITTED_PRECISION); + } + add_transform(node, loc, rot, scale); + break; + } + } +} + +void TransformWriter::add_node_transform_identity(COLLADASW::Node &node, + BCExportSettings &export_settings) +{ + BC_export_transformation_type transformation_type = + export_settings.get_object_transformation_type(); + switch (transformation_type) { + case BC_TRANSFORMATION_TYPE_MATRIX: { + BCMatrix mat; + DMatrix d_obmat; + mat.get_matrix(d_obmat); + node.addMatrix("transform", d_obmat); + break; + } + default: { + float loc[3] = {0.0f, 0.0f, 0.0f}; + float scale[3] = {1.0f, 1.0f, 1.0f}; + float rot[3] = {0.0f, 0.0f, 0.0f}; + add_transform(node, loc, rot, scale); + break; + } + } +} + +void TransformWriter::add_transform(COLLADASW::Node &node, + float loc[3], + float rot[3], + float scale[3]) +{ + node.addScale("scale", scale[0], scale[1], scale[2]); + node.addRotateZ("rotationZ", RAD2DEGF(rot[2])); + node.addRotateY("rotationY", RAD2DEGF(rot[1])); + node.addRotateX("rotationX", RAD2DEGF(rot[0])); + node.addTranslate("location", loc[0], loc[1], loc[2]); +} diff --git a/source/blender/io/collada/TransformWriter.h b/source/blender/io/collada/TransformWriter.h new file mode 100644 index 00000000000..d2e4b369cdc --- /dev/null +++ b/source/blender/io/collada/TransformWriter.h @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __TRANSFORMWRITER_H__ +#define __TRANSFORMWRITER_H__ + +#include "COLLADASWNode.h" + +#include "DNA_object_types.h" + +#include "collada_internal.h" +#include "collada_utils.h" +#include "collada.h" + +class TransformWriter { + protected: + void add_joint_transform(COLLADASW::Node &node, + float mat[4][4], + float parent_mat[4][4], + BCExportSettings &export_settings, + bool has_restmat); + + void add_node_transform_ob(COLLADASW::Node &node, Object *ob, BCExportSettings &export_settings); + + void add_node_transform_identity(COLLADASW::Node &node, BCExportSettings &export_settings); + + private: + void add_transform(COLLADASW::Node &node, float loc[3], float rot[3], float scale[3]); +}; + +#endif diff --git a/source/blender/io/collada/collada.cpp b/source/blender/io/collada/collada.cpp new file mode 100644 index 00000000000..ea5600aa850 --- /dev/null +++ b/source/blender/io/collada/collada.cpp @@ -0,0 +1,117 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "DocumentExporter.h" +#include "DocumentImporter.h" +#include "ExportSettings.h" +#include "ImportSettings.h" +#include "collada.h" + +extern "C" { +#include "BKE_scene.h" +#include "BKE_context.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +/* make dummy file */ +#include "BLI_fileops.h" +#include "BLI_linklist.h" + +static void print_import_header(ImportSettings &import_settings) +{ + fprintf(stderr, "+-- Collada Import parameters------\n"); + fprintf(stderr, "| input file : %s\n", import_settings.filepath); + fprintf(stderr, "| use units : %s\n", (import_settings.import_units) ? "yes" : "no"); + fprintf(stderr, "| autoconnect : %s\n", (import_settings.auto_connect) ? "yes" : "no"); + fprintf(stderr, "+-- Armature Import parameters ----\n"); + fprintf(stderr, "| find bone chains: %s\n", (import_settings.find_chains) ? "yes" : "no"); + fprintf(stderr, "| min chain len : %d\n", import_settings.min_chain_length); + fprintf(stderr, "| fix orientation : %s\n", (import_settings.fix_orientation) ? "yes" : "no"); + fprintf(stderr, "| keep bind info : %s\n", (import_settings.keep_bind_info) ? "yes" : "no"); +} + +static void print_import_footer(int status) +{ + fprintf(stderr, "+----------------------------------\n"); + fprintf(stderr, "| Collada Import : %s\n", (status) ? "OK" : "FAIL"); + fprintf(stderr, "+----------------------------------\n"); +} + +int collada_import(bContext *C, ImportSettings *import_settings) +{ + print_import_header(*import_settings); + DocumentImporter imp(C, import_settings); + int status = imp.import() ? 1 : 0; + print_import_footer(status); + + return status; +} + +int collada_export(bContext *C, ExportSettings *export_settings) +{ + BlenderContext blender_context(C); + ViewLayer *view_layer = blender_context.get_view_layer(); + + int includeFilter = OB_REL_NONE; + if (export_settings->include_armatures) { + includeFilter |= OB_REL_MOD_ARMATURE; + } + if (export_settings->include_children) { + includeFilter |= OB_REL_CHILDREN_RECURSIVE; + } + + /* Fetch the complete set of exported objects + * ATTENTION: Invisible objects will not be exported + */ + eObjectSet objectSet = (export_settings->selected) ? OB_SET_SELECTED : OB_SET_ALL; + export_settings->export_set = BKE_object_relational_superset( + view_layer, objectSet, (eObRelationTypes)includeFilter); + + int export_count = BLI_linklist_count(export_settings->export_set); + + if (export_count == 0) { + if (export_settings->selected) { + fprintf(stderr, + "Collada: Found no objects to export.\nPlease ensure that all objects which shall " + "be exported are also visible in the 3D Viewport.\n"); + } + else { + fprintf(stderr, "Collada: Your scene seems to be empty. No Objects will be exported.\n"); + } + } + else { + if (export_settings->sort_by_name) { + bc_bubble_sort_by_Object_name(export_settings->export_set); + } + } + + DocumentExporter exporter(blender_context, export_settings); + int status = exporter.exportCurrentScene(); + + BLI_linklist_free(export_settings->export_set, NULL); + + return (status) ? -1 : export_count; +} + +/* end extern C */ +} diff --git a/source/blender/io/collada/collada.h b/source/blender/io/collada/collada.h new file mode 100644 index 00000000000..72753e170a3 --- /dev/null +++ b/source/blender/io/collada/collada.h @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __COLLADA_H__ +#define __COLLADA_H__ + +#include + +#include "ImportSettings.h" +#include "ExportSettings.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLI_linklist.h" +#include "BLI_path_util.h" +#include "RNA_types.h" + +struct bContext; + +/* + * both return 1 on success, 0 on error + */ +int collada_import(struct bContext *C, ImportSettings *import_settings); + +int collada_export(struct bContext *C, ExportSettings *export_settings); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/blender/io/collada/collada_internal.cpp b/source/blender/io/collada/collada_internal.cpp new file mode 100644 index 00000000000..7e834045795 --- /dev/null +++ b/source/blender/io/collada/collada_internal.cpp @@ -0,0 +1,340 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" +#include "collada_utils.h" + +#include "BLI_linklist.h" +#include "ED_armature.h" + +UnitConverter::UnitConverter() : unit(), up_axis(COLLADAFW::FileInfo::Z_UP) +{ + axis_angle_to_mat4_single(x_up_mat4, 'Y', -0.5 * M_PI); + axis_angle_to_mat4_single(y_up_mat4, 'X', 0.5 * M_PI); + + unit_m4(z_up_mat4); + unit_m4(scale_mat4); +} + +void UnitConverter::read_asset(const COLLADAFW::FileInfo *asset) +{ + unit = asset->getUnit(); + up_axis = asset->getUpAxisType(); +} + +UnitConverter::UnitSystem UnitConverter::isMetricSystem() +{ + switch (unit.getLinearUnitUnit()) { + case COLLADAFW::FileInfo::Unit::MILLIMETER: + case COLLADAFW::FileInfo::Unit::CENTIMETER: + case COLLADAFW::FileInfo::Unit::DECIMETER: + case COLLADAFW::FileInfo::Unit::METER: + case COLLADAFW::FileInfo::Unit::KILOMETER: + return UnitConverter::Metric; + case COLLADAFW::FileInfo::Unit::INCH: + case COLLADAFW::FileInfo::Unit::FOOT: + case COLLADAFW::FileInfo::Unit::YARD: + return UnitConverter::Imperial; + default: + return UnitConverter::None; + } +} + +float UnitConverter::getLinearMeter() +{ + return (float)unit.getLinearUnitMeter(); +} + +void UnitConverter::convertVector3(COLLADABU::Math::Vector3 &vec, float *v) +{ + v[0] = vec.x; + v[1] = vec.y; + v[2] = vec.z; +} + +// TODO need also for angle conversion, time conversion... + +void UnitConverter::dae_matrix_to_mat4_(float out[4][4], const COLLADABU::Math::Matrix4 &in) +{ + // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h) + // so here, to make a blender matrix, we swap columns and rows + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + out[i][j] = in[j][i]; + } + } +} + +void UnitConverter::mat4_to_dae(float out[4][4], float in[4][4]) +{ + transpose_m4_m4(out, in); +} + +void UnitConverter::mat4_to_dae_double(double out[4][4], float in[4][4]) +{ + float mat[4][4]; + + mat4_to_dae(mat, in); + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + out[i][j] = mat[i][j]; + } + } +} + +float (&UnitConverter::get_rotation())[4][4] +{ + switch (up_axis) { + case COLLADAFW::FileInfo::X_UP: + return x_up_mat4; + break; + case COLLADAFW::FileInfo::Y_UP: + return y_up_mat4; + break; + default: + return z_up_mat4; + break; + } +} + +float (&UnitConverter::get_scale())[4][4] +{ + return scale_mat4; +} + +void UnitConverter::calculate_scale(Scene &sce) +{ + PointerRNA scene_ptr, unit_settings; + PropertyRNA *system_ptr, *scale_ptr; + RNA_id_pointer_create(&sce.id, &scene_ptr); + + unit_settings = RNA_pointer_get(&scene_ptr, "unit_settings"); + system_ptr = RNA_struct_find_property(&unit_settings, "system"); + scale_ptr = RNA_struct_find_property(&unit_settings, "scale_length"); + + int type = RNA_property_enum_get(&unit_settings, system_ptr); + + float bl_scale; + + switch (type) { + case USER_UNIT_NONE: + bl_scale = 1.0; // map 1 Blender unit to 1 Meter + break; + + case USER_UNIT_METRIC: + bl_scale = RNA_property_float_get(&unit_settings, scale_ptr); + break; + + default: + bl_scale = RNA_property_float_get(&unit_settings, scale_ptr); + // it looks like the conversion to Imperial is done implicitly. + // So nothing to do here. + break; + } + + float rescale[3]; + rescale[0] = rescale[1] = rescale[2] = getLinearMeter() / bl_scale; + + size_to_mat4(scale_mat4, rescale); +} + +/** + * Translation map. + * Used to translate every COLLADA id to a valid id, no matter what "wrong" letters may be + * included. Look at the IDREF XSD declaration for more. + * Follows strictly the COLLADA XSD declaration which explicitly allows non-english chars, + * like special chars (e.g. micro sign), umlauts and so on. + * The COLLADA spec also allows additional chars for member access ('.'), these + * must obviously be removed too, otherwise they would be heavily misinterpreted. + */ +const unsigned char translate_start_name_map[256] = { + + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 95, 95, 95, + 95, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 95, 95, 95, 95, 95, + + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, +}; + +const unsigned char translate_name_map[256] = { + + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 45, 95, 95, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 57, 95, 95, 95, 95, 95, 95, 95, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 95, 95, 95, 95, + 95, 95, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, + 114, 115, 116, 117, 118, 119, 120, 121, 122, 95, 95, 95, 95, 95, + + 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, + 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, + 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, + 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, + 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, + 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, +}; + +typedef std::map> map_string_list; +map_string_list global_id_map; + +void clear_global_id_map() +{ + global_id_map.clear(); +} + +/** Look at documentation of translate_map */ +std::string translate_id(const char *idString) +{ + std::string id = std::string(idString); + return translate_id(id); +} + +std::string translate_id(const std::string &id) +{ + if (id.size() == 0) { + return id; + } + + std::string id_translated = id; + id_translated[0] = translate_start_name_map[(unsigned int)id_translated[0]]; + for (unsigned int i = 1; i < id_translated.size(); i++) { + id_translated[i] = translate_name_map[(unsigned int)id_translated[i]]; + } + // It's so much workload now, the if () should speed up things. + if (id_translated != id) { + // Search duplicates + map_string_list::iterator iter = global_id_map.find(id_translated); + if (iter != global_id_map.end()) { + unsigned int i = 0; + bool found = false; + for (i = 0; i < iter->second.size(); i++) { + if (id == iter->second[i]) { + found = true; + break; + } + } + bool convert = false; + if (found) { + if (i > 0) { + convert = true; + } + } + else { + convert = true; + global_id_map[id_translated].push_back(id); + } + if (convert) { + std::stringstream out; + out << ++i; + id_translated += out.str(); + } + } + else { + global_id_map[id_translated].push_back(id); + } + } + return id_translated; +} + +std::string id_name(void *id) +{ + return ((ID *)id)->name + 2; +} + +std::string encode_xml(std::string xml) +{ + const std::map escape{ + {'<', "<"}, {'>', ">"}, {'"', """}, {'\'', "'"}, {'&', "&"}}; + + std::map::const_iterator it; + std::string encoded_xml = ""; + + for (unsigned int i = 0; i < xml.size(); i++) { + char c = xml.at(i); + it = escape.find(c); + + if (it == escape.end()) { + encoded_xml += c; + } + else { + encoded_xml += it->second; + } + } + return encoded_xml; +} + +std::string get_geometry_id(Object *ob) +{ + return translate_id(id_name(ob->data)) + "-mesh"; +} + +std::string get_geometry_id(Object *ob, bool use_instantiation) +{ + std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob); + + return translate_id(geom_name) + "-mesh"; +} + +std::string get_light_id(Object *ob) +{ + return translate_id(id_name(ob)) + "-light"; +} + +std::string get_joint_sid(Bone *bone) +{ + return translate_id(bone->name); +} +static std::string get_joint_sid(EditBone *bone) +{ + return translate_id(bone->name); +} + +std::string get_camera_id(Object *ob) +{ + return translate_id(id_name(ob)) + "-camera"; +} + +std::string get_effect_id(Material *mat) +{ + return translate_id(id_name(mat)) + "-effect"; +} + +std::string get_material_id(Material *mat) +{ + return translate_id(id_name(mat)) + "-material"; +} + +std::string get_morph_id(Object *ob) +{ + return translate_id(id_name(ob)) + "-morph"; +} diff --git a/source/blender/io/collada/collada_internal.h b/source/blender/io/collada/collada_internal.h new file mode 100644 index 00000000000..297ea9c0bbb --- /dev/null +++ b/source/blender/io/collada/collada_internal.h @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __COLLADA_INTERNAL_H__ +#define __COLLADA_INTERNAL_H__ + +#include +#include +#include + +#include "COLLADAFWFileInfo.h" +#include "Math/COLLADABUMathMatrix4.h" + +#include "DNA_armature_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "BLI_math.h" +#include "BLI_linklist.h" + +class UnitConverter { + private: + COLLADAFW::FileInfo::Unit unit; + COLLADAFW::FileInfo::UpAxisType up_axis; + + float x_up_mat4[4][4]; + float y_up_mat4[4][4]; + float z_up_mat4[4][4]; + float scale_mat4[4][4]; + + public: + enum UnitSystem { + None, + Metric, + Imperial, + }; + + // Initialize with Z_UP, since Blender uses right-handed, z-up + UnitConverter(); + + void read_asset(const COLLADAFW::FileInfo *asset); + + void convertVector3(COLLADABU::Math::Vector3 &vec, float *v); + + UnitConverter::UnitSystem isMetricSystem(void); + + float getLinearMeter(void); + + // TODO need also for angle conversion, time conversion... + + static void dae_matrix_to_mat4_(float out[4][4], const COLLADABU::Math::Matrix4 &in); + static void mat4_to_dae(float out[4][4], float in[4][4]); + static void mat4_to_dae_double(double out[4][4], float in[4][4]); + + float (&get_rotation())[4][4]; + float (&get_scale())[4][4]; + void calculate_scale(Scene &sce); +}; + +extern void clear_global_id_map(); +/** Look at documentation of translate_map */ +extern std::string translate_id(const std::string &id); +extern std::string translate_id(const char *idString); + +extern std::string id_name(void *id); +extern std::string encode_xml(std::string xml); + +extern std::string get_geometry_id(Object *ob); +extern std::string get_geometry_id(Object *ob, bool use_instantiation); + +extern std::string get_light_id(Object *ob); + +extern std::string get_joint_sid(Bone *bone); + +extern std::string get_camera_id(Object *ob); +extern std::string get_morph_id(Object *ob); + +extern std::string get_effect_id(Material *mat); +extern std::string get_material_id(Material *mat); + +#endif /* __COLLADA_INTERNAL_H__ */ diff --git a/source/blender/io/collada/collada_utils.cpp b/source/blender/io/collada/collada_utils.cpp new file mode 100644 index 00000000000..26b392af0a1 --- /dev/null +++ b/source/blender/io/collada/collada_utils.cpp @@ -0,0 +1,1458 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +/* COLLADABU_ASSERT, may be able to remove later */ +#include "COLLADABUPlatform.h" + +#include "COLLADAFWGeometry.h" +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWMeshVertexData.h" + +#include +#include + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DNA_modifier_types.h" +#include "DNA_customdata_types.h" +#include "DNA_key_types.h" +#include "DNA_object_types.h" +#include "DNA_constraint_types.h" +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" +#include "DNA_armature_types.h" + +#include "BLI_math.h" +#include "BLI_linklist.h" +#include "BLI_listbase.h" + +#include "BKE_action.h" +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_constraint.h" +#include "BKE_key.h" +#include "BKE_material.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_global.h" +#include "BKE_layer.h" +#include "BKE_lib_id.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_object.h" +#include "BKE_scene.h" + +#include "ED_armature.h" +#include "ED_screen.h" +#include "ED_node.h" +#include "ED_object.h" + +#include "WM_api.h" /* XXX hrm, see if we can do without this */ +#include "WM_types.h" + +#include "bmesh.h" +#include "bmesh_tools.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" +#if 0 +# include "NOD_common.h" +#endif +} + +#include "collada_utils.h" +#include "ExportSettings.h" +#include "BlenderContext.h" + +float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray &array, unsigned int index) +{ + if (index >= array.getValuesCount()) { + return 0.0f; + } + + if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) { + return array.getFloatValues()->getData()[index]; + } + else { + return array.getDoubleValues()->getData()[index]; + } +} + +/* copied from /editors/object/object_relations.c */ +int bc_test_parent_loop(Object *par, Object *ob) +{ + /* test if 'ob' is a parent somewhere in par's parents */ + + if (par == NULL) { + return 0; + } + if (ob == par) { + return 1; + } + + return bc_test_parent_loop(par->parent, ob); +} + +bool bc_validateConstraints(bConstraint *con) +{ + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); + + /* these we can skip completely (invalid constraints...) */ + if (cti == NULL) { + return false; + } + if (con->flag & (CONSTRAINT_DISABLE | CONSTRAINT_OFF)) { + return false; + } + + /* these constraints can't be evaluated anyway */ + if (cti->evaluate_constraint == NULL) { + return false; + } + + /* influence == 0 should be ignored */ + if (con->enforce == 0.0f) { + return false; + } + + /* validation passed */ + return true; +} + +bool bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space) +{ + Scene *scene = CTX_data_scene(C); + int partype = PAR_OBJECT; + const bool xmirror = false; + const bool keep_transform = false; + + if (par && is_parent_space) { + mul_m4_m4m4(ob->obmat, par->obmat, ob->obmat); + } + + bool ok = ED_object_parent_set(NULL, C, scene, ob, par, partype, xmirror, keep_transform, NULL); + return ok; +} + +std::vector bc_getSceneActions(const bContext *C, Object *ob, bool all_actions) +{ + std::vector actions; + if (all_actions) { + Main *bmain = CTX_data_main(C); + ID *id; + + for (id = (ID *)bmain->actions.first; id; id = (ID *)(id->next)) { + bAction *act = (bAction *)id; + /* XXX This currently creates too many actions. + * TODO Need to check if the action is compatible to the given object. */ + actions.push_back(act); + } + } + else { + bAction *action = bc_getSceneObjectAction(ob); + actions.push_back(action); + } + + return actions; +} + +std::string bc_get_action_id(std::string action_name, + std::string ob_name, + std::string channel_type, + std::string axis_name, + std::string axis_separator) +{ + std::string result = action_name + "_" + channel_type; + if (ob_name.length() > 0) { + result = ob_name + "_" + result; + } + if (axis_name.length() > 0) { + result += axis_separator + axis_name; + } + return translate_id(result); +} + +void bc_update_scene(BlenderContext &blender_context, float ctime) +{ + Main *bmain = blender_context.get_main(); + Scene *scene = blender_context.get_scene(); + Depsgraph *depsgraph = blender_context.get_depsgraph(); + + /* See remark in physics_fluid.c lines 395...) */ + // BKE_scene_update_for_newframe(ev_context, bmain, scene, scene->lay); + BKE_scene_frame_set(scene, ctime); + ED_update_for_newframe(bmain, depsgraph); +} + +Object *bc_add_object(Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name) +{ + Object *ob = BKE_object_add_only_object(bmain, type, name); + + ob->data = BKE_object_obdata_add_from_type(bmain, type, name); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); + + LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); + BKE_collection_object_add(bmain, layer_collection->collection, ob); + + Base *base = BKE_view_layer_base_find(view_layer, ob); + /* TODO: is setting active needed? */ + BKE_view_layer_base_select_and_set_active(view_layer, base); + + return ob; +} + +Mesh *bc_get_mesh_copy(BlenderContext &blender_context, + Object *ob, + BC_export_mesh_type export_mesh_type, + bool apply_modifiers, + bool triangulate) +{ + CustomData_MeshMasks mask = CD_MASK_MESH; + Mesh *tmpmesh = NULL; + if (apply_modifiers) { +#if 0 /* Not supported by new system currently... */ + switch (export_mesh_type) { + case BC_MESH_TYPE_VIEW: { + dm = mesh_create_derived_view(depsgraph, scene, ob, &mask); + break; + } + case BC_MESH_TYPE_RENDER: { + dm = mesh_create_derived_render(depsgraph, scene, ob, &mask); + break; + } + } +#else + Depsgraph *depsgraph = blender_context.get_depsgraph(); + Scene *scene_eval = blender_context.get_evaluated_scene(); + Object *ob_eval = blender_context.get_evaluated_object(ob); + tmpmesh = mesh_get_eval_final(depsgraph, scene_eval, ob_eval, &mask); +#endif + } + else { + tmpmesh = (Mesh *)ob->data; + } + + BKE_id_copy_ex(NULL, &tmpmesh->id, (ID **)&tmpmesh, LIB_ID_COPY_LOCALIZE); + + if (triangulate) { + bc_triangulate_mesh(tmpmesh); + } + BKE_mesh_tessface_ensure(tmpmesh); + return tmpmesh; +} + +Object *bc_get_assigned_armature(Object *ob) +{ + Object *ob_arm = NULL; + + if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) { + ob_arm = ob->parent; + } + else { + ModifierData *mod; + for (mod = (ModifierData *)ob->modifiers.first; mod; mod = mod->next) { + if (mod->type == eModifierType_Armature) { + ob_arm = ((ArmatureModifierData *)mod)->object; + } + } + } + + return ob_arm; +} + +bool bc_has_object_type(LinkNode *export_set, short obtype) +{ + LinkNode *node; + + for (node = export_set; node; node = node->next) { + Object *ob = (Object *)node->link; + /* XXX - why is this checking for ob->data? - we could be looking for empties */ + if (ob->type == obtype && ob->data) { + return true; + } + } + return false; +} + +/* Use bubble sort algorithm for sorting the export set */ +void bc_bubble_sort_by_Object_name(LinkNode *export_set) +{ + bool sorted = false; + LinkNode *node; + for (node = export_set; node->next && !sorted; node = node->next) { + + sorted = true; + + LinkNode *current; + for (current = export_set; current->next; current = current->next) { + Object *a = (Object *)current->link; + Object *b = (Object *)current->next->link; + + if (strcmp(a->id.name, b->id.name) > 0) { + current->link = b; + current->next->link = a; + sorted = false; + } + } + } +} + +/* Check if a bone is the top most exportable bone in the bone hierarchy. + * When deform_bones_only == false, then only bones with NO parent + * can be root bones. Otherwise the top most deform bones in the hierarchy + * are root bones. + */ +bool bc_is_root_bone(Bone *aBone, bool deform_bones_only) +{ + if (deform_bones_only) { + Bone *root = NULL; + Bone *bone = aBone; + while (bone) { + if (!(bone->flag & BONE_NO_DEFORM)) { + root = bone; + } + bone = bone->parent; + } + return (aBone == root); + } + else { + return !(aBone->parent); + } +} + +int bc_get_active_UVLayer(Object *ob) +{ + Mesh *me = (Mesh *)ob->data; + return CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV); +} + +std::string bc_url_encode(std::string data) +{ + /* XXX We probably do not need to do a full encoding. + * But in case that is necessary,then it can be added here. + */ + return bc_replace_string(data, "#", "%23"); +} + +std::string bc_replace_string(std::string data, + const std::string &pattern, + const std::string &replacement) +{ + size_t pos = 0; + while ((pos = data.find(pattern, pos)) != std::string::npos) { + data.replace(pos, pattern.length(), replacement); + pos += replacement.length(); + } + return data; +} + +/** + * Calculate a rescale factor such that the imported scene's scale + * is preserved. I.e. 1 meter in the import will also be + * 1 meter in the current scene. + */ + +void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene) +{ + if (scale_to_scene) { + mul_m4_m4m4(ob->obmat, bc_unit.get_scale(), ob->obmat); + } + mul_m4_m4m4(ob->obmat, bc_unit.get_rotation(), ob->obmat); + BKE_object_apply_mat4(ob, ob->obmat, 0, 0); +} + +void bc_match_scale(std::vector *objects_done, + UnitConverter &bc_unit, + bool scale_to_scene) +{ + for (std::vector::iterator it = objects_done->begin(); it != objects_done->end(); + ++it) { + Object *ob = *it; + if (ob->parent == NULL) { + bc_match_scale(*it, bc_unit, scale_to_scene); + } + } +} + +/* + * Convenience function to get only the needed components of a matrix + */ +void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size) +{ + if (size) { + mat4_to_size(size, mat); + } + + if (eul) { + mat4_to_eul(eul, mat); + } + + if (quat) { + mat4_to_quat(quat, mat); + } + + if (loc) { + copy_v3_v3(loc, mat[3]); + } +} + +/* + * Create rotation_quaternion from a delta rotation and a reference quat + * + * Input: + * mat_from: The rotation matrix before rotation + * mat_to : The rotation matrix after rotation + * qref : the quat corresponding to mat_from + * + * Output: + * rot : the calculated result (quaternion) + */ +void bc_rotate_from_reference_quat(float quat_to[4], float quat_from[4], float mat_to[4][4]) +{ + float qd[4]; + float matd[4][4]; + float mati[4][4]; + float mat_from[4][4]; + quat_to_mat4(mat_from, quat_from); + + /* Calculate the difference matrix matd between mat_from and mat_to */ + invert_m4_m4(mati, mat_from); + mul_m4_m4m4(matd, mati, mat_to); + + mat4_to_quat(qd, matd); + + mul_qt_qtqt(quat_to, qd, quat_from); /* rot is the final rotation corresponding to mat_to */ +} + +void bc_triangulate_mesh(Mesh *me) +{ + bool use_beauty = false; + bool tag_only = false; + + /* XXX: The triangulation method selection could be offered in the UI. */ + int quad_method = MOD_TRIANGULATE_QUAD_SHORTEDGE; + + const struct BMeshCreateParams bm_create_params = {0}; + BMesh *bm = BM_mesh_create(&bm_mesh_allocsize_default, &bm_create_params); + BMeshFromMeshParams bm_from_me_params = {0}; + bm_from_me_params.calc_face_normal = true; + BM_mesh_bm_from_me(bm, me, &bm_from_me_params); + BM_mesh_triangulate(bm, quad_method, use_beauty, 4, tag_only, NULL, NULL, NULL); + + BMeshToMeshParams bm_to_me_params = {0}; + bm_to_me_params.calc_object_remap = false; + BM_mesh_bm_to_me(NULL, bm, me, &bm_to_me_params); + BM_mesh_free(bm); +} + +/* + * A bone is a leaf when it has no children or all children are not connected. + */ +bool bc_is_leaf_bone(Bone *bone) +{ + for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) { + if (child->flag & BONE_CONNECTED) { + return false; + } + } + return true; +} + +EditBone *bc_get_edit_bone(bArmature *armature, char *name) +{ + EditBone *eBone; + + for (eBone = (EditBone *)armature->edbo->first; eBone; eBone = eBone->next) { + if (STREQ(name, eBone->name)) { + return eBone; + } + } + + return NULL; +} +int bc_set_layer(int bitfield, int layer) +{ + return bc_set_layer(bitfield, layer, true); /* enable */ +} + +int bc_set_layer(int bitfield, int layer, bool enable) +{ + int bit = 1u << layer; + + if (enable) { + bitfield |= bit; + } + else { + bitfield &= ~bit; + } + + return bitfield; +} + +/** + * This method creates a new extension map when needed. + * \note The ~BoneExtensionManager destructor takes care + * to delete the created maps when the manager is removed. + */ +BoneExtensionMap &BoneExtensionManager::getExtensionMap(bArmature *armature) +{ + std::string key = armature->id.name; + BoneExtensionMap *result = extended_bone_maps[key]; + if (result == NULL) { + result = new BoneExtensionMap(); + extended_bone_maps[key] = result; + } + return *result; +} + +BoneExtensionManager::~BoneExtensionManager() +{ + std::map::iterator map_it; + for (map_it = extended_bone_maps.begin(); map_it != extended_bone_maps.end(); ++map_it) { + BoneExtensionMap *extended_bones = map_it->second; + for (BoneExtensionMap::iterator ext_it = extended_bones->begin(); + ext_it != extended_bones->end(); + ++ext_it) { + if (ext_it->second != NULL) { + delete ext_it->second; + } + } + extended_bones->clear(); + delete extended_bones; + } +} + +/** + * BoneExtended is a helper class needed for the Bone chain finder + * See ArmatureImporter::fix_leaf_bones() + * and ArmatureImporter::connect_bone_chains() + */ + +BoneExtended::BoneExtended(EditBone *aBone) +{ + this->set_name(aBone->name); + this->chain_length = 0; + this->is_leaf = false; + this->tail[0] = 0.0f; + this->tail[1] = 0.5f; + this->tail[2] = 0.0f; + this->use_connect = -1; + this->roll = 0; + this->bone_layers = 0; + + this->has_custom_tail = false; + this->has_custom_roll = false; +} + +char *BoneExtended::get_name() +{ + return name; +} + +void BoneExtended::set_name(char *aName) +{ + BLI_strncpy(name, aName, MAXBONENAME); +} + +int BoneExtended::get_chain_length() +{ + return chain_length; +} + +void BoneExtended::set_chain_length(const int aLength) +{ + chain_length = aLength; +} + +void BoneExtended::set_leaf_bone(bool state) +{ + is_leaf = state; +} + +bool BoneExtended::is_leaf_bone() +{ + return is_leaf; +} + +void BoneExtended::set_roll(float roll) +{ + this->roll = roll; + this->has_custom_roll = true; +} + +bool BoneExtended::has_roll() +{ + return this->has_custom_roll; +} + +float BoneExtended::get_roll() +{ + return this->roll; +} + +void BoneExtended::set_tail(float vec[]) +{ + this->tail[0] = vec[0]; + this->tail[1] = vec[1]; + this->tail[2] = vec[2]; + this->has_custom_tail = true; +} + +bool BoneExtended::has_tail() +{ + return this->has_custom_tail; +} + +float *BoneExtended::get_tail() +{ + return this->tail; +} + +inline bool isInteger(const std::string &s) +{ + if (s.empty() || ((!isdigit(s[0])) && (s[0] != '-') && (s[0] != '+'))) { + return false; + } + + char *p; + strtol(s.c_str(), &p, 10); + + return (*p == 0); +} + +void BoneExtended::set_bone_layers(std::string layerString, std::vector &layer_labels) +{ + std::stringstream ss(layerString); + std::string layer; + int pos; + + while (ss >> layer) { + + /* Blender uses numbers to specify layers*/ + if (isInteger(layer)) { + pos = atoi(layer.c_str()); + if (pos >= 0 && pos < 32) { + this->bone_layers = bc_set_layer(this->bone_layers, pos); + continue; + } + } + + /* layer uses labels (not supported by blender). Map to layer numbers:*/ + pos = find(layer_labels.begin(), layer_labels.end(), layer) - layer_labels.begin(); + if (pos >= layer_labels.size()) { + layer_labels.push_back(layer); /* remember layer number for future usage*/ + } + + if (pos > 31) { + fprintf(stderr, + "Too many layers in Import. Layer %s mapped to Blender layer 31\n", + layer.c_str()); + pos = 31; + } + + /* If numeric layers and labeled layers are used in parallel (unlikely), + * we get a potential mixup. Just leave as is for now. + */ + this->bone_layers = bc_set_layer(this->bone_layers, pos); + } +} + +std::string BoneExtended::get_bone_layers(int bitfield) +{ + std::string result = ""; + std::string sep = ""; + int bit = 1u; + + std::ostringstream ss; + for (int i = 0; i < 32; i++) { + if (bit & bitfield) { + ss << sep << i; + sep = " "; + } + bit = bit << 1; + } + return ss.str(); +} + +int BoneExtended::get_bone_layers() +{ + /* ensure that the bone is in at least one bone layer! */ + return (bone_layers == 0) ? 1 : bone_layers; +} + +void BoneExtended::set_use_connect(int use_connect) +{ + this->use_connect = use_connect; +} + +int BoneExtended::get_use_connect() +{ + return this->use_connect; +} + +/** + * Stores a 4*4 matrix as a custom bone property array of size 16 + */ +void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]) +{ + IDProperty *idgroup = (IDProperty *)ebone->prop; + if (idgroup == NULL) { + IDPropertyTemplate val = {0}; + idgroup = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); + ebone->prop = idgroup; + } + + IDPropertyTemplate val = {0}; + val.array.len = 16; + val.array.type = IDP_FLOAT; + + IDProperty *data = IDP_New(IDP_ARRAY, &val, key); + float *array = (float *)IDP_Array(data); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + array[4 * i + j] = mat[i][j]; + } + } + + IDP_AddToGroup(idgroup, data); +} + +#if 0 +/** + * Stores a Float value as a custom bone property + * + * Note: This function is currently not needed. Keep for future usage + */ +static void bc_set_IDProperty(EditBone *ebone, const char *key, float value) +{ + if (ebone->prop == NULL) { + IDPropertyTemplate val = {0}; + ebone->prop = IDP_New(IDP_GROUP, &val, "RNA_EditBone ID properties"); + } + + IDProperty *pgroup = (IDProperty *)ebone->prop; + IDPropertyTemplate val = {0}; + IDProperty *prop = IDP_New(IDP_FLOAT, &val, key); + IDP_Float(prop) = value; + IDP_AddToGroup(pgroup, prop); +} +#endif + +/** + * Get a custom property when it exists. + * This function is also used to check if a property exists. + */ +IDProperty *bc_get_IDProperty(Bone *bone, std::string key) +{ + return (bone->prop == NULL) ? NULL : IDP_GetPropertyFromGroup(bone->prop, key.c_str()); +} + +/** + * Read a custom bone property and convert to float + * Return def if the property does not exist. + */ +float bc_get_property(Bone *bone, std::string key, float def) +{ + float result = def; + IDProperty *property = bc_get_IDProperty(bone, key); + if (property) { + switch (property->type) { + case IDP_INT: + result = (float)(IDP_Int(property)); + break; + case IDP_FLOAT: + result = (float)(IDP_Float(property)); + break; + case IDP_DOUBLE: + result = (float)(IDP_Double(property)); + break; + default: + result = def; + } + } + return result; +} + +/** + * Read a custom bone property and convert to matrix + * Return true if conversion was successful + * + * Return false if: + * - the property does not exist + * - is not an array of size 16 + */ +bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]) +{ + IDProperty *property = bc_get_IDProperty(bone, key); + if (property && property->type == IDP_ARRAY && property->len == 16) { + float *array = (float *)IDP_Array(property); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + mat[i][j] = array[4 * i + j]; + } + } + return true; + } + return false; +} + +/** + * get a vector that is stored in 3 custom properties (used in Blender <= 2.78) + */ +void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]) +{ + val[0] = bc_get_property(bone, key + "_x", def[0]); + val[1] = bc_get_property(bone, key + "_y", def[1]); + val[2] = bc_get_property(bone, key + "_z", def[2]); +} + +/** + * Check if vector exist stored in 3 custom properties (used in Blender <= 2.78) + */ +static bool has_custom_props(Bone *bone, bool enabled, std::string key) +{ + if (!enabled) { + return false; + } + + return (bc_get_IDProperty(bone, key + "_x") || bc_get_IDProperty(bone, key + "_y") || + bc_get_IDProperty(bone, key + "_z")); +} + +void bc_enable_fcurves(bAction *act, char *bone_name) +{ + FCurve *fcu; + char prefix[200]; + + if (bone_name) { + BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name); + } + + for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) { + if (bone_name) { + if (STREQLEN(fcu->rna_path, prefix, strlen(prefix))) { + fcu->flag &= ~FCURVE_DISABLED; + } + else { + fcu->flag |= FCURVE_DISABLED; + } + } + else { + fcu->flag &= ~FCURVE_DISABLED; + } + } +} + +bool bc_bone_matrix_local_get(Object *ob, Bone *bone, Matrix &mat, bool for_opensim) +{ + + /* Ok, lets be super cautious and check if the bone exists */ + bPose *pose = ob->pose; + bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name); + if (!pchan) { + return false; + } + + bAction *action = bc_getSceneObjectAction(ob); + bPoseChannel *parchan = pchan->parent; + + bc_enable_fcurves(action, bone->name); + float ipar[4][4]; + + if (bone->parent) { + invert_m4_m4(ipar, parchan->pose_mat); + mul_m4_m4m4(mat, ipar, pchan->pose_mat); + } + else { + copy_m4_m4(mat, pchan->pose_mat); + } + + /* OPEN_SIM_COMPATIBILITY + * AFAIK animation to second life is via BVH, but no + * reason to not have the collada-animation be correct */ + if (for_opensim) { + float temp[4][4]; + copy_m4_m4(temp, bone->arm_mat); + temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; + invert_m4(temp); + + mul_m4_m4m4(mat, mat, temp); + + if (bone->parent) { + copy_m4_m4(temp, bone->parent->arm_mat); + temp[3][0] = temp[3][1] = temp[3][2] = 0.0f; + + mul_m4_m4m4(mat, temp, mat); + } + } + bc_enable_fcurves(action, NULL); + return true; +} + +bool bc_is_animated(BCMatrixSampleMap &values) +{ + static float MIN_DISTANCE = 0.00001; + + if (values.size() < 2) { + return false; /* need at least 2 entries to be not flat */ + } + + BCMatrixSampleMap::iterator it; + const BCMatrix *refmat = NULL; + for (it = values.begin(); it != values.end(); ++it) { + const BCMatrix *matrix = it->second; + + if (refmat == NULL) { + refmat = matrix; + continue; + } + + if (!matrix->in_range(*refmat, MIN_DISTANCE)) { + return true; + } + } + return false; +} + +bool bc_has_animations(Object *ob) +{ + /* Check for object, light and camera transform animations */ + if ((bc_getSceneObjectAction(ob) && bc_getSceneObjectAction(ob)->curves.first) || + (bc_getSceneLightAction(ob) && bc_getSceneLightAction(ob)->curves.first) || + (bc_getSceneCameraAction(ob) && bc_getSceneCameraAction(ob)->curves.first)) { + return true; + } + + /* Check Material Effect parameter animations. */ + for (int a = 0; a < ob->totcol; a++) { + Material *ma = BKE_object_material_get(ob, a + 1); + if (!ma) { + continue; + } + if (ma->adt && ma->adt->action && ma->adt->action->curves.first) { + return true; + } + } + + Key *key = BKE_key_from_object(ob); + if ((key && key->adt && key->adt->action) && key->adt->action->curves.first) { + return true; + } + + return false; +} + +bool bc_has_animations(Scene *sce, LinkNode *export_set) +{ + LinkNode *node; + if (export_set) { + for (node = export_set; node; node = node->next) { + Object *ob = (Object *)node->link; + + if (bc_has_animations(ob)) { + return true; + } + } + } + return false; +} + +void bc_add_global_transform(Matrix &to_mat, + const Matrix &from_mat, + const BCMatrix &global_transform, + const bool invert) +{ + copy_m4_m4(to_mat, from_mat); + bc_add_global_transform(to_mat, global_transform, invert); +} + +void bc_add_global_transform(Vector &to_vec, + const Vector &from_vec, + const BCMatrix &global_transform, + const bool invert) +{ + copy_v3_v3(to_vec, from_vec); + bc_add_global_transform(to_vec, global_transform, invert); +} + +void bc_add_global_transform(Matrix &to_mat, const BCMatrix &global_transform, const bool invert) +{ + BCMatrix mat(to_mat); + mat.add_transform(global_transform, invert); + mat.get_matrix(to_mat); +} + +void bc_add_global_transform(Vector &to_vec, const BCMatrix &global_transform, const bool invert) +{ + Matrix mat; + Vector from_vec; + copy_v3_v3(from_vec, to_vec); + global_transform.get_matrix(mat, false, 6, invert); + mul_v3_m4v3(to_vec, mat, from_vec); +} + +void bc_apply_global_transform(Matrix &to_mat, const BCMatrix &global_transform, const bool invert) +{ + BCMatrix mat(to_mat); + mat.apply_transform(global_transform, invert); + mat.get_matrix(to_mat); +} + +void bc_apply_global_transform(Vector &to_vec, const BCMatrix &global_transform, const bool invert) +{ + Matrix transform; + global_transform.get_matrix(transform); + mul_v3_m4v3(to_vec, transform, to_vec); +} + +/** + * Check if custom information about bind matrix exists and modify the from_mat + * accordingly. + * + * Note: This is old style for Blender <= 2.78 only kept for compatibility + */ +void bc_create_restpose_mat(BCExportSettings &export_settings, + Bone *bone, + float to_mat[4][4], + float from_mat[4][4], + bool use_local_space) +{ + float loc[3]; + float rot[3]; + float scale[3]; + static const float V0[3] = {0, 0, 0}; + + if (!has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_loc") && + !has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_rot") && + !has_custom_props(bone, export_settings.get_keep_bind_info(), "restpose_scale")) { + /* No need */ + copy_m4_m4(to_mat, from_mat); + return; + } + + bc_decompose(from_mat, loc, rot, NULL, scale); + loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); + + if (export_settings.get_keep_bind_info()) { + bc_get_property_vector(bone, "restpose_loc", loc, loc); + + if (use_local_space && bone->parent) { + Bone *b = bone; + while (b->parent) { + b = b->parent; + float ploc[3]; + bc_get_property_vector(b, "restpose_loc", ploc, V0); + loc[0] += ploc[0]; + loc[1] += ploc[1]; + loc[2] += ploc[2]; + } + } + } + + if (export_settings.get_keep_bind_info()) { + if (bc_get_IDProperty(bone, "restpose_rot_x")) { + rot[0] = DEG2RADF(bc_get_property(bone, "restpose_rot_x", 0)); + } + if (bc_get_IDProperty(bone, "restpose_rot_y")) { + rot[1] = DEG2RADF(bc_get_property(bone, "restpose_rot_y", 0)); + } + if (bc_get_IDProperty(bone, "restpose_rot_z")) { + rot[2] = DEG2RADF(bc_get_property(bone, "restpose_rot_z", 0)); + } + } + + if (export_settings.get_keep_bind_info()) { + bc_get_property_vector(bone, "restpose_scale", scale, scale); + } + + loc_eulO_size_to_mat4(to_mat, loc, rot, scale, 6); +} + +void bc_sanitize_v3(float v[3], int precision) +{ + for (int i = 0; i < 3; i++) { + double val = (double)v[i]; + val = double_round(val, precision); + v[i] = (float)val; + } +} + +void bc_sanitize_v3(double v[3], int precision) +{ + for (int i = 0; i < 3; i++) { + v[i] = double_round(v[i], precision); + } +} + +void bc_copy_m4_farray(float r[4][4], float *a) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r[i][j] = *a++; + } + } +} + +void bc_copy_farray_m4(float *r, float a[4][4]) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + *r++ = a[i][j]; + } + } +} + +void bc_copy_darray_m4d(double *r, double a[4][4]) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + *r++ = a[i][j]; + } + } +} + +void bc_copy_v44_m4d(std::vector> &r, double (&a)[4][4]) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r[i][j] = a[i][j]; + } + } +} + +void bc_copy_m4d_v44(double (&r)[4][4], std::vector> &a) +{ + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + r[i][j] = a[i][j]; + } + } +} + +/** + * Returns name of Active UV Layer or empty String if no active UV Layer defined + */ +static std::string bc_get_active_uvlayer_name(Mesh *me) +{ + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + if (num_layers) { + char *layer_name = bc_CustomData_get_active_layer_name(&me->ldata, CD_MLOOPUV); + if (layer_name) { + return std::string(layer_name); + } + } + return ""; +} + +/** + * Returns name of Active UV Layer or empty String if no active UV Layer defined. + * Assuming the Object is of type MESH + */ +static std::string bc_get_active_uvlayer_name(Object *ob) +{ + Mesh *me = (Mesh *)ob->data; + return bc_get_active_uvlayer_name(me); +} + +/** + * Returns UV Layer name or empty string if layer index is out of range + */ +static std::string bc_get_uvlayer_name(Mesh *me, int layer) +{ + int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV); + if (num_layers && layer < num_layers) { + char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, layer); + if (layer_name) { + return std::string(layer_name); + } + } + return ""; +} + +std::string bc_find_bonename_in_path(std::string path, std::string probe) +{ + std::string result; + char *boneName = BLI_str_quoted_substrN(path.c_str(), probe.c_str()); + if (boneName) { + result = std::string(boneName); + MEM_freeN(boneName); + } + return result; +} + +static bNodeTree *prepare_material_nodetree(Material *ma) +{ + if (ma->nodetree == NULL) { + ma->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree"); + ma->use_nodes = true; + } + return ma->nodetree; +} + +static bNode *bc_add_node( + bContext *C, bNodeTree *ntree, int node_type, int locx, int locy, std::string label) +{ + bNode *node = nodeAddStaticNode(C, ntree, node_type); + if (node) { + if (label.length() > 0) { + strcpy(node->label, label.c_str()); + } + node->locx = locx; + node->locy = locy; + node->flag |= NODE_SELECT; + } + return node; +} + +static bNode *bc_add_node(bContext *C, bNodeTree *ntree, int node_type, int locx, int locy) +{ + return bc_add_node(C, ntree, node_type, locx, locy, ""); +} + +#if 0 +/* experimental, probably not used */ +static bNodeSocket *bc_group_add_input_socket(bNodeTree *ntree, + bNode *to_node, + int to_index, + std::string label) +{ + bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); + + //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); + //return socket; + + bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); + bNode *inputGroup = ntreeFindType(ntree, NODE_GROUP_INPUT); + node_group_input_verify(ntree, inputGroup, (ID *)ntree); + bNodeSocket *newsock = node_group_input_find_socket(inputGroup, gsock->identifier); + nodeAddLink(ntree, inputGroup, newsock, to_node, to_socket); + strcpy(newsock->name, label.c_str()); + return newsock; +} + +static bNodeSocket *bc_group_add_output_socket(bNodeTree *ntree, + bNode *from_node, + int from_index, + std::string label) +{ + bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); + + //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket); + //return socket; + + bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, from_node, from_socket); + bNode *outputGroup = ntreeFindType(ntree, NODE_GROUP_OUTPUT); + node_group_output_verify(ntree, outputGroup, (ID *)ntree); + bNodeSocket *newsock = node_group_output_find_socket(outputGroup, gsock->identifier); + nodeAddLink(ntree, from_node, from_socket, outputGroup, newsock); + strcpy(newsock->name, label.c_str()); + return newsock; +} + +void bc_make_group(bContext *C, bNodeTree *ntree, std::map nmap) +{ + bNode *gnode = node_group_make_from_selected(C, ntree, "ShaderNodeGroup", "ShaderNodeTree"); + bNodeTree *gtree = (bNodeTree *)gnode->id; + + bc_group_add_input_socket(gtree, nmap["main"], 0, "Diffuse"); + bc_group_add_input_socket(gtree, nmap["emission"], 0, "Emission"); + bc_group_add_input_socket(gtree, nmap["mix"], 0, "Transparency"); + bc_group_add_input_socket(gtree, nmap["emission"], 1, "Emission"); + bc_group_add_input_socket(gtree, nmap["main"], 4, "Metallic"); + bc_group_add_input_socket(gtree, nmap["main"], 5, "Specular"); + + bc_group_add_output_socket(gtree, nmap["mix"], 0, "Shader"); +} +#endif + +static void bc_node_add_link( + bNodeTree *ntree, bNode *from_node, int from_index, bNode *to_node, int to_index) +{ + bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index); + bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index); + + nodeAddLink(ntree, from_node, from_socket, to_node, to_socket); +} + +void bc_add_default_shader(bContext *C, Material *ma) +{ + bNodeTree *ntree = prepare_material_nodetree(ma); + std::map nmap; +#if 0 + nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300); + nmap["emission"] = bc_add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission"); + nmap["add"] = bc_add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400); + nmap["transparent"] = bc_add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200); + nmap["mix"] = bc_add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency"); + nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300); + nmap["out"]->flag &= ~NODE_SELECT; + + bc_node_add_link(ntree, nmap["emission"], 0, nmap["add"], 0); + bc_node_add_link(ntree, nmap["main"], 0, nmap["add"], 1); + bc_node_add_link(ntree, nmap["add"], 0, nmap["mix"], 1); + bc_node_add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2); + + bc_node_add_link(ntree, nmap["mix"], 0, nmap["out"], 0); + /* experimental, probably not used. */ + bc_make_group(C, ntree, nmap); +#else + nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, 0, 300); + nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 300, 300); + bc_node_add_link(ntree, nmap["main"], 0, nmap["out"], 0); +#endif +} + +COLLADASW::ColorOrTexture bc_get_base_color(Material *ma) +{ + /* for alpha see bc_get_alpha() */ + Color default_color = {ma->r, ma->g, ma->b, 1.0}; + bNode *shader = bc_get_master_shader(ma); + if (ma->use_nodes && shader) { + return bc_get_cot_from_shader(shader, "Base Color", default_color, false); + } + else { + return bc_get_cot(default_color); + } +} + +COLLADASW::ColorOrTexture bc_get_emission(Material *ma) +{ + Color default_color = {0, 0, 0, 1}; + bNode *shader = bc_get_master_shader(ma); + if (ma->use_nodes && shader) { + return bc_get_cot_from_shader(shader, "Emission", default_color); + } + else { + return bc_get_cot(default_color); /* default black */ + } +} + +COLLADASW::ColorOrTexture bc_get_ambient(Material *ma) +{ + Color default_color = {0, 0, 0, 1.0}; + return bc_get_cot(default_color); +} + +COLLADASW::ColorOrTexture bc_get_specular(Material *ma) +{ + Color default_color = {0, 0, 0, 1.0}; + return bc_get_cot(default_color); +} + +COLLADASW::ColorOrTexture bc_get_reflective(Material *ma) +{ + Color default_color = {0, 0, 0, 1.0}; + return bc_get_cot(default_color); +} + +double bc_get_alpha(Material *ma) +{ + double alpha = ma->a; /* fallback if no socket found */ + bNode *master_shader = bc_get_master_shader(ma); + if (ma->use_nodes && master_shader) { + bc_get_float_from_shader(master_shader, alpha, "Alpha"); + } + return alpha; +} + +double bc_get_ior(Material *ma) +{ + double ior = -1; /* fallback if no socket found */ + bNode *master_shader = bc_get_master_shader(ma); + if (ma->use_nodes && master_shader) { + bc_get_float_from_shader(master_shader, ior, "IOR"); + } + return ior; +} + +double bc_get_shininess(Material *ma) +{ + double ior = -1; /* fallback if no socket found */ + bNode *master_shader = bc_get_master_shader(ma); + if (ma->use_nodes && master_shader) { + bc_get_float_from_shader(master_shader, ior, "Roughness"); + } + return ior; +} + +double bc_get_reflectivity(Material *ma) +{ + double reflectivity = ma->spec; /* fallback if no socket found */ + bNode *master_shader = bc_get_master_shader(ma); + if (ma->use_nodes && master_shader) { + bc_get_float_from_shader(master_shader, reflectivity, "Metallic"); + } + return reflectivity; +} + +double bc_get_float_from_shader(bNode *shader, double &val, std::string nodeid) +{ + bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); + if (socket) { + bNodeSocketValueFloat *ref = (bNodeSocketValueFloat *)socket->default_value; + val = (double)ref->value; + return true; + } + return false; +} + +COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, + std::string nodeid, + Color &default_color, + bool with_alpha) +{ + bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, nodeid.c_str()); + if (socket) { + bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value; + float *col = dcol->value; + return bc_get_cot(col, with_alpha); + } + else { + return bc_get_cot(default_color, with_alpha); + } +} + +bNode *bc_get_master_shader(Material *ma) +{ + bNodeTree *nodetree = ma->nodetree; + if (nodetree) { + for (bNode *node = (bNode *)nodetree->nodes.first; node; node = node->next) { + if (node->typeinfo->type == SH_NODE_BSDF_PRINCIPLED) { + return node; + } + } + } + return NULL; +} + +COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a) +{ + COLLADASW::Color color(r, g, b, a); + COLLADASW::ColorOrTexture cot(color); + return cot; +} + +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha) +{ + COLLADASW::Color color(col[0], col[1], col[2], (with_alpha) ? col[3] : 1.0); + COLLADASW::ColorOrTexture cot(color); + return cot; +} diff --git a/source/blender/io/collada/collada_utils.h b/source/blender/io/collada/collada_utils.h new file mode 100644 index 00000000000..5c5e1415422 --- /dev/null +++ b/source/blender/io/collada/collada_utils.h @@ -0,0 +1,399 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup collada + */ + +#ifndef __COLLADA_UTILS_H__ +#define __COLLADA_UTILS_H__ + +#include "COLLADAFWMeshPrimitive.h" +#include "COLLADAFWGeometry.h" +#include "COLLADAFWFloatOrDoubleArray.h" +#include "COLLADAFWTypes.h" +#include "COLLADASWEffectProfile.h" +#include "COLLADAFWColorOrTexture.h" + +#include +#include +#include +#include + +extern "C" { +#include "DNA_object_types.h" +#include "DNA_anim_types.h" +#include "DNA_constraint_types.h" +#include "DNA_mesh_types.h" +#include "DNA_light_types.h" +#include "DNA_camera_types.h" + +#include "DNA_customdata_types.h" +#include "DNA_texture_types.h" +#include "DNA_scene_types.h" + +#include "RNA_access.h" + +#include "BLI_linklist.h" +#include "BLI_utildefines.h" +#include "BLI_string.h" + +#include "BKE_main.h" +#include "BKE_context.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_idprop.h" +#include "BKE_node.h" +} + +#include "DEG_depsgraph_query.h" + +#include "ImportSettings.h" +#include "ExportSettings.h" +#include "collada_internal.h" +#include "BCSampleData.h" +#include "BlenderContext.h" + +constexpr int LIMITTED_PRECISION = 6; + +typedef std::map UidImageMap; +typedef std::map KeyImageMap; +typedef std::map> TexIndexTextureArrayMap; +typedef std::set BCObjectSet; + +extern void bc_update_scene(BlenderContext &blender_context, float ctime); + +/* Action helpers */ + +std::vector bc_getSceneActions(const bContext *C, Object *ob, bool all_actions); + +/* Action helpers */ + +inline bAction *bc_getSceneObjectAction(Object *ob) +{ + return (ob->adt && ob->adt->action) ? ob->adt->action : NULL; +} + +/* Returns Light Action or NULL */ +inline bAction *bc_getSceneLightAction(Object *ob) +{ + if (ob->type != OB_LAMP) { + return NULL; + } + + Light *lamp = (Light *)ob->data; + return (lamp->adt && lamp->adt->action) ? lamp->adt->action : NULL; +} + +/* Return Camera Action or NULL */ +inline bAction *bc_getSceneCameraAction(Object *ob) +{ + if (ob->type != OB_CAMERA) { + return NULL; + } + + Camera *camera = (Camera *)ob->data; + return (camera->adt && camera->adt->action) ? camera->adt->action : NULL; +} + +/* returns material action or NULL */ +inline bAction *bc_getSceneMaterialAction(Material *ma) +{ + if (ma == NULL) { + return NULL; + } + + return (ma->adt && ma->adt->action) ? ma->adt->action : NULL; +} + +std::string bc_get_action_id(std::string action_name, + std::string ob_name, + std::string channel_type, + std::string axis_name, + std::string axis_separator = "_"); + +extern float bc_get_float_value(const COLLADAFW::FloatOrDoubleArray &array, unsigned int index); +extern int bc_test_parent_loop(Object *par, Object *ob); + +extern bool bc_validateConstraints(bConstraint *con); + +bool bc_set_parent(Object *ob, Object *par, bContext *C, bool is_parent_space = true); +extern Object *bc_add_object( + Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name); +extern Mesh *bc_get_mesh_copy(BlenderContext &blender_context, + Object *ob, + BC_export_mesh_type export_mesh_type, + bool apply_modifiers, + bool triangulate); + +extern Object *bc_get_assigned_armature(Object *ob); +extern bool bc_has_object_type(LinkNode *export_set, short obtype); + +extern char *bc_CustomData_get_layer_name(const CustomData *data, int type, int n); +extern char *bc_CustomData_get_active_layer_name(const CustomData *data, int type); + +extern void bc_bubble_sort_by_Object_name(LinkNode *export_set); +extern bool bc_is_root_bone(Bone *aBone, bool deform_bones_only); +extern int bc_get_active_UVLayer(Object *ob); + +std::string bc_find_bonename_in_path(std::string path, std::string probe); + +inline std::string bc_string_after(const std::string &s, const std::string probe) +{ + size_t i = s.rfind(probe); + if (i != std::string::npos) { + return (s.substr(i + probe.length(), s.length() - i)); + } + return (s); +} + +inline std::string bc_string_before(const std::string &s, const std::string probe) +{ + size_t i = s.find(probe); + if (i != std::string::npos) { + return s.substr(0, i); + } + return (s); +} + +inline bool bc_startswith(std::string const &value, std::string const &starting) +{ + if (starting.size() > value.size()) { + return false; + } + return (value.substr(0, starting.size()) == starting); +} + +inline bool bc_endswith(const std::string &value, const std::string &ending) +{ + if (ending.size() > value.size()) { + return false; + } + + return value.compare(value.size() - ending.size(), ending.size(), ending) == 0; +} + +#if 0 /* UNUSED */ +inline bool bc_endswith(std::string const &value, std::string const &ending) +{ + if (ending.size() > value.size()) { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} +#endif + +extern std::string bc_replace_string(std::string data, + const std::string &pattern, + const std::string &replacement); +extern std::string bc_url_encode(std::string data); +extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene); +extern void bc_match_scale(std::vector *objects_done, + UnitConverter &unit_converter, + bool scale_to_scene); + +extern void bc_decompose(float mat[4][4], float *loc, float eul[3], float quat[4], float *size); +extern void bc_rotate_from_reference_quat(float quat_to[4], + float quat_from[4], + float mat_to[4][4]); + +extern void bc_triangulate_mesh(Mesh *me); +extern bool bc_is_leaf_bone(Bone *bone); +extern EditBone *bc_get_edit_bone(bArmature *armature, char *name); +extern int bc_set_layer(int bitfield, int layer, bool enable); +extern int bc_set_layer(int bitfield, int layer); + +inline bool bc_in_range(float a, float b, float range) +{ + return fabsf(a - b) < range; +} +void bc_copy_m4_farray(float r[4][4], float *a); +void bc_copy_farray_m4(float *r, float a[4][4]); +void bc_copy_darray_m4d(double *r, double a[4][4]); +void bc_copy_m4d_v44(double (&r)[4][4], std::vector> &a); +void bc_copy_v44_m4d(std::vector> &a, double (&r)[4][4]); + +void bc_sanitize_v3(double v[3], int precision); +void bc_sanitize_v3(float v[3], int precision); + +extern IDProperty *bc_get_IDProperty(Bone *bone, std::string key); +extern void bc_set_IDProperty(EditBone *ebone, const char *key, float value); +extern void bc_set_IDPropertyMatrix(EditBone *ebone, const char *key, float mat[4][4]); + +extern float bc_get_property(Bone *bone, std::string key, float def); +extern void bc_get_property_vector(Bone *bone, std::string key, float val[3], const float def[3]); +extern bool bc_get_property_matrix(Bone *bone, std::string key, float mat[4][4]); + +extern void bc_enable_fcurves(bAction *act, char *bone_name); +extern bool bc_bone_matrix_local_get(Object *ob, Bone *bone, Matrix &mat, bool for_opensim); +extern bool bc_is_animated(BCMatrixSampleMap &values); +extern bool bc_has_animations(Scene *sce, LinkNode *node); +extern bool bc_has_animations(Object *ob); + +extern void bc_add_global_transform(Matrix &to_mat, + const Matrix &from_mat, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_add_global_transform(Vector &to_vec, + const Vector &from_vec, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_add_global_transform(Vector &to_vec, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_add_global_transform(Matrix &to_mat, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_apply_global_transform(Matrix &to_mat, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_apply_global_transform(Vector &to_vec, + const BCMatrix &global_transform, + const bool invert = false); +extern void bc_create_restpose_mat(BCExportSettings &export_settings, + Bone *bone, + float to_mat[4][4], + float from_mat[4][4], + bool use_local_space); + +class ColladaBaseNodes { + private: + std::vector base_objects; + + public: + void add(Object *ob) + { + base_objects.push_back(ob); + } + + bool contains(Object *ob) + { + std::vector::iterator it = std::find(base_objects.begin(), base_objects.end(), ob); + return (it != base_objects.end()); + } + + int size() + { + return base_objects.size(); + } + + Object *get(int index) + { + return base_objects[index]; + } +}; + +class BCPolygonNormalsIndices { + std::vector normal_indices; + + public: + void add_index(unsigned int index) + { + normal_indices.push_back(index); + } + + unsigned int operator[](unsigned int i) + { + return normal_indices[i]; + } +}; + +class BoneExtended { + + private: + char name[MAXBONENAME]; + int chain_length; + bool is_leaf; + float tail[3]; + float roll; + + int bone_layers; + int use_connect; + bool has_custom_tail; + bool has_custom_roll; + + public: + BoneExtended(EditBone *aBone); + + void set_name(char *aName); + char *get_name(); + + void set_chain_length(const int aLength); + int get_chain_length(); + + void set_leaf_bone(bool state); + bool is_leaf_bone(); + + void set_bone_layers(std::string layers, std::vector &layer_labels); + int get_bone_layers(); + static std::string get_bone_layers(int bitfield); + + void set_roll(float roll); + bool has_roll(); + float get_roll(); + + void set_tail(float vec[]); + float *get_tail(); + bool has_tail(); + + void set_use_connect(int use_connect); + int get_use_connect(); +}; + +/* a map to store bone extension maps + * std:string : an armature name + * BoneExtended * : a map that contains extra data for bones + */ +typedef std::map BoneExtensionMap; + +/* + * A class to organize bone extension data for multiple Armatures. + * this is needed for the case where a Collada file contains 2 or more + * separate armatures. + */ +class BoneExtensionManager { + private: + std::map extended_bone_maps; + + public: + BoneExtensionMap &getExtensionMap(bArmature *armature); + ~BoneExtensionManager(); +}; + +void bc_add_default_shader(bContext *C, Material *ma); +bNode *bc_get_master_shader(Material *ma); + +COLLADASW::ColorOrTexture bc_get_base_color(Material *ma); +COLLADASW::ColorOrTexture bc_get_emission(Material *ma); +COLLADASW::ColorOrTexture bc_get_ambient(Material *ma); +COLLADASW::ColorOrTexture bc_get_specular(Material *ma); +COLLADASW::ColorOrTexture bc_get_reflective(Material *ma); + +double bc_get_reflectivity(Material *ma); +double bc_get_alpha(Material *ma); +double bc_get_ior(Material *ma); +double bc_get_shininess(Material *ma); + +double bc_get_float_from_shader(bNode *shader, double &ior, std::string nodeid); +COLLADASW::ColorOrTexture bc_get_cot_from_shader(bNode *shader, + std::string nodeid, + Color &default_color, + bool with_alpha = true); + +COLLADASW::ColorOrTexture bc_get_cot(float r, float g, float b, float a); +COLLADASW::ColorOrTexture bc_get_cot(Color col, bool with_alpha = true); + +#endif diff --git a/source/blender/io/collada/version.conf b/source/blender/io/collada/version.conf new file mode 100644 index 00000000000..d39af7a53df --- /dev/null +++ b/source/blender/io/collada/version.conf @@ -0,0 +1 @@ +463ba8a2ef5a021ce21df614dde29e0ee800e10b diff --git a/source/blender/io/usd/CMakeLists.txt b/source/blender/io/usd/CMakeLists.txt new file mode 100644 index 00000000000..732a638a255 --- /dev/null +++ b/source/blender/io/usd/CMakeLists.txt @@ -0,0 +1,111 @@ +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# The Original Code is Copyright (C) 2019, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +# This suppresses the warning "This file includes at least one deprecated or antiquated +# header which may be removed without further notice at a future date", which is caused +# by the USD library including on Linux. This has been reported at: +# https://github.com/PixarAnimationStudios/USD/issues/1057. +if(UNIX AND NOT APPLE) + add_definitions(-D_GLIBCXX_PERMIT_BACKWARD_HASH) +endif() +if(WIN32) + add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) +endif() +add_definitions(-DPXR_STATIC) + +set(INC + . + ../../blenkernel + ../../blenlib + ../../blenloader + ../../bmesh + ../../depsgraph + ../../editors/include + ../../makesdna + ../../makesrna + ../../windowmanager + ../../../../intern/guardedalloc + ../../../../intern/utfconv +) + +set(INC_SYS + ${USD_INCLUDE_DIRS} + ${BOOST_INCLUDE_DIR} + ${TBB_INCLUDE_DIR} +) + +set(SRC + intern/abstract_hierarchy_iterator.cc + intern/usd_capi.cc + intern/usd_hierarchy_iterator.cc + intern/usd_writer_abstract.cc + intern/usd_writer_camera.cc + intern/usd_writer_hair.cc + intern/usd_writer_light.cc + intern/usd_writer_mesh.cc + intern/usd_writer_metaball.cc + intern/usd_writer_transform.cc + + usd.h + intern/abstract_hierarchy_iterator.h + intern/usd_exporter_context.h + intern/usd_hierarchy_iterator.h + intern/usd_writer_abstract.h + intern/usd_writer_camera.h + intern/usd_writer_hair.h + intern/usd_writer_light.h + intern/usd_writer_mesh.h + intern/usd_writer_metaball.h + intern/usd_writer_transform.h +) + +set(LIB + bf_blenkernel + bf_blenlib +) + +list(APPEND LIB + ${BOOST_LIBRARIES} +) + +list(APPEND LIB +) + +blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") + +if(WIN32) + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /WHOLEARCHIVE:${USD_DEBUG_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /WHOLEARCHIVE:${USD_RELEASE_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /WHOLEARCHIVE:${USD_RELEASE_LIB}") + set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /WHOLEARCHIVE:${USD_RELEASE_LIB}") +endif() + +# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives +if(WIN32) + target_link_libraries(bf_usd INTERFACE ${USD_LIBRARIES}) +elseif(CMAKE_COMPILER_IS_GNUCXX) + target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}") +elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") + target_link_libraries(bf_usd INTERFACE -Wl,-force_load ${USD_LIBRARIES}) +else() + message(FATAL_ERROR "Unknown how to link USD with your compiler ${CMAKE_CXX_COMPILER_ID}") +endif() + +target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES}) diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc new file mode 100644 index 00000000000..a8ed2c5f2a5 --- /dev/null +++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.cc @@ -0,0 +1,595 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "abstract_hierarchy_iterator.h" + +#include +#include +#include +#include + +extern "C" { +#include "BKE_anim.h" +#include "BKE_particle.h" + +#include "BLI_assert.h" +#include "BLI_listbase.h" +#include "BLI_math_matrix.h" + +#include "DNA_ID.h" +#include "DNA_layer_types.h" +#include "DNA_object_types.h" +#include "DNA_particle_types.h" + +#include "DEG_depsgraph_query.h" +} + +namespace USD { + +const HierarchyContext *HierarchyContext::root() +{ + return nullptr; +} + +bool HierarchyContext::operator<(const HierarchyContext &other) const +{ + if (object != other.object) { + return object < other.object; + } + if (duplicator != NULL && duplicator == other.duplicator) { + // Only resort to string comparisons when both objects are created by the same duplicator. + return export_name < other.export_name; + } + + return export_parent < other.export_parent; +} + +bool HierarchyContext::is_instance() const +{ + return !original_export_path.empty(); +} +void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path) +{ + original_export_path = reference_export_path; +} +void HierarchyContext::mark_as_not_instanced() +{ + original_export_path.clear(); +} + +AbstractHierarchyWriter::~AbstractHierarchyWriter() +{ +} + +AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) + : depsgraph_(depsgraph), writers_() +{ +} + +AbstractHierarchyIterator::~AbstractHierarchyIterator() +{ +} + +void AbstractHierarchyIterator::iterate_and_write() +{ + export_graph_construct(); + connect_loose_objects(); + export_graph_prune(); + determine_export_paths(HierarchyContext::root()); + determine_duplication_references(HierarchyContext::root(), ""); + make_writers(HierarchyContext::root()); + export_graph_clear(); +} + +void AbstractHierarchyIterator::release_writers() +{ + for (WriterMap::value_type it : writers_) { + delete_object_writer(it.second); + } + writers_.clear(); +} + +std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const +{ + return name; +} + +std::string AbstractHierarchyIterator::get_id_name(const ID *id) const +{ + if (id == nullptr) { + return ""; + } + + return make_valid_name(std::string(id->name + 2)); +} + +std::string AbstractHierarchyIterator::get_object_data_path(const HierarchyContext *context) const +{ + BLI_assert(!context->export_path.empty()); + BLI_assert(context->object->data); + + return path_concatenate(context->export_path, get_object_data_name(context->object)); +} + +void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const +{ + size_t total_graph_size = 0; + for (const ExportGraph::value_type &map_iter : graph) { + const DupliAndDuplicator &parent_info = map_iter.first; + Object *const export_parent = parent_info.first; + Object *const duplicator = parent_info.second; + + if (duplicator != nullptr) { + printf(" DU %s (as dupped by %s):\n", + export_parent == nullptr ? "-null-" : (export_parent->id.name + 2), + duplicator->id.name + 2); + } + else { + printf(" OB %s:\n", export_parent == nullptr ? "-null-" : (export_parent->id.name + 2)); + } + + total_graph_size += map_iter.second.size(); + for (HierarchyContext *child_ctx : map_iter.second) { + if (child_ctx->duplicator == nullptr) { + printf(" - %s%s%s\n", + child_ctx->object->id.name + 2, + child_ctx->weak_export ? " (weak)" : "", + child_ctx->original_export_path.size() ? + (std::string("ref ") + child_ctx->original_export_path).c_str() : + ""); + } + else { + printf(" - %s (dup by %s%s) %s\n", + child_ctx->object->id.name + 2, + child_ctx->duplicator->id.name + 2, + child_ctx->weak_export ? ", weak" : "", + child_ctx->original_export_path.size() ? + (std::string("ref ") + child_ctx->original_export_path).c_str() : + ""); + } + } + } + printf(" (Total graph size: %lu objects\n", total_graph_size); +} + +void AbstractHierarchyIterator::export_graph_construct() +{ + Scene *scene = DEG_get_evaluated_scene(depsgraph_); + + DEG_OBJECT_ITER_BEGIN (depsgraph_, + object, + DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) { + // Non-instanced objects always have their object-parent as export-parent. + const bool weak_export = mark_as_weak_export(object); + visit_object(object, object->parent, weak_export); + + if (weak_export) { + // If a duplicator shouldn't be exported, its duplilist also shouldn't be. + continue; + } + + // Export the duplicated objects instanced by this object. + ListBase *lb = object_duplilist(depsgraph_, scene, object); + if (lb) { + // Construct the set of duplicated objects, so that later we can determine whether a parent + // is also duplicated itself. + std::set dupli_set; + LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { + if (!should_visit_dupli_object(dupli_object)) { + continue; + } + dupli_set.insert(dupli_object->ob); + } + + LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { + if (!should_visit_dupli_object(dupli_object)) { + continue; + } + + visit_dupli_object(dupli_object, object, dupli_set); + } + } + + free_object_duplilist(lb); + } + DEG_OBJECT_ITER_END; +} + +void AbstractHierarchyIterator::connect_loose_objects() +{ + // Find those objects whose parent is not part of the export graph; these + // objects would be skipped when traversing the graph as a hierarchy. + // These objects will have to be re-attached to some parent object in order to + // fit into the hierarchy. + ExportGraph loose_objects_graph = export_graph_; + for (const ExportGraph::value_type &map_iter : export_graph_) { + for (const HierarchyContext *child : map_iter.second) { + // An object that is marked as a child of another object is not considered 'loose'. + loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); + } + } + // The root of the hierarchy is always found, so it's never considered 'loose'. + loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); + + // Iterate over the loose objects and connect them to their export parent. + for (const ExportGraph::value_type &map_iter : loose_objects_graph) { + const DupliAndDuplicator &export_info = map_iter.first; + Object *object = export_info.first; + Object *export_parent = object->parent; + + while (true) { + // Loose objects will all be real objects, as duplicated objects always have + // their duplicator or other exported duplicated object as ancestor. + ExportGraph::iterator found_parent_iter = export_graph_.find( + std::make_pair(export_parent, nullptr)); + + visit_object(object, export_parent, true); + if (found_parent_iter != export_graph_.end()) { + break; + } + // 'export_parent' will never be nullptr here, as the export graph contains the + // tuple as root and thus will cause a break. + BLI_assert(export_parent != nullptr); + + object = export_parent; + export_parent = export_parent->parent; + } + } +} + +static bool remove_weak_subtrees(const HierarchyContext *context, + AbstractHierarchyIterator::ExportGraph &clean_graph, + const AbstractHierarchyIterator::ExportGraph &input_graph) +{ + bool all_is_weak = context != nullptr && context->weak_export; + Object *object = context != nullptr ? context->object : nullptr; + Object *duplicator = context != nullptr ? context->duplicator : nullptr; + + const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator); + AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator; + + child_iterator = input_graph.find(map_key); + if (child_iterator != input_graph.end()) { + for (HierarchyContext *child_context : child_iterator->second) { + bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph); + all_is_weak &= child_tree_is_weak; + + if (child_tree_is_weak) { + // This subtree is all weak, so we can remove it from the current object's children. + clean_graph[map_key].erase(child_context); + delete child_context; + } + } + } + + if (all_is_weak) { + // This node and all its children are weak, so it can be removed from the export graph. + clean_graph.erase(map_key); + } + + return all_is_weak; +} + +void AbstractHierarchyIterator::export_graph_prune() +{ + // Take a copy of the map so that we can modify while recursing. + ExportGraph unpruned_export_graph = export_graph_; + remove_weak_subtrees(HierarchyContext::root(), export_graph_, unpruned_export_graph); +} + +void AbstractHierarchyIterator::export_graph_clear() +{ + for (ExportGraph::iterator::value_type &it : export_graph_) { + for (HierarchyContext *context : it.second) { + delete context; + } + } + export_graph_.clear(); +} + +void AbstractHierarchyIterator::visit_object(Object *object, + Object *export_parent, + bool weak_export) +{ + HierarchyContext *context = new HierarchyContext(); + context->object = object; + context->export_name = get_object_name(object); + context->export_parent = export_parent; + context->duplicator = nullptr; + context->weak_export = weak_export; + context->animation_check_include_parent = false; + context->export_path = ""; + context->original_export_path = ""; + copy_m4_m4(context->matrix_world, object->obmat); + + export_graph_[std::make_pair(export_parent, nullptr)].insert(context); +} + +void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, + Object *duplicator, + const std::set &dupli_set) +{ + ExportGraph::key_type graph_index; + bool animation_check_include_parent = false; + + HierarchyContext *context = new HierarchyContext(); + context->object = dupli_object->ob; + context->duplicator = duplicator; + context->weak_export = false; + context->export_path = ""; + context->original_export_path = ""; + + /* If the dupli-object's parent is also instanced by this object, use that as the + * export parent. Otherwise use the dupli-parent as export parent. */ + Object *parent = dupli_object->ob->parent; + if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) { + // The parent object is part of the duplicated collection. + context->export_parent = parent; + graph_index = std::make_pair(parent, duplicator); + } + else { + /* The parent object is NOT part of the duplicated collection. This means that the world + * transform of this dupliobject can be influenced by objects that are not part of its + * export graph. */ + animation_check_include_parent = true; + context->export_parent = duplicator; + graph_index = std::make_pair(duplicator, nullptr); + } + + context->animation_check_include_parent = animation_check_include_parent; + copy_m4_m4(context->matrix_world, dupli_object->mat); + + // Construct export name for the dupli-instance. + std::stringstream suffix_stream; + suffix_stream << std::hex; + for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) { + suffix_stream << "-" << dupli_object->persistent_id[i]; + } + context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str()); + + export_graph_[graph_index].insert(context); +} + +AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children( + const HierarchyContext *context) +{ + if (context == nullptr) { + return export_graph_[std::make_pair(nullptr, nullptr)]; + } + + return export_graph_[std::make_pair(context->object, context->duplicator)]; +} + +void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context) +{ + const std::string &parent_export_path = parent_context ? parent_context->export_path : ""; + + for (HierarchyContext *context : graph_children(parent_context)) { + context->export_path = path_concatenate(parent_export_path, context->export_name); + + if (context->duplicator == nullptr) { + /* This is an original (i.e. non-instanced) object, so we should keep track of where it was + * exported to, just in case it gets instanced somewhere. */ + ID *source_ob = &context->object->id; + duplisource_export_path_[source_ob] = context->export_path; + + if (context->object->data != nullptr) { + ID *object_data = static_cast(context->object->data); + ID *source_data = object_data; + duplisource_export_path_[source_data] = get_object_data_path(context); + } + } + + determine_export_paths(context); + } +} + +void AbstractHierarchyIterator::determine_duplication_references( + const HierarchyContext *parent_context, std::string indent) +{ + ExportChildren children = graph_children(parent_context); + + for (HierarchyContext *context : children) { + if (context->duplicator != nullptr) { + ID *source_id = &context->object->id; + const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_id); + + if (it == duplisource_export_path_.end()) { + // The original was not found, so mark this instance as "the original". + context->mark_as_not_instanced(); + duplisource_export_path_[source_id] = context->export_path; + } + else { + context->mark_as_instance_of(it->second); + } + + if (context->object->data) { + ID *source_data_id = (ID *)context->object->data; + const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_data_id); + + if (it == duplisource_export_path_.end()) { + // The original was not found, so mark this instance as "original". + std::string data_path = get_object_data_path(context); + context->mark_as_not_instanced(); + duplisource_export_path_[source_id] = context->export_path; + duplisource_export_path_[source_data_id] = data_path; + } + } + } + + determine_duplication_references(context, indent + " "); + } +} + +void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context) +{ + AbstractHierarchyWriter *transform_writer = nullptr; + float parent_matrix_inv_world[4][4]; + + if (parent_context) { + invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world); + } + else { + unit_m4(parent_matrix_inv_world); + } + + for (HierarchyContext *context : graph_children(parent_context)) { + copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); + + // Get or create the transform writer. + transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer); + if (transform_writer == nullptr) { + // Unable to export, so there is nothing to attach any children to; just abort this entire + // branch of the export hierarchy. + return; + } + + BLI_assert(DEG_is_evaluated_object(context->object)); + /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse + * to write an orthographic camera. By the time that this is known, the XForm has already been + * written. */ + transform_writer->write(*context); + + if (!context->weak_export) { + make_writers_particle_systems(context); + make_writer_object_data(context); + } + + // Recurse into this object's children. + make_writers(context); + } + + // TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something. +} + +void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context) +{ + if (context->object->data == nullptr) { + return; + } + + HierarchyContext data_context = *context; + data_context.export_path = get_object_data_path(context); + + /* data_context.original_export_path is just a copy from the context. It points to the object, + * but needs to point to the object data. */ + if (data_context.is_instance()) { + ID *object_data = static_cast(context->object->data); + data_context.original_export_path = duplisource_export_path_[object_data]; + + /* If the object is marked as an instance, so should the object data. */ + BLI_assert(data_context.is_instance()); + } + + AbstractHierarchyWriter *data_writer; + data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer); + if (data_writer == nullptr) { + return; + } + + data_writer->write(data_context); +} + +void AbstractHierarchyIterator::make_writers_particle_systems( + const HierarchyContext *transform_context) +{ + Object *object = transform_context->object; + ParticleSystem *psys = static_cast(object->particlesystem.first); + for (; psys; psys = psys->next) { + if (!psys_check_enabled(object, psys, true)) { + continue; + } + + HierarchyContext hair_context = *transform_context; + hair_context.export_path = path_concatenate(transform_context->export_path, + get_id_name(&psys->part->id)); + hair_context.particle_system = psys; + + AbstractHierarchyWriter *writer = nullptr; + switch (psys->part->type) { + case PART_HAIR: + writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer); + break; + case PART_EMITTER: + writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer); + break; + } + + if (writer != nullptr) { + writer->write(hair_context); + } + } +} + +std::string AbstractHierarchyIterator::get_object_name(const Object *object) const +{ + return get_id_name(&object->id); +} + +std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const +{ + ID *object_data = static_cast(object->data); + return get_id_name(object_data); +} + +AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer(const std::string &export_path) +{ + WriterMap::iterator it = writers_.find(export_path); + + if (it == writers_.end()) { + return nullptr; + } + return it->second; +} + +AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer( + HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func) +{ + AbstractHierarchyWriter *writer = get_writer(context->export_path); + if (writer != nullptr) { + return writer; + } + + writer = (this->*create_func)(context); + if (writer == nullptr) { + return nullptr; + } + + writers_[context->export_path] = writer; + + return writer; +} + +std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path, + const std::string &child_path) const +{ + return parent_path + "/" + child_path; +} + +bool AbstractHierarchyIterator::mark_as_weak_export(const Object * /*object*/) const +{ + return false; +} +bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dupli_object) const +{ + // Removing dupli_object->no_draw hides things like custom bone shapes. + return !dupli_object->no_draw; +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/abstract_hierarchy_iterator.h b/source/blender/io/usd/intern/abstract_hierarchy_iterator.h new file mode 100644 index 00000000000..8bca2ddd447 --- /dev/null +++ b/source/blender/io/usd/intern/abstract_hierarchy_iterator.h @@ -0,0 +1,251 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +/* + * This file contains the AbstractHierarchyIterator. It is intended for exporters for file + * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that + * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic. + * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats. + * + * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the + * export hierarchy. The former is the parent/child structure in Blender, which can have multiple + * parent-like objects. For example, a duplicated object can have both a duplicator and a parent, + * both determining the final transform. The export hierarchy is the hierarchy as written to the + * file, and every object has only one export-parent. + * + * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export. + * Selections like "selected only" or "no hair systems" are left to concrete subclasses. + */ + +#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__ +#define __ABSTRACT_HIERARCHY_ITERATOR_H__ + +#include +#include +#include + +struct Base; +struct Depsgraph; +struct DupliObject; +struct ID; +struct Object; +struct ParticleSystem; +struct ViewLayer; + +namespace USD { + +class AbstractHierarchyWriter; + +/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext + * struct contains everything necessary to export a single object to a file. */ +struct HierarchyContext { + /*********** Determined during hierarchy iteration: ***************/ + Object *object; + Object *export_parent; + Object *duplicator; + float matrix_world[4][4]; + std::string export_name; + + /* When weak_export=true, the object will be exported only as transform, and only if is an + * ancestor of an object with weak_export=false. + * + * In other words: when weak_export=true but this object has no children, or all decendants also + * have weak_export=true, this object (and by recursive reasoning all its decendants) will be + * excluded from the export. + * + * The export hierarchy is kept as close to the the hierarchy in Blender as possible. As such, an + * object that serves as a parent for another object, but which should NOT be exported itself, is + * exported only as transform (i.e. as empty). This happens with objects that are part of a + * holdout collection (which prevents them from being exported) but also parent of an exported + * object. */ + bool weak_export; + + /* When true, this object should check its parents for animation data when determining whether + * it's animated. This is necessary when a parent object in Blender is not part of the export. */ + bool animation_check_include_parent; + + /*********** Determined during writer creation: ***************/ + float parent_matrix_inv_world[4][4]; // Inverse of the parent's world matrix. + std::string export_path; // Hierarchical path, such as "/grandparent/parent/objectname". + ParticleSystem *particle_system; // Only set for particle/hair writers. + + /* Hierarchical path of the object this object is duplicating; only set when this object should + * be stored as a reference to its original. It can happen that the original is not part of the + * exported objects, in which case this string is empty even though 'duplicator' is set. */ + std::string original_export_path; + + bool operator<(const HierarchyContext &other) const; + + /* Return a HierarchyContext representing the root of the export hierarchy. */ + static const HierarchyContext *root(); + + /* For handling instanced collections, instances created by particles, etc. */ + bool is_instance() const; + void mark_as_instance_of(const std::string &reference_export_path); + void mark_as_not_instanced(); +}; + +/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc. + * + * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally + * that's the first frame to be exported, but can be later, for example when objects are + * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every + * frame the object exists in the dependency graph and should be exported. + */ +class AbstractHierarchyWriter { + public: + virtual ~AbstractHierarchyWriter(); + virtual void write(HierarchyContext &context) = 0; + // TODO(Sybren): add function like absent() that's called when a writer was previously created, + // but wasn't used while exporting the current frame (for example, a particle-instanced mesh of + // which the particle is no longer alive). +}; + +/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export + * writers. These writers are then called to perform the actual writing to a USD or Alembic file. + * + * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame + * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done + * in separate code. + */ +class AbstractHierarchyIterator { + public: + /* Mapping from export path to writer. */ + typedef std::map WriterMap; + /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). + * This is typically used to store a pair of HierarchyContext::object and + * HierarchyContext::duplicator. */ + typedef std::pair DupliAndDuplicator; + /* All the children of some object, as per the export hierarchy. */ + typedef std::set ExportChildren; + /* Mapping from an object and its duplicator to the object's export-children. */ + typedef std::map ExportGraph; + /* Mapping from ID to its export path. This is used for instancing; given an + * instanced datablock, the export path of the original can be looked up. */ + typedef std::map ExportPathMap; + + protected: + ExportGraph export_graph_; + ExportPathMap duplisource_export_path_; + Depsgraph *depsgraph_; + WriterMap writers_; + + public: + explicit AbstractHierarchyIterator(Depsgraph *depsgraph); + virtual ~AbstractHierarchyIterator(); + + /* Iterate over the depsgraph, create writers, and tell the writers to write. + * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported + * frame. */ + void iterate_and_write(); + + /* Release all writers. Call after all frames have been exported. */ + void release_writers(); + + /* Convert the given name to something that is valid for the exported file format. + * This base implementation is a no-op; override in a concrete subclass. */ + virtual std::string make_valid_name(const std::string &name) const; + + /* Return the name of this ID datablock that is valid for the exported file format. Overriding is + * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format. + * NULL-safe: when `id == nullptr` this returns an empty string. */ + virtual std::string get_id_name(const ID *id) const; + + /* Given a HierarchyContext of some Object *, return an export path that is valid for its + * object->data. Overriding is necessary when the exported format does NOT expect the object's + * data to be a child of the object. */ + virtual std::string get_object_data_path(const HierarchyContext *context) const; + + private: + void debug_print_export_graph(const ExportGraph &graph) const; + + void export_graph_construct(); + void connect_loose_objects(); + void export_graph_prune(); + void export_graph_clear(); + + void visit_object(Object *object, Object *export_parent, bool weak_export); + void visit_dupli_object(DupliObject *dupli_object, + Object *duplicator, + const std::set &dupli_set); + + ExportChildren &graph_children(const HierarchyContext *parent_context); + + void determine_export_paths(const HierarchyContext *parent_context); + void determine_duplication_references(const HierarchyContext *parent_context, + std::string indent); + + void make_writers(const HierarchyContext *parent_context); + void make_writer_object_data(const HierarchyContext *context); + void make_writers_particle_systems(const HierarchyContext *context); + + /* Convenience wrappers around get_id_name(). */ + std::string get_object_name(const Object *object) const; + std::string get_object_data_name(const Object *object) const; + + AbstractHierarchyWriter *get_writer(const std::string &export_path); + + typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( + const HierarchyContext *); + /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func + * function should be one of the create_XXXX_writer(context) functions declared below. */ + AbstractHierarchyWriter *ensure_writer(HierarchyContext *context, + create_writer_func create_func); + + protected: + /* Construct a valid path for the export file format. This class concatenates by using '/' as a + * path separator, which is valid for both Alembic and USD. */ + virtual std::string path_concatenate(const std::string &parent_path, + const std::string &child_path) const; + + /* Return whether this object should be marked as 'weak export' or not. + * + * When this returns false, writers for the transform and data are created, + * and dupli-objects dupli-object generated from this object will be passed to + * should_visit_dupli_object(). + * + * When this returns true, only a transform writer is created and marked as + * 'weak export'. In this case, the transform writer will be removed before + * exporting starts, unless a decendant of this object is to be exported. + * Dupli-object generated from this object will also be skipped. + * + * See HierarchyContext::weak_export. + */ + virtual bool mark_as_weak_export(const Object *object) const; + + virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const; + + /* These functions should create an AbstractHierarchyWriter subclass instance, or return + * nullptr if the object or its data should not be exported. Returning a nullptr for + * data/hair/particle will NOT prevent the transform to be written. + * + * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in + * delete_object_writer(). */ + virtual AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) = 0; + virtual AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) = 0; + + /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */ + virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0; +}; + +} // namespace USD + +#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/io/usd/intern/usd_capi.cc b/source/blender/io/usd/intern/usd_capi.cc new file mode 100644 index 00000000000..83e11cd7bf3 --- /dev/null +++ b/source/blender/io/usd/intern/usd_capi.cc @@ -0,0 +1,233 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +#include "usd.h" +#include "usd_hierarchy_iterator.h" + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +extern "C" { +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "DNA_scene_types.h" + +#include "BKE_blender_version.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_scene.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" +#include "BLI_string.h" + +#include "WM_api.h" +#include "WM_types.h" +} + +namespace USD { + +struct ExportJobData { + ViewLayer *view_layer; + Main *bmain; + Depsgraph *depsgraph; + wmWindowManager *wm; + + char filename[FILE_MAX]; + USDExportParams params; + + short *stop; + short *do_update; + float *progress; + + bool was_canceled; + bool export_ok; +}; + +static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) +{ + ExportJobData *data = static_cast(customdata); + + data->stop = stop; + data->do_update = do_update; + data->progress = progress; + data->was_canceled = false; + + G.is_rendering = true; + WM_set_locked_interface(data->wm, true); + G.is_break = false; + + // Construct the depsgraph for exporting. + Scene *scene = DEG_get_input_scene(data->depsgraph); + ViewLayer *view_layer = DEG_get_input_view_layer(data->depsgraph); + DEG_graph_build_from_view_layer(data->depsgraph, data->bmain, scene, view_layer); + BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); + + *progress = 0.0f; + *do_update = true; + + // For restoring the current frame after exporting animation is done. + const int orig_frame = CFRA; + + pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); + if (!usd_stage) { + /* This happens when the USD JSON files cannot be found. When that happens, + * the USD library doesn't know it has the functionality to write USDA and + * USDC files, and creating a new UsdStage fails. */ + WM_reportf( + RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); + data->export_ok = false; + return; + } + + usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); + usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, + pxr::VtValue(scene->unit.scale_length)); + usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender ") + versionstr); + + // Set up the stage for animated data. + if (data->params.export_animation) { + usd_stage->SetTimeCodesPerSecond(FPS); + usd_stage->SetStartTimeCode(scene->r.sfra); + usd_stage->SetEndTimeCode(scene->r.efra); + } + + USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); + + if (data->params.export_animation) { + // Writing the animated frames is not 100% of the work, but it's our best guess. + float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1)); + + for (float frame = scene->r.sfra; frame <= scene->r.efra; frame++) { + if (G.is_break || (stop != nullptr && *stop)) { + break; + } + + // Update the scene for the next frame to render. + scene->r.cfra = static_cast(frame); + scene->r.subframe = frame - scene->r.cfra; + BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + + iter.set_export_frame(frame); + iter.iterate_and_write(); + + *progress += progress_per_frame; + *do_update = true; + } + } + else { + // If we're not animating, a single iteration over all objects is enough. + iter.iterate_and_write(); + } + + iter.release_writers(); + usd_stage->GetRootLayer()->Save(); + + // Finish up by going back to the keyframe that was current before we started. + if (CFRA != orig_frame) { + CFRA = orig_frame; + BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); + } + + data->export_ok = !data->was_canceled; + + *progress = 1.0f; + *do_update = true; +} + +static void export_endjob(void *customdata) +{ + ExportJobData *data = static_cast(customdata); + + DEG_graph_free(data->depsgraph); + + if (data->was_canceled && BLI_exists(data->filename)) { + BLI_delete(data->filename, false, false); + } + + G.is_rendering = false; + WM_set_locked_interface(data->wm, false); +} + +} // namespace USD + +bool USD_export(bContext *C, + const char *filepath, + const USDExportParams *params, + bool as_background_job) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + Scene *scene = CTX_data_scene(C); + + USD::ExportJobData *job = static_cast( + MEM_mallocN(sizeof(USD::ExportJobData), "ExportJobData")); + + job->bmain = CTX_data_main(C); + job->wm = CTX_wm_manager(C); + job->export_ok = false; + BLI_strncpy(job->filename, filepath, sizeof(job->filename)); + + job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); + job->params = *params; + + bool export_ok = false; + if (as_background_job) { + wmJob *wm_job = WM_jobs_get( + job->wm, CTX_wm_window(C), scene, "USD Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); + + /* setup job */ + WM_jobs_customdata_set(wm_job, job, MEM_freeN); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); + WM_jobs_callbacks(wm_job, USD::export_startjob, NULL, NULL, USD::export_endjob); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ + short stop = 0, do_update = 0; + float progress = 0.f; + + USD::export_startjob(job, &stop, &do_update, &progress); + USD::export_endjob(job); + export_ok = job->export_ok; + + MEM_freeN(job); + } + + return export_ok; +} + +int USD_get_version(void) +{ + /* USD 19.11 defines: + * + * #define PXR_MAJOR_VERSION 0 + * #define PXR_MINOR_VERSION 19 + * #define PXR_PATCH_VERSION 11 + * #define PXR_VERSION 1911 + * + * So the major version is implicit/invisible in the public version number. + */ + return PXR_VERSION; +} diff --git a/source/blender/io/usd/intern/usd_exporter_context.h b/source/blender/io/usd/intern/usd_exporter_context.h new file mode 100644 index 00000000000..4ae415b3d34 --- /dev/null +++ b/source/blender/io/usd/intern/usd_exporter_context.h @@ -0,0 +1,44 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_EXPORTER_CONTEXT_H__ +#define __USD_EXPORTER_CONTEXT_H__ + +#include "usd.h" + +#include +#include + +struct Depsgraph; +struct Object; + +namespace USD { + +class USDHierarchyIterator; + +struct USDExporterContext { + Depsgraph *depsgraph; + const pxr::UsdStageRefPtr stage; + const pxr::SdfPath usd_path; + const USDHierarchyIterator *hierarchy_iterator; + const USDExportParams &export_params; +}; + +} // namespace USD + +#endif /* __USD_EXPORTER_CONTEXT_H__ */ diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.cc b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc new file mode 100644 index 00000000000..fd888f39adc --- /dev/null +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.cc @@ -0,0 +1,150 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd.h" + +#include "usd_hierarchy_iterator.h" +#include "usd_writer_abstract.h" +#include "usd_writer_camera.h" +#include "usd_writer_hair.h" +#include "usd_writer_light.h" +#include "usd_writer_mesh.h" +#include "usd_writer_metaball.h" +#include "usd_writer_transform.h" + +#include + +#include + +extern "C" { +#include "BKE_anim.h" + +#include "BLI_assert.h" + +#include "DEG_depsgraph_query.h" + +#include "DNA_ID.h" +#include "DNA_layer_types.h" +#include "DNA_object_types.h" +} + +namespace USD { + +USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, + pxr::UsdStageRefPtr stage, + const USDExportParams ¶ms) + : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params) +{ +} + +bool USDHierarchyIterator::mark_as_weak_export(const Object *object) const +{ + if (params_.selected_objects_only && (object->base_flag & BASE_SELECTED) == 0) { + return true; + } + return false; +} + +void USDHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer) +{ + delete static_cast(writer); +} + +std::string USDHierarchyIterator::make_valid_name(const std::string &name) const +{ + return pxr::TfMakeValidIdentifier(name); +} + +void USDHierarchyIterator::set_export_frame(float frame_nr) +{ + // The USD stage is already set up to have FPS timecodes per frame. + export_time_ = pxr::UsdTimeCode(frame_nr); +} + +const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const +{ + return export_time_; +} + +USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context) +{ + return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; +} + +AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer( + const HierarchyContext *context) +{ + return new USDTransformWriter(create_usd_export_context(context)); +} + +AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const HierarchyContext *context) +{ + USDExporterContext usd_export_context = create_usd_export_context(context); + USDAbstractWriter *data_writer = nullptr; + + switch (context->object->type) { + case OB_MESH: + data_writer = new USDMeshWriter(usd_export_context); + break; + case OB_CAMERA: + data_writer = new USDCameraWriter(usd_export_context); + break; + case OB_LAMP: + data_writer = new USDLightWriter(usd_export_context); + break; + case OB_MBALL: + data_writer = new USDMetaballWriter(usd_export_context); + break; + + case OB_EMPTY: + case OB_CURVE: + case OB_SURF: + case OB_FONT: + case OB_SPEAKER: + case OB_LIGHTPROBE: + case OB_LATTICE: + case OB_ARMATURE: + case OB_GPENCIL: + return nullptr; + case OB_TYPE_MAX: + BLI_assert(!"OB_TYPE_MAX should not be used"); + return nullptr; + } + + if (!data_writer->is_supported(context)) { + delete data_writer; + return nullptr; + } + + return data_writer; +} + +AbstractHierarchyWriter *USDHierarchyIterator::create_hair_writer(const HierarchyContext *context) +{ + if (!params_.export_hair) { + return nullptr; + } + return new USDHairWriter(create_usd_export_context(context)); +} + +AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const HierarchyContext *) +{ + return nullptr; +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_hierarchy_iterator.h b/source/blender/io/usd/intern/usd_hierarchy_iterator.h new file mode 100644 index 00000000000..90c82c6e551 --- /dev/null +++ b/source/blender/io/usd/intern/usd_hierarchy_iterator.h @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_HIERARCHY_ITERATOR_H__ +#define __USD_HIERARCHY_ITERATOR_H__ + +#include "abstract_hierarchy_iterator.h" +#include "usd_exporter_context.h" +#include "usd.h" + +#include + +#include +#include + +struct Depsgraph; +struct ID; +struct Object; + +namespace USD { + +class USDHierarchyIterator : public AbstractHierarchyIterator { + private: + const pxr::UsdStageRefPtr stage_; + pxr::UsdTimeCode export_time_; + const USDExportParams ¶ms_; + + public: + USDHierarchyIterator(Depsgraph *depsgraph, + pxr::UsdStageRefPtr stage, + const USDExportParams ¶ms); + + void set_export_frame(float frame_nr); + const pxr::UsdTimeCode &get_export_time_code() const; + + virtual std::string make_valid_name(const std::string &name) const override; + + protected: + virtual bool mark_as_weak_export(const Object *object) const override; + + virtual AbstractHierarchyWriter *create_transform_writer( + const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override; + virtual AbstractHierarchyWriter *create_particle_writer( + const HierarchyContext *context) override; + + virtual void delete_object_writer(AbstractHierarchyWriter *writer) override; + + private: + USDExporterContext create_usd_export_context(const HierarchyContext *context); +}; + +} // namespace USD + +#endif /* __USD_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_abstract.cc b/source/blender/io/usd/intern/usd_writer_abstract.cc new file mode 100644 index 00000000000..4d0b4364fb5 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_abstract.cc @@ -0,0 +1,147 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_abstract.h" +#include "usd_hierarchy_iterator.h" + +#include + +extern "C" { +#include "BKE_animsys.h" +#include "BKE_key.h" + +#include "DNA_modifier_types.h" +} + +/* TfToken objects are not cheap to construct, so we do it once. */ +namespace usdtokens { +// Materials +static const pxr::TfToken diffuse_color("diffuseColor", pxr::TfToken::Immortal); +static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal); +static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal); +static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal); +static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); +static const pxr::TfToken surface("surface", pxr::TfToken::Immortal); +} // namespace usdtokens + +namespace USD { + +USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context) + : usd_export_context_(usd_export_context), + usd_value_writer_(), + frame_has_been_written_(false), + is_animated_(false) +{ +} + +USDAbstractWriter::~USDAbstractWriter() +{ +} + +bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const +{ + return true; +} + +pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const +{ + if (is_animated_) { + return usd_export_context_.hierarchy_iterator->get_export_time_code(); + } + // By using the default timecode USD won't even write a single `timeSample` for non-animated + // data. Instead, it writes it as non-timesampled. + static pxr::UsdTimeCode default_timecode = pxr::UsdTimeCode::Default(); + return default_timecode; +} + +void USDAbstractWriter::write(HierarchyContext &context) +{ + if (!frame_has_been_written_) { + is_animated_ = usd_export_context_.export_params.export_animation && + check_is_animated(context); + } + else if (!is_animated_) { + /* A frame has already been written, and without animation one frame is enough. */ + return; + } + + do_write(context); + + frame_has_been_written_ = true; +} + +bool USDAbstractWriter::check_is_animated(const HierarchyContext &context) const +{ + const Object *object = context.object; + + if (BKE_animdata_id_is_animated(static_cast(object->data))) { + return true; + } + if (BKE_key_from_object(object) != nullptr) { + return true; + } + + /* Test modifiers. */ + /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on + * time. */ + ModifierData *md = static_cast(object->modifiers.first); + while (md) { + if (md->type != eModifierType_Subsurf) { + return true; + } + md = md->next; + } + + return false; +} + +const pxr::SdfPath &USDAbstractWriter::usd_path() const +{ + return usd_export_context_.usd_path; +} + +pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) +{ + static pxr::SdfPath material_library_path("/_materials"); + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + + // Construct the material. + pxr::TfToken material_name(usd_export_context_.hierarchy_iterator->get_id_name(&material->id)); + pxr::SdfPath usd_path = material_library_path.AppendChild(material_name); + pxr::UsdShadeMaterial usd_material = pxr::UsdShadeMaterial::Get(stage, usd_path); + if (usd_material) { + return usd_material; + } + usd_material = pxr::UsdShadeMaterial::Define(stage, usd_path); + + // Construct the shader. + pxr::SdfPath shader_path = usd_path.AppendChild(usdtokens::preview_shader); + pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(stage, shader_path); + shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); + shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f) + .Set(pxr::GfVec3f(material->r, material->g, material->b)); + shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness); + shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic); + + // Connect the shader and the material together. + usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface); + + return usd_material; +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_abstract.h b/source/blender/io/usd/intern/usd_writer_abstract.h new file mode 100644 index 00000000000..835d3a42c80 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_abstract.h @@ -0,0 +1,77 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_ABSTRACT_H__ +#define __USD_WRITER_ABSTRACT_H__ + +#include "usd_exporter_context.h" +#include "abstract_hierarchy_iterator.h" + +#include +#include +#include +#include + +#include + +extern "C" { +#include "DEG_depsgraph_query.h" +#include "DNA_material_types.h" +} + +struct Material; +struct Object; + +namespace USD { + +class USDAbstractWriter : public AbstractHierarchyWriter { + protected: + const USDExporterContext usd_export_context_; + pxr::UsdUtilsSparseValueWriter usd_value_writer_; + + bool frame_has_been_written_; + bool is_animated_; + + public: + USDAbstractWriter(const USDExporterContext &usd_export_context); + virtual ~USDAbstractWriter(); + + virtual void write(HierarchyContext &context) override; + + /* Returns true if the data to be written is actually supported. This would, for example, allow a + * hypothetical camera writer accept a perspective camera but reject an orthogonal one. + * + * Returning false from a transform writer will prevent the object and all its decendants from + * being exported. Returning false from a data writer (object data, hair, or particles) will + * only prevent that data from being written (and thus cause the object to be exported as an + * Empty). */ + virtual bool is_supported(const HierarchyContext *context) const; + + const pxr::SdfPath &usd_path() const; + + protected: + virtual void do_write(HierarchyContext &context) = 0; + virtual bool check_is_animated(const HierarchyContext &context) const; + pxr::UsdTimeCode get_export_time_code() const; + + pxr::UsdShadeMaterial ensure_usd_material(Material *material); +}; + +} // namespace USD + +#endif /* __USD_WRITER_ABSTRACT_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_camera.cc b/source/blender/io/usd/intern/usd_writer_camera.cc new file mode 100644 index 00000000000..9b85d69559c --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_camera.cc @@ -0,0 +1,111 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_camera.h" +#include "usd_hierarchy_iterator.h" + +#include +#include + +extern "C" { +#include "BKE_camera.h" +#include "BLI_assert.h" + +#include "DNA_camera_types.h" +#include "DNA_scene_types.h" +} + +namespace USD { + +USDCameraWriter::USDCameraWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDCameraWriter::is_supported(const HierarchyContext *context) const +{ + Camera *camera = static_cast(context->object->data); + return camera->type == CAM_PERSP; +} + +static void camera_sensor_size_for_render(const Camera *camera, + const struct RenderData *rd, + float *r_sensor_x, + float *r_sensor_y) +{ + /* Compute the final image size in pixels. */ + float sizex = rd->xsch * rd->xasp; + float sizey = rd->ysch * rd->yasp; + + int sensor_fit = BKE_camera_sensor_fit(camera->sensor_fit, sizex, sizey); + + switch (sensor_fit) { + case CAMERA_SENSOR_FIT_HOR: + *r_sensor_x = camera->sensor_x; + *r_sensor_y = camera->sensor_x * sizey / sizex; + break; + case CAMERA_SENSOR_FIT_VERT: + *r_sensor_x = camera->sensor_y * sizex / sizey; + *r_sensor_y = camera->sensor_y; + break; + case CAMERA_SENSOR_FIT_AUTO: + BLI_assert(!"Camera fit should be either horizontal or vertical"); + break; + } +} + +void USDCameraWriter::do_write(HierarchyContext &context) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + pxr::UsdGeomCamera usd_camera = pxr::UsdGeomCamera::Define(usd_export_context_.stage, + usd_export_context_.usd_path); + + Camera *camera = static_cast(context.object->data); + Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); + + usd_camera.CreateProjectionAttr().Set(pxr::UsdGeomTokens->perspective); + + /* USD stores the focal length in "millimeters or tenths of world units", because at some point + * they decided world units might be centimeters. Quite confusing, as the USD Viewer shows the + * correct FoV when we write millimeters and not "tenths of world units". + */ + usd_camera.CreateFocalLengthAttr().Set(camera->lens, timecode); + + float aperture_x, aperture_y; + camera_sensor_size_for_render(camera, &scene->r, &aperture_x, &aperture_y); + + float film_aspect = aperture_x / aperture_y; + usd_camera.CreateHorizontalApertureAttr().Set(aperture_x, timecode); + usd_camera.CreateVerticalApertureAttr().Set(aperture_y, timecode); + usd_camera.CreateHorizontalApertureOffsetAttr().Set(aperture_x * camera->shiftx, timecode); + usd_camera.CreateVerticalApertureOffsetAttr().Set(aperture_y * camera->shifty * film_aspect, + timecode); + + usd_camera.CreateClippingRangeAttr().Set( + pxr::VtValue(pxr::GfVec2f(camera->clip_start, camera->clip_end)), timecode); + + // Write DoF-related attributes. + if (camera->dof.flag & CAM_DOF_ENABLED) { + usd_camera.CreateFStopAttr().Set(camera->dof.aperture_fstop, timecode); + + float focus_distance = scene->unit.scale_length * + BKE_camera_object_dof_distance(context.object); + usd_camera.CreateFocusDistanceAttr().Set(focus_distance, timecode); + } +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_camera.h b/source/blender/io/usd/intern/usd_writer_camera.h new file mode 100644 index 00000000000..971264ef11e --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_camera.h @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_CAMERA_H__ +#define __USD_WRITER_CAMERA_H__ + +#include "usd_writer_abstract.h" + +namespace USD { + +/* Writer for writing camera data to UsdGeomCamera. */ +class USDCameraWriter : public USDAbstractWriter { + public: + USDCameraWriter(const USDExporterContext &ctx); + + protected: + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; +}; + +} // namespace USD + +#endif /* __USD_WRITER_CAMERA_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_hair.cc b/source/blender/io/usd/intern/usd_writer_hair.cc new file mode 100644 index 00000000000..9251425c0b8 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_hair.cc @@ -0,0 +1,90 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_hair.h" +#include "usd_hierarchy_iterator.h" + +#include +#include + +extern "C" { +#include "BKE_particle.h" + +#include "DNA_particle_types.h" +} + +namespace USD { + +USDHairWriter::USDHairWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +void USDHairWriter::do_write(HierarchyContext &context) +{ + ParticleSystem *psys = context.particle_system; + ParticleCacheKey **cache = psys->pathcache; + if (cache == nullptr) { + return; + } + + pxr::UsdTimeCode timecode = get_export_time_code(); + pxr::UsdGeomBasisCurves curves = pxr::UsdGeomBasisCurves::Define(usd_export_context_.stage, + usd_export_context_.usd_path); + + // TODO(Sybren): deal with (psys->part->flag & PART_HAIR_BSPLINE) + curves.CreateBasisAttr(pxr::VtValue(pxr::UsdGeomTokens->bspline)); + curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->cubic)); + + pxr::VtArray points; + pxr::VtIntArray curve_point_counts; + curve_point_counts.reserve(psys->totpart); + + ParticleCacheKey *strand; + for (int strand_index = 0; strand_index < psys->totpart; ++strand_index) { + strand = cache[strand_index]; + + int point_count = strand->segments + 1; + curve_point_counts.push_back(point_count); + + for (int point_index = 0; point_index < point_count; ++point_index, ++strand) { + points.push_back(pxr::GfVec3f(strand->co)); + } + } + + pxr::UsdAttribute attr_points = curves.CreatePointsAttr(pxr::VtValue(), true); + pxr::UsdAttribute attr_vertex_counts = curves.CreateCurveVertexCountsAttr(pxr::VtValue(), true); + if (!attr_points.HasValue()) { + attr_points.Set(points, pxr::UsdTimeCode::Default()); + attr_vertex_counts.Set(curve_point_counts, pxr::UsdTimeCode::Default()); + } + usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(points), timecode); + usd_value_writer_.SetAttribute(attr_vertex_counts, pxr::VtValue(curve_point_counts), timecode); + + if (psys->totpart > 0) { + pxr::VtArray colors; + colors.push_back(pxr::GfVec3f(cache[0]->col)); + curves.CreateDisplayColorAttr(pxr::VtValue(colors)); + } +} + +bool USDHairWriter::check_is_animated(const HierarchyContext &) const +{ + return true; +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_hair.h b/source/blender/io/usd/intern/usd_writer_hair.h new file mode 100644 index 00000000000..1e882fa1654 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_hair.h @@ -0,0 +1,38 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_HAIR_H__ +#define __USD_WRITER_HAIR_H__ + +#include "usd_writer_abstract.h" + +namespace USD { + +/* Writer for writing hair particle data as USD curves. */ +class USDHairWriter : public USDAbstractWriter { + public: + USDHairWriter(const USDExporterContext &ctx); + + protected: + virtual void do_write(HierarchyContext &context) override; + virtual bool check_is_animated(const HierarchyContext &context) const override; +}; + +} // namespace USD + +#endif /* __USD_WRITER_HAIR_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc new file mode 100644 index 00000000000..e13e2c58a79 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -0,0 +1,112 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_light.h" +#include "usd_hierarchy_iterator.h" + +#include +#include +#include +#include + +extern "C" { +#include "BLI_assert.h" +#include "BLI_utildefines.h" + +#include "DNA_light_types.h" +#include "DNA_object_types.h" +} + +namespace USD { + +USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDLightWriter::is_supported(const HierarchyContext *context) const +{ + Light *light = static_cast(context->object->data); + return ELEM(light->type, LA_AREA, LA_LOCAL, LA_SUN); +} + +void USDLightWriter::do_write(HierarchyContext &context) +{ + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + const pxr::SdfPath &usd_path = usd_export_context_.usd_path; + pxr::UsdTimeCode timecode = get_export_time_code(); + + Light *light = static_cast(context.object->data); + pxr::UsdLuxLight usd_light; + + switch (light->type) { + case LA_AREA: + switch (light->area_shape) { + case LA_AREA_DISK: + case LA_AREA_ELLIPSE: { /* An ellipse light will deteriorate into a disk light. */ + pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path); + disk_light.CreateRadiusAttr().Set(light->area_size, timecode); + usd_light = disk_light; + break; + } + case LA_AREA_RECT: { + pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path); + rect_light.CreateWidthAttr().Set(light->area_size, timecode); + rect_light.CreateHeightAttr().Set(light->area_sizey, timecode); + usd_light = rect_light; + break; + } + case LA_AREA_SQUARE: { + pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path); + rect_light.CreateWidthAttr().Set(light->area_size, timecode); + rect_light.CreateHeightAttr().Set(light->area_size, timecode); + usd_light = rect_light; + break; + } + } + break; + case LA_LOCAL: { + pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path); + sphere_light.CreateRadiusAttr().Set(light->area_size, timecode); + usd_light = sphere_light; + break; + } + case LA_SUN: + usd_light = pxr::UsdLuxDistantLight::Define(stage, usd_path); + break; + default: + BLI_assert(!"is_supported() returned true for unsupported light type"); + } + + /* Scale factor to get to somewhat-similar illumination. Since the USDViewer had similar + * over-exposure as Blender Internal with the same values, this code applies the reverse of the + * versioning code in light_emission_unify(). */ + float usd_intensity; + if (light->type == LA_SUN) { + /* Untested, as the Hydra GL viewport of USDViewer doesn't support distant lights. */ + usd_intensity = light->energy; + } + else { + usd_intensity = light->energy / 100.f; + } + usd_light.CreateIntensityAttr().Set(usd_intensity, timecode); + + usd_light.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode); + usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode); +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_light.h b/source/blender/io/usd/intern/usd_writer_light.h new file mode 100644 index 00000000000..349c034b6bc --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_light.h @@ -0,0 +1,37 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_LIGHT_H__ +#define __USD_WRITER_LIGHT_H__ + +#include "usd_writer_abstract.h" + +namespace USD { + +class USDLightWriter : public USDAbstractWriter { + public: + USDLightWriter(const USDExporterContext &ctx); + + protected: + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; +}; + +} // namespace USD + +#endif /* __USD_WRITER_LIGHT_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_mesh.cc b/source/blender/io/usd/intern/usd_writer_mesh.cc new file mode 100644 index 00000000000..74005afaf31 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_mesh.cc @@ -0,0 +1,489 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_mesh.h" +#include "usd_hierarchy_iterator.h" + +#include +#include +#include + +extern "C" { +#include "BLI_assert.h" +#include "BLI_math_vector.h" + +#include "BKE_anim.h" +#include "BKE_customdata.h" +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" + +#include "DNA_layer_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_fluidsim_types.h" +#include "DNA_particle_types.h" +} + +namespace USD { + +USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const +{ + Object *object = context->object; + bool is_dupli = context->duplicator != nullptr; + int base_flag; + + if (is_dupli) { + /* Construct the object's base flags from its dupliparent, just like is done in + * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing + * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents + * copying the Object for every dupli. */ + base_flag = object->base_flag; + object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI; + } + + int visibility = BKE_object_visibility(object, + usd_export_context_.export_params.evaluation_mode); + + if (is_dupli) { + object->base_flag = base_flag; + } + + return (visibility & OB_VISIBLE_SELF) != 0; +} + +void USDGenericMeshWriter::do_write(HierarchyContext &context) +{ + Object *object_eval = context.object; + bool needsfree = false; + Mesh *mesh = get_export_mesh(object_eval, needsfree); + + if (mesh == NULL) { + return; + } + + try { + write_mesh(context, mesh); + + if (needsfree) { + free_export_mesh(mesh); + } + } + catch (...) { + if (needsfree) { + free_export_mesh(mesh); + } + throw; + } +} + +void USDGenericMeshWriter::free_export_mesh(Mesh *mesh) +{ + BKE_id_free(NULL, mesh); +} + +struct USDMeshData { + pxr::VtArray points; + pxr::VtIntArray face_vertex_counts; + pxr::VtIntArray face_indices; + std::map face_groups; + + /* The length of this array specifies the number of creases on the surface. Each element gives + * the number of (must be adjacent) vertices in each crease, whose indices are linearly laid out + * in the 'creaseIndices' attribute. Since each crease must be at least one edge long, each + * element of this array should be greater than one. */ + pxr::VtIntArray crease_lengths; + /* The indices of all vertices forming creased edges. The size of this array must be equal to the + * sum of all elements of the 'creaseLengths' attribute. */ + pxr::VtIntArray crease_vertex_indices; + /* The per-crease or per-edge sharpness for all creases (Usd.Mesh.SHARPNESS_INFINITE for a + * perfectly sharp crease). Since 'creaseLengths' encodes the number of vertices in each crease, + * the number of elements in this array will be either len(creaseLengths) or the sum over all X + * of (creaseLengths[X] - 1). Note that while the RI spec allows each crease to have either a + * single sharpness or a value per-edge, USD will encode either a single sharpness per crease on + * a mesh, or sharpnesses for all edges making up the creases on a mesh. */ + pxr::VtFloatArray crease_sharpnesses; +}; + +void USDGenericMeshWriter::write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + + const CustomData *ldata = &mesh->ldata; + for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { + const CustomDataLayer *layer = &ldata->layers[layer_idx]; + if (layer->type != CD_MLOOPUV) { + continue; + } + + /* UV coordinates are stored in a Primvar on the Mesh, and can be referenced from materials. + * The primvar name is the same as the UV Map name. This is to allow the standard name "st" + * for texture coordinates by naming the UV Map as such, without having to guess which UV Map + * is the "standard" one. */ + pxr::TfToken primvar_name(pxr::TfMakeValidIdentifier(layer->name)); + pxr::UsdGeomPrimvar uv_coords_primvar = usd_mesh.CreatePrimvar( + primvar_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->faceVarying); + + MLoopUV *mloopuv = static_cast(layer->data); + pxr::VtArray uv_coords; + for (int loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) { + uv_coords.push_back(pxr::GfVec2f(mloopuv[loop_idx].uv)); + } + + if (!uv_coords_primvar.HasValue()) { + uv_coords_primvar.Set(uv_coords, pxr::UsdTimeCode::Default()); + } + const pxr::UsdAttribute &uv_coords_attr = uv_coords_primvar.GetAttr(); + usd_value_writer_.SetAttribute(uv_coords_attr, pxr::VtValue(uv_coords), timecode); + } +} + +void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + pxr::UsdTimeCode defaultTime = pxr::UsdTimeCode::Default(); + pxr::UsdStageRefPtr stage = usd_export_context_.stage; + const pxr::SdfPath &usd_path = usd_export_context_.usd_path; + + pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(stage, usd_path); + USDMeshData usd_mesh_data; + get_geometry_data(mesh, usd_mesh_data); + + if (usd_export_context_.export_params.use_instancing && context.is_instance()) { + // This object data is instanced, just reference the original instead of writing a copy. + if (context.export_path == context.original_export_path) { + printf("USD ref error: export path is reference path: %s\n", context.export_path.c_str()); + BLI_assert(!"USD reference error"); + return; + } + pxr::SdfPath ref_path(context.original_export_path); + if (!usd_mesh.GetPrim().GetReferences().AddInternalReference(ref_path)) { + /* See this URL for a description fo why referencing may fail" + * https://graphics.pixar.com/usd/docs/api/class_usd_references.html#Usd_Failing_References + */ + printf("USD Export warning: unable to add reference from %s to %s, not instancing object\n", + context.export_path.c_str(), + context.original_export_path.c_str()); + return; + } + /* The material path will be of the form , which is outside the + subtree pointed to by ref_path. As a result, the referenced data is not allowed to point out + of its own subtree. It does work when we override the material with exactly the same path, + though.*/ + if (usd_export_context_.export_params.export_materials) { + assign_materials(context, usd_mesh, usd_mesh_data.face_groups); + } + return; + } + + pxr::UsdAttribute attr_points = usd_mesh.CreatePointsAttr(pxr::VtValue(), true); + pxr::UsdAttribute attr_face_vertex_counts = usd_mesh.CreateFaceVertexCountsAttr(pxr::VtValue(), + true); + pxr::UsdAttribute attr_face_vertex_indices = usd_mesh.CreateFaceVertexIndicesAttr(pxr::VtValue(), + true); + + if (!attr_points.HasValue()) { + // Provide the initial value as default. This makes USD write the value as constant if they + // don't change over time. + attr_points.Set(usd_mesh_data.points, defaultTime); + attr_face_vertex_counts.Set(usd_mesh_data.face_vertex_counts, defaultTime); + attr_face_vertex_indices.Set(usd_mesh_data.face_indices, defaultTime); + } + + usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(usd_mesh_data.points), timecode); + usd_value_writer_.SetAttribute( + attr_face_vertex_counts, pxr::VtValue(usd_mesh_data.face_vertex_counts), timecode); + usd_value_writer_.SetAttribute( + attr_face_vertex_indices, pxr::VtValue(usd_mesh_data.face_indices), timecode); + + if (!usd_mesh_data.crease_lengths.empty()) { + pxr::UsdAttribute attr_crease_lengths = usd_mesh.CreateCreaseLengthsAttr(pxr::VtValue(), true); + pxr::UsdAttribute attr_crease_indices = usd_mesh.CreateCreaseIndicesAttr(pxr::VtValue(), true); + pxr::UsdAttribute attr_crease_sharpness = usd_mesh.CreateCreaseSharpnessesAttr(pxr::VtValue(), + true); + + if (!attr_crease_lengths.HasValue()) { + attr_crease_lengths.Set(usd_mesh_data.crease_lengths, defaultTime); + attr_crease_indices.Set(usd_mesh_data.crease_vertex_indices, defaultTime); + attr_crease_sharpness.Set(usd_mesh_data.crease_sharpnesses, defaultTime); + } + + usd_value_writer_.SetAttribute( + attr_crease_lengths, pxr::VtValue(usd_mesh_data.crease_lengths), timecode); + usd_value_writer_.SetAttribute( + attr_crease_indices, pxr::VtValue(usd_mesh_data.crease_vertex_indices), timecode); + usd_value_writer_.SetAttribute( + attr_crease_sharpness, pxr::VtValue(usd_mesh_data.crease_sharpnesses), timecode); + } + + if (usd_export_context_.export_params.export_uvmaps) { + write_uv_maps(mesh, usd_mesh); + } + if (usd_export_context_.export_params.export_normals) { + write_normals(mesh, usd_mesh); + } + write_surface_velocity(context.object, mesh, usd_mesh); + + // TODO(Sybren): figure out what happens when the face groups change. + if (frame_has_been_written_) { + return; + } + + usd_mesh.CreateSubdivisionSchemeAttr().Set(pxr::UsdGeomTokens->none); + + if (usd_export_context_.export_params.export_materials) { + assign_materials(context, usd_mesh, usd_mesh_data.face_groups); + } +} + +static void get_vertices(const Mesh *mesh, USDMeshData &usd_mesh_data) +{ + usd_mesh_data.points.reserve(mesh->totvert); + + const MVert *verts = mesh->mvert; + for (int i = 0; i < mesh->totvert; ++i) { + usd_mesh_data.points.push_back(pxr::GfVec3f(verts[i].co)); + } +} + +static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data) +{ + /* Only construct face groups (a.k.a. geometry subsets) when we need them for material + * assignments. */ + bool construct_face_groups = mesh->totcol > 1; + + usd_mesh_data.face_vertex_counts.reserve(mesh->totpoly); + usd_mesh_data.face_indices.reserve(mesh->totloop); + + MLoop *mloop = mesh->mloop; + MPoly *mpoly = mesh->mpoly; + for (int i = 0; i < mesh->totpoly; ++i, ++mpoly) { + MLoop *loop = mloop + mpoly->loopstart; + usd_mesh_data.face_vertex_counts.push_back(mpoly->totloop); + for (int j = 0; j < mpoly->totloop; ++j, ++loop) { + usd_mesh_data.face_indices.push_back(loop->v); + } + + if (construct_face_groups) { + usd_mesh_data.face_groups[mpoly->mat_nr].push_back(i); + } + } +} + +static void get_creases(const Mesh *mesh, USDMeshData &usd_mesh_data) +{ + const float factor = 1.0f / 255.0f; + + MEdge *edge = mesh->medge; + float sharpness; + for (int edge_idx = 0, totedge = mesh->totedge; edge_idx < totedge; ++edge_idx, ++edge) { + if (edge->crease == 0) { + continue; + } + + if (edge->crease == 255) { + sharpness = pxr::UsdGeomMesh::SHARPNESS_INFINITE; + } + else { + sharpness = static_cast(edge->crease) * factor; + } + + usd_mesh_data.crease_vertex_indices.push_back(edge->v1); + usd_mesh_data.crease_vertex_indices.push_back(edge->v2); + usd_mesh_data.crease_lengths.push_back(2); + usd_mesh_data.crease_sharpnesses.push_back(sharpness); + } +} + +void USDGenericMeshWriter::get_geometry_data(const Mesh *mesh, USDMeshData &usd_mesh_data) +{ + get_vertices(mesh, usd_mesh_data); + get_loops_polys(mesh, usd_mesh_data); + get_creases(mesh, usd_mesh_data); +} + +void USDGenericMeshWriter::assign_materials(const HierarchyContext &context, + pxr::UsdGeomMesh usd_mesh, + const MaterialFaceGroups &usd_face_groups) +{ + if (context.object->totcol == 0) { + return; + } + + /* Binding a material to a geometry subset isn't supported by the Hydra GL viewport yet, + * which is why we always bind the first material to the entire mesh. See + * https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */ + bool mesh_material_bound = false; + for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) { + Material *material = BKE_object_material_get(context.object, mat_num + 1); + if (material == nullptr) { + continue; + } + + pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); + usd_material.Bind(usd_mesh.GetPrim()); + + /* USD seems to support neither per-material nor per-face-group double-sidedness, so we just + * use the flag from the first non-empty material slot. */ + usd_mesh.CreateDoubleSidedAttr( + pxr::VtValue((material->blend_flag & MA_BL_CULL_BACKFACE) == 0)); + + mesh_material_bound = true; + break; + } + + if (!mesh_material_bound) { + /* Blender defaults to double-sided, but USD to single-sided. */ + usd_mesh.CreateDoubleSidedAttr(pxr::VtValue(true)); + } + + if (!mesh_material_bound || usd_face_groups.size() < 2) { + /* Either all material slots were empty or there is only one material in use. As geometry + * subsets are only written when actually used to assign a material, and the mesh already has + * the material assigned, there is no need to continue. */ + return; + } + + // Define a geometry subset per material. + for (const MaterialFaceGroups::value_type &face_group : usd_face_groups) { + short material_number = face_group.first; + const pxr::VtIntArray &face_indices = face_group.second; + + Material *material = BKE_object_material_get(context.object, material_number + 1); + if (material == nullptr) { + continue; + } + + pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); + pxr::TfToken material_name = usd_material.GetPath().GetNameToken(); + + pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(usd_mesh); + pxr::UsdGeomSubset usd_face_subset = api.CreateMaterialBindSubset(material_name, face_indices); + usd_material.Bind(usd_face_subset.GetPrim()); + } +} + +void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) +{ + pxr::UsdTimeCode timecode = get_export_time_code(); + const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); + + pxr::VtVec3fArray loop_normals; + loop_normals.reserve(mesh->totloop); + + if (lnors != nullptr) { + /* Export custom loop normals. */ + for (int loop_idx = 0, totloop = mesh->totloop; loop_idx < totloop; ++loop_idx) { + loop_normals.push_back(pxr::GfVec3f(lnors[loop_idx])); + } + } + else { + /* Compute the loop normals based on the 'smooth' flag. */ + float normal[3]; + MPoly *mpoly = mesh->mpoly; + const MVert *mvert = mesh->mvert; + for (int poly_idx = 0, totpoly = mesh->totpoly; poly_idx < totpoly; ++poly_idx, ++mpoly) { + MLoop *mloop = mesh->mloop + mpoly->loopstart; + + if ((mpoly->flag & ME_SMOOTH) == 0) { + /* Flat shaded, use common normal for all verts. */ + BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, normal); + pxr::GfVec3f pxr_normal(normal); + for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx) { + loop_normals.push_back(pxr_normal); + } + } + else { + /* Smooth shaded, use individual vert normals. */ + for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx, ++mloop) { + normal_short_to_float_v3(normal, mvert[mloop->v].no); + loop_normals.push_back(pxr::GfVec3f(normal)); + } + } + } + } + + pxr::UsdAttribute attr_normals = usd_mesh.CreateNormalsAttr(pxr::VtValue(), true); + if (!attr_normals.HasValue()) { + attr_normals.Set(loop_normals, pxr::UsdTimeCode::Default()); + } + usd_value_writer_.SetAttribute(attr_normals, pxr::VtValue(loop_normals), timecode); + usd_mesh.SetNormalsInterpolation(pxr::UsdGeomTokens->faceVarying); +} + +void USDGenericMeshWriter::write_surface_velocity(Object *object, + const Mesh *mesh, + pxr::UsdGeomMesh usd_mesh) +{ + /* Only velocities from the fluid simulation are exported. This is the most important case, + * though, as the baked mesh changes topology all the time, and thus computing the velocities + * at import time in a post-processing step is hard. */ + ModifierData *md = modifiers_findByType(object, eModifierType_Fluidsim); + if (md == nullptr) { + return; + } + + /* Check that the fluid sim modifier is enabled and has useful data. */ + const bool use_render = (DEG_get_mode(usd_export_context_.depsgraph) == DAG_EVAL_RENDER); + const ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; + const Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); + if (!modifier_isEnabled(scene, md, required_mode)) { + return; + } + FluidsimModifierData *fsmd = reinterpret_cast(md); + if (!fsmd->fss || fsmd->fss->type != OB_FLUIDSIM_DOMAIN) { + return; + } + FluidsimSettings *fss = fsmd->fss; + if (!fss->meshVelocities) { + return; + } + + /* Export per-vertex velocity vectors. */ + pxr::VtVec3fArray usd_velocities; + usd_velocities.reserve(mesh->totvert); + + FluidVertexVelocity *mesh_velocities = fss->meshVelocities; + for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; + ++vertex_idx, ++mesh_velocities) { + usd_velocities.push_back(pxr::GfVec3f(mesh_velocities->vel)); + } + + pxr::UsdTimeCode timecode = get_export_time_code(); + usd_mesh.CreateVelocitiesAttr().Set(usd_velocities, timecode); +} + +USDMeshWriter::USDMeshWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx) +{ +} + +Mesh *USDMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/) +{ + return BKE_object_get_evaluated_mesh(object_eval); +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_mesh.h b/source/blender/io/usd/intern/usd_writer_mesh.h new file mode 100644 index 00000000000..4175e2b7e27 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_mesh.h @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_MESH_H__ +#define __USD_WRITER_MESH_H__ + +#include "usd_writer_abstract.h" + +#include + +namespace USD { + +struct USDMeshData; + +/* Writer for USD geometry. Does not assume the object is a mesh object. */ +class USDGenericMeshWriter : public USDAbstractWriter { + public: + USDGenericMeshWriter(const USDExporterContext &ctx); + + protected: + virtual bool is_supported(const HierarchyContext *context) const override; + virtual void do_write(HierarchyContext &context) override; + + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0; + virtual void free_export_mesh(Mesh *mesh); + + private: + /* Mapping from material slot number to array of face indices with that material. */ + typedef std::map MaterialFaceGroups; + + void write_mesh(HierarchyContext &context, Mesh *mesh); + void get_geometry_data(const Mesh *mesh, struct USDMeshData &usd_mesh_data); + void assign_materials(const HierarchyContext &context, + pxr::UsdGeomMesh usd_mesh, + const MaterialFaceGroups &usd_face_groups); + void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); + void write_surface_velocity(Object *object, const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); +}; + +class USDMeshWriter : public USDGenericMeshWriter { + public: + USDMeshWriter(const USDExporterContext &ctx); + + protected: + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; +}; + +} // namespace USD + +#endif /* __USD_WRITER_MESH_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_metaball.cc b/source/blender/io/usd/intern/usd_writer_metaball.cc new file mode 100644 index 00000000000..25b216d20f3 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_metaball.cc @@ -0,0 +1,81 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_metaball.h" +#include "usd_hierarchy_iterator.h" + +#include +#include +#include + +extern "C" { +#include "BLI_assert.h" + +#include "BKE_displist.h" +#include "BKE_lib_id.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_object.h" + +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +} + +namespace USD { + +USDMetaballWriter::USDMetaballWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx) +{ +} + +bool USDMetaballWriter::is_supported(const HierarchyContext *context) const +{ + Scene *scene = DEG_get_input_scene(usd_export_context_.depsgraph); + return is_basis_ball(scene, context->object) && USDGenericMeshWriter::is_supported(context); +} + +bool USDMetaballWriter::check_is_animated(const HierarchyContext & /*context*/) const +{ + /* We assume that metaballs are always animated, as the current object may + * not be animated but another ball in the same group may be. */ + return true; +} + +Mesh *USDMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree) +{ + Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval); + if (mesh_eval != nullptr) { + /* Mesh_eval only exists when generative modifiers are in use. */ + r_needsfree = false; + return mesh_eval; + } + r_needsfree = true; + return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false); +} + +void USDMetaballWriter::free_export_mesh(Mesh *mesh) +{ + BKE_id_free(nullptr, mesh); +} + +bool USDMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const +{ + Object *basis_ob = BKE_mball_basis_find(scene, ob); + return ob == basis_ob; +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_metaball.h b/source/blender/io/usd/intern/usd_writer_metaball.h new file mode 100644 index 00000000000..1a86daae2ae --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_metaball.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_METABALL_H__ +#define __USD_WRITER_METABALL_H__ + +#include "usd_writer_mesh.h" + +namespace USD { + +class USDMetaballWriter : public USDGenericMeshWriter { + public: + USDMetaballWriter(const USDExporterContext &ctx); + + protected: + virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; + virtual void free_export_mesh(Mesh *mesh) override; + virtual bool is_supported(const HierarchyContext *context) const override; + virtual bool check_is_animated(const HierarchyContext &context) const override; + + private: + bool is_basis_ball(Scene *scene, Object *ob) const; +}; + +} // namespace USD + +#endif /* __USD_WRITER_METABALL_H__ */ diff --git a/source/blender/io/usd/intern/usd_writer_transform.cc b/source/blender/io/usd/intern/usd_writer_transform.cc new file mode 100644 index 00000000000..321b516221a --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_transform.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#include "usd_writer_transform.h" +#include "usd_hierarchy_iterator.h" + +#include +#include + +extern "C" { +#include "BKE_object.h" + +#include "BLI_math_matrix.h" + +#include "DNA_layer_types.h" +} + +namespace USD { + +USDTransformWriter::USDTransformWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) +{ +} + +void USDTransformWriter::do_write(HierarchyContext &context) +{ + float parent_relative_matrix[4][4]; // The object matrix relative to the parent. + mul_m4_m4m4(parent_relative_matrix, context.parent_matrix_inv_world, context.matrix_world); + + // Write the transform relative to the parent. + pxr::UsdGeomXform xform = pxr::UsdGeomXform::Define(usd_export_context_.stage, + usd_export_context_.usd_path); + if (!xformOp_) { + xformOp_ = xform.AddTransformOp(); + } + xformOp_.Set(pxr::GfMatrix4d(parent_relative_matrix), get_export_time_code()); +} + +bool USDTransformWriter::check_is_animated(const HierarchyContext &context) const +{ + if (context.duplicator != NULL) { + /* This object is being duplicated, so could be emitted by a particle system and thus + * influenced by forces. TODO(Sybren): Make this more strict. Probably better to get from the + * depsgraph whether this object instance has a time source. */ + return true; + } + return BKE_object_moves_in_time(context.object, context.animation_check_include_parent); +} + +} // namespace USD diff --git a/source/blender/io/usd/intern/usd_writer_transform.h b/source/blender/io/usd/intern/usd_writer_transform.h new file mode 100644 index 00000000000..52c4a657f33 --- /dev/null +++ b/source/blender/io/usd/intern/usd_writer_transform.h @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ +#ifndef __USD_WRITER_TRANSFORM_H__ +#define __USD_WRITER_TRANSFORM_H__ + +#include "usd_writer_abstract.h" + +#include + +namespace USD { + +class USDTransformWriter : public USDAbstractWriter { + private: + pxr::UsdGeomXformOp xformOp_; + + public: + USDTransformWriter(const USDExporterContext &ctx); + + protected: + void do_write(HierarchyContext &context) override; + bool check_is_animated(const HierarchyContext &context) const override; +}; + +} // namespace USD + +#endif /* __USD_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/io/usd/usd.h b/source/blender/io/usd/usd.h new file mode 100644 index 00000000000..8a5575d53cf --- /dev/null +++ b/source/blender/io/usd/usd.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2019 Blender Foundation. + * All rights reserved. + */ + +#ifndef __USD_H__ +#define __USD_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "DEG_depsgraph.h" + +struct Scene; +struct bContext; + +struct USDExportParams { + bool export_animation; + bool export_hair; + bool export_uvmaps; + bool export_normals; + bool export_materials; + bool selected_objects_only; + bool use_instancing; + enum eEvaluationMode evaluation_mode; +}; + +/* The USD_export takes a as_background_job parameter, and returns a boolean. + * + * When as_background_job=true, returns false immediately after scheduling + * a background job. + * + * When as_background_job=false, performs the export synchronously, and returns + * true when the export was ok, and false if there were any errors. + */ + +bool USD_export(struct bContext *C, + const char *filepath, + const struct USDExportParams *params, + bool as_background_job); + +int USD_get_version(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __USD_H__ */ diff --git a/source/blender/makesrna/intern/CMakeLists.txt b/source/blender/makesrna/intern/CMakeLists.txt index b2f1f6c651d..a813fe12c02 100644 --- a/source/blender/makesrna/intern/CMakeLists.txt +++ b/source/blender/makesrna/intern/CMakeLists.txt @@ -287,7 +287,7 @@ endif() if(WITH_ALEMBIC) list(APPEND INC - ../../alembic + ../../io/alembic ) add_definitions(-DWITH_ALEMBIC) endif() diff --git a/source/blender/makesrna/intern/rna_cachefile.c b/source/blender/makesrna/intern/rna_cachefile.c index 189a4a7de86..7b4b7c11c17 100644 --- a/source/blender/makesrna/intern/rna_cachefile.c +++ b/source/blender/makesrna/intern/rna_cachefile.c @@ -42,7 +42,7 @@ # include "WM_types.h" # ifdef WITH_ALEMBIC -# include "../../../alembic/ABC_alembic.h" +# include "ABC_alembic.h" # endif static void rna_CacheFile_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index ee697955ad4..1b5077899f5 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -38,7 +38,7 @@ #include "rna_internal.h" /* own include */ #ifdef WITH_ALEMBIC -# include "../../alembic/ABC_alembic.h" +# include "ABC_alembic.h" #endif const EnumPropertyItem rna_enum_abc_compression_items[] = { diff --git a/source/blender/modifiers/CMakeLists.txt b/source/blender/modifiers/CMakeLists.txt index 3e340461673..48acbdc17f3 100644 --- a/source/blender/modifiers/CMakeLists.txt +++ b/source/blender/modifiers/CMakeLists.txt @@ -116,7 +116,7 @@ set(LIB if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) list(APPEND INC - ../alembic + ../io/alembic ) list(APPEND LIB bf_alembic diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 983ea02423f..0b05a0dfcfe 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -305,14 +305,14 @@ endif() if(WITH_ALEMBIC) add_definitions(-DWITH_ALEMBIC) list(APPEND INC - ../../alembic + ../../io/alembic ) endif() if(WITH_USD) add_definitions(-DWITH_USD) list(APPEND INC - ../../usd + ../../io/usd ) endif() diff --git a/source/blender/usd/CMakeLists.txt b/source/blender/usd/CMakeLists.txt deleted file mode 100644 index d85b3cfe4e5..00000000000 --- a/source/blender/usd/CMakeLists.txt +++ /dev/null @@ -1,111 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# The Original Code is Copyright (C) 2019, Blender Foundation -# All rights reserved. -# ***** END GPL LICENSE BLOCK ***** - -# This suppresses the warning "This file includes at least one deprecated or antiquated -# header which may be removed without further notice at a future date", which is caused -# by the USD library including on Linux. This has been reported at: -# https://github.com/PixarAnimationStudios/USD/issues/1057. -if(UNIX AND NOT APPLE) - add_definitions(-D_GLIBCXX_PERMIT_BACKWARD_HASH) -endif() -if(WIN32) - add_definitions(-DNOMINMAX -DWIN32_LEAN_AND_MEAN) -endif() -add_definitions(-DPXR_STATIC) - -set(INC - . - ../blenkernel - ../blenlib - ../blenloader - ../bmesh - ../depsgraph - ../editors/include - ../makesdna - ../makesrna - ../windowmanager - ../../../intern/guardedalloc - ../../../intern/utfconv -) - -set(INC_SYS - ${USD_INCLUDE_DIRS} - ${BOOST_INCLUDE_DIR} - ${TBB_INCLUDE_DIR} -) - -set(SRC - intern/abstract_hierarchy_iterator.cc - intern/usd_capi.cc - intern/usd_hierarchy_iterator.cc - intern/usd_writer_abstract.cc - intern/usd_writer_camera.cc - intern/usd_writer_hair.cc - intern/usd_writer_light.cc - intern/usd_writer_mesh.cc - intern/usd_writer_metaball.cc - intern/usd_writer_transform.cc - - usd.h - intern/abstract_hierarchy_iterator.h - intern/usd_exporter_context.h - intern/usd_hierarchy_iterator.h - intern/usd_writer_abstract.h - intern/usd_writer_camera.h - intern/usd_writer_hair.h - intern/usd_writer_light.h - intern/usd_writer_mesh.h - intern/usd_writer_metaball.h - intern/usd_writer_transform.h -) - -set(LIB - bf_blenkernel - bf_blenlib -) - -list(APPEND LIB - ${BOOST_LIBRARIES} -) - -list(APPEND LIB -) - -blender_add_lib(bf_usd "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") - -if(WIN32) - set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_DEBUG " /WHOLEARCHIVE:${USD_DEBUG_LIB}") - set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELEASE " /WHOLEARCHIVE:${USD_RELEASE_LIB}") - set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO " /WHOLEARCHIVE:${USD_RELEASE_LIB}") - set_property(TARGET bf_usd APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL " /WHOLEARCHIVE:${USD_RELEASE_LIB}") -endif() - -# Source: https://github.com/PixarAnimationStudios/USD/blob/master/BUILDING.md#linking-whole-archives -if(WIN32) - target_link_libraries(bf_usd INTERFACE ${USD_LIBRARIES}) -elseif(CMAKE_COMPILER_IS_GNUCXX) - target_link_libraries(bf_usd INTERFACE "-Wl,--whole-archive ${USD_LIBRARIES} -Wl,--no-whole-archive ${TBB_LIBRARIES}") -elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - target_link_libraries(bf_usd INTERFACE -Wl,-force_load ${USD_LIBRARIES}) -else() - message(FATAL_ERROR "Unknown how to link USD with your compiler ${CMAKE_CXX_COMPILER_ID}") -endif() - -target_link_libraries(bf_usd INTERFACE ${TBB_LIBRARIES}) diff --git a/source/blender/usd/intern/abstract_hierarchy_iterator.cc b/source/blender/usd/intern/abstract_hierarchy_iterator.cc deleted file mode 100644 index a8ed2c5f2a5..00000000000 --- a/source/blender/usd/intern/abstract_hierarchy_iterator.cc +++ /dev/null @@ -1,595 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "abstract_hierarchy_iterator.h" - -#include -#include -#include -#include - -extern "C" { -#include "BKE_anim.h" -#include "BKE_particle.h" - -#include "BLI_assert.h" -#include "BLI_listbase.h" -#include "BLI_math_matrix.h" - -#include "DNA_ID.h" -#include "DNA_layer_types.h" -#include "DNA_object_types.h" -#include "DNA_particle_types.h" - -#include "DEG_depsgraph_query.h" -} - -namespace USD { - -const HierarchyContext *HierarchyContext::root() -{ - return nullptr; -} - -bool HierarchyContext::operator<(const HierarchyContext &other) const -{ - if (object != other.object) { - return object < other.object; - } - if (duplicator != NULL && duplicator == other.duplicator) { - // Only resort to string comparisons when both objects are created by the same duplicator. - return export_name < other.export_name; - } - - return export_parent < other.export_parent; -} - -bool HierarchyContext::is_instance() const -{ - return !original_export_path.empty(); -} -void HierarchyContext::mark_as_instance_of(const std::string &reference_export_path) -{ - original_export_path = reference_export_path; -} -void HierarchyContext::mark_as_not_instanced() -{ - original_export_path.clear(); -} - -AbstractHierarchyWriter::~AbstractHierarchyWriter() -{ -} - -AbstractHierarchyIterator::AbstractHierarchyIterator(Depsgraph *depsgraph) - : depsgraph_(depsgraph), writers_() -{ -} - -AbstractHierarchyIterator::~AbstractHierarchyIterator() -{ -} - -void AbstractHierarchyIterator::iterate_and_write() -{ - export_graph_construct(); - connect_loose_objects(); - export_graph_prune(); - determine_export_paths(HierarchyContext::root()); - determine_duplication_references(HierarchyContext::root(), ""); - make_writers(HierarchyContext::root()); - export_graph_clear(); -} - -void AbstractHierarchyIterator::release_writers() -{ - for (WriterMap::value_type it : writers_) { - delete_object_writer(it.second); - } - writers_.clear(); -} - -std::string AbstractHierarchyIterator::make_valid_name(const std::string &name) const -{ - return name; -} - -std::string AbstractHierarchyIterator::get_id_name(const ID *id) const -{ - if (id == nullptr) { - return ""; - } - - return make_valid_name(std::string(id->name + 2)); -} - -std::string AbstractHierarchyIterator::get_object_data_path(const HierarchyContext *context) const -{ - BLI_assert(!context->export_path.empty()); - BLI_assert(context->object->data); - - return path_concatenate(context->export_path, get_object_data_name(context->object)); -} - -void AbstractHierarchyIterator::debug_print_export_graph(const ExportGraph &graph) const -{ - size_t total_graph_size = 0; - for (const ExportGraph::value_type &map_iter : graph) { - const DupliAndDuplicator &parent_info = map_iter.first; - Object *const export_parent = parent_info.first; - Object *const duplicator = parent_info.second; - - if (duplicator != nullptr) { - printf(" DU %s (as dupped by %s):\n", - export_parent == nullptr ? "-null-" : (export_parent->id.name + 2), - duplicator->id.name + 2); - } - else { - printf(" OB %s:\n", export_parent == nullptr ? "-null-" : (export_parent->id.name + 2)); - } - - total_graph_size += map_iter.second.size(); - for (HierarchyContext *child_ctx : map_iter.second) { - if (child_ctx->duplicator == nullptr) { - printf(" - %s%s%s\n", - child_ctx->object->id.name + 2, - child_ctx->weak_export ? " (weak)" : "", - child_ctx->original_export_path.size() ? - (std::string("ref ") + child_ctx->original_export_path).c_str() : - ""); - } - else { - printf(" - %s (dup by %s%s) %s\n", - child_ctx->object->id.name + 2, - child_ctx->duplicator->id.name + 2, - child_ctx->weak_export ? ", weak" : "", - child_ctx->original_export_path.size() ? - (std::string("ref ") + child_ctx->original_export_path).c_str() : - ""); - } - } - } - printf(" (Total graph size: %lu objects\n", total_graph_size); -} - -void AbstractHierarchyIterator::export_graph_construct() -{ - Scene *scene = DEG_get_evaluated_scene(depsgraph_); - - DEG_OBJECT_ITER_BEGIN (depsgraph_, - object, - DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | - DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) { - // Non-instanced objects always have their object-parent as export-parent. - const bool weak_export = mark_as_weak_export(object); - visit_object(object, object->parent, weak_export); - - if (weak_export) { - // If a duplicator shouldn't be exported, its duplilist also shouldn't be. - continue; - } - - // Export the duplicated objects instanced by this object. - ListBase *lb = object_duplilist(depsgraph_, scene, object); - if (lb) { - // Construct the set of duplicated objects, so that later we can determine whether a parent - // is also duplicated itself. - std::set dupli_set; - LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { - if (!should_visit_dupli_object(dupli_object)) { - continue; - } - dupli_set.insert(dupli_object->ob); - } - - LISTBASE_FOREACH (DupliObject *, dupli_object, lb) { - if (!should_visit_dupli_object(dupli_object)) { - continue; - } - - visit_dupli_object(dupli_object, object, dupli_set); - } - } - - free_object_duplilist(lb); - } - DEG_OBJECT_ITER_END; -} - -void AbstractHierarchyIterator::connect_loose_objects() -{ - // Find those objects whose parent is not part of the export graph; these - // objects would be skipped when traversing the graph as a hierarchy. - // These objects will have to be re-attached to some parent object in order to - // fit into the hierarchy. - ExportGraph loose_objects_graph = export_graph_; - for (const ExportGraph::value_type &map_iter : export_graph_) { - for (const HierarchyContext *child : map_iter.second) { - // An object that is marked as a child of another object is not considered 'loose'. - loose_objects_graph.erase(std::make_pair(child->object, child->duplicator)); - } - } - // The root of the hierarchy is always found, so it's never considered 'loose'. - loose_objects_graph.erase(std::make_pair(nullptr, nullptr)); - - // Iterate over the loose objects and connect them to their export parent. - for (const ExportGraph::value_type &map_iter : loose_objects_graph) { - const DupliAndDuplicator &export_info = map_iter.first; - Object *object = export_info.first; - Object *export_parent = object->parent; - - while (true) { - // Loose objects will all be real objects, as duplicated objects always have - // their duplicator or other exported duplicated object as ancestor. - ExportGraph::iterator found_parent_iter = export_graph_.find( - std::make_pair(export_parent, nullptr)); - - visit_object(object, export_parent, true); - if (found_parent_iter != export_graph_.end()) { - break; - } - // 'export_parent' will never be nullptr here, as the export graph contains the - // tuple as root and thus will cause a break. - BLI_assert(export_parent != nullptr); - - object = export_parent; - export_parent = export_parent->parent; - } - } -} - -static bool remove_weak_subtrees(const HierarchyContext *context, - AbstractHierarchyIterator::ExportGraph &clean_graph, - const AbstractHierarchyIterator::ExportGraph &input_graph) -{ - bool all_is_weak = context != nullptr && context->weak_export; - Object *object = context != nullptr ? context->object : nullptr; - Object *duplicator = context != nullptr ? context->duplicator : nullptr; - - const AbstractHierarchyIterator::DupliAndDuplicator map_key = std::make_pair(object, duplicator); - AbstractHierarchyIterator::ExportGraph::const_iterator child_iterator; - - child_iterator = input_graph.find(map_key); - if (child_iterator != input_graph.end()) { - for (HierarchyContext *child_context : child_iterator->second) { - bool child_tree_is_weak = remove_weak_subtrees(child_context, clean_graph, input_graph); - all_is_weak &= child_tree_is_weak; - - if (child_tree_is_weak) { - // This subtree is all weak, so we can remove it from the current object's children. - clean_graph[map_key].erase(child_context); - delete child_context; - } - } - } - - if (all_is_weak) { - // This node and all its children are weak, so it can be removed from the export graph. - clean_graph.erase(map_key); - } - - return all_is_weak; -} - -void AbstractHierarchyIterator::export_graph_prune() -{ - // Take a copy of the map so that we can modify while recursing. - ExportGraph unpruned_export_graph = export_graph_; - remove_weak_subtrees(HierarchyContext::root(), export_graph_, unpruned_export_graph); -} - -void AbstractHierarchyIterator::export_graph_clear() -{ - for (ExportGraph::iterator::value_type &it : export_graph_) { - for (HierarchyContext *context : it.second) { - delete context; - } - } - export_graph_.clear(); -} - -void AbstractHierarchyIterator::visit_object(Object *object, - Object *export_parent, - bool weak_export) -{ - HierarchyContext *context = new HierarchyContext(); - context->object = object; - context->export_name = get_object_name(object); - context->export_parent = export_parent; - context->duplicator = nullptr; - context->weak_export = weak_export; - context->animation_check_include_parent = false; - context->export_path = ""; - context->original_export_path = ""; - copy_m4_m4(context->matrix_world, object->obmat); - - export_graph_[std::make_pair(export_parent, nullptr)].insert(context); -} - -void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, - Object *duplicator, - const std::set &dupli_set) -{ - ExportGraph::key_type graph_index; - bool animation_check_include_parent = false; - - HierarchyContext *context = new HierarchyContext(); - context->object = dupli_object->ob; - context->duplicator = duplicator; - context->weak_export = false; - context->export_path = ""; - context->original_export_path = ""; - - /* If the dupli-object's parent is also instanced by this object, use that as the - * export parent. Otherwise use the dupli-parent as export parent. */ - Object *parent = dupli_object->ob->parent; - if (parent != nullptr && dupli_set.find(parent) != dupli_set.end()) { - // The parent object is part of the duplicated collection. - context->export_parent = parent; - graph_index = std::make_pair(parent, duplicator); - } - else { - /* The parent object is NOT part of the duplicated collection. This means that the world - * transform of this dupliobject can be influenced by objects that are not part of its - * export graph. */ - animation_check_include_parent = true; - context->export_parent = duplicator; - graph_index = std::make_pair(duplicator, nullptr); - } - - context->animation_check_include_parent = animation_check_include_parent; - copy_m4_m4(context->matrix_world, dupli_object->mat); - - // Construct export name for the dupli-instance. - std::stringstream suffix_stream; - suffix_stream << std::hex; - for (int i = 0; i < MAX_DUPLI_RECUR && dupli_object->persistent_id[i] != INT_MAX; i++) { - suffix_stream << "-" << dupli_object->persistent_id[i]; - } - context->export_name = make_valid_name(get_object_name(context->object) + suffix_stream.str()); - - export_graph_[graph_index].insert(context); -} - -AbstractHierarchyIterator::ExportChildren &AbstractHierarchyIterator::graph_children( - const HierarchyContext *context) -{ - if (context == nullptr) { - return export_graph_[std::make_pair(nullptr, nullptr)]; - } - - return export_graph_[std::make_pair(context->object, context->duplicator)]; -} - -void AbstractHierarchyIterator::determine_export_paths(const HierarchyContext *parent_context) -{ - const std::string &parent_export_path = parent_context ? parent_context->export_path : ""; - - for (HierarchyContext *context : graph_children(parent_context)) { - context->export_path = path_concatenate(parent_export_path, context->export_name); - - if (context->duplicator == nullptr) { - /* This is an original (i.e. non-instanced) object, so we should keep track of where it was - * exported to, just in case it gets instanced somewhere. */ - ID *source_ob = &context->object->id; - duplisource_export_path_[source_ob] = context->export_path; - - if (context->object->data != nullptr) { - ID *object_data = static_cast(context->object->data); - ID *source_data = object_data; - duplisource_export_path_[source_data] = get_object_data_path(context); - } - } - - determine_export_paths(context); - } -} - -void AbstractHierarchyIterator::determine_duplication_references( - const HierarchyContext *parent_context, std::string indent) -{ - ExportChildren children = graph_children(parent_context); - - for (HierarchyContext *context : children) { - if (context->duplicator != nullptr) { - ID *source_id = &context->object->id; - const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_id); - - if (it == duplisource_export_path_.end()) { - // The original was not found, so mark this instance as "the original". - context->mark_as_not_instanced(); - duplisource_export_path_[source_id] = context->export_path; - } - else { - context->mark_as_instance_of(it->second); - } - - if (context->object->data) { - ID *source_data_id = (ID *)context->object->data; - const ExportPathMap::const_iterator &it = duplisource_export_path_.find(source_data_id); - - if (it == duplisource_export_path_.end()) { - // The original was not found, so mark this instance as "original". - std::string data_path = get_object_data_path(context); - context->mark_as_not_instanced(); - duplisource_export_path_[source_id] = context->export_path; - duplisource_export_path_[source_data_id] = data_path; - } - } - } - - determine_duplication_references(context, indent + " "); - } -} - -void AbstractHierarchyIterator::make_writers(const HierarchyContext *parent_context) -{ - AbstractHierarchyWriter *transform_writer = nullptr; - float parent_matrix_inv_world[4][4]; - - if (parent_context) { - invert_m4_m4(parent_matrix_inv_world, parent_context->matrix_world); - } - else { - unit_m4(parent_matrix_inv_world); - } - - for (HierarchyContext *context : graph_children(parent_context)) { - copy_m4_m4(context->parent_matrix_inv_world, parent_matrix_inv_world); - - // Get or create the transform writer. - transform_writer = ensure_writer(context, &AbstractHierarchyIterator::create_transform_writer); - if (transform_writer == nullptr) { - // Unable to export, so there is nothing to attach any children to; just abort this entire - // branch of the export hierarchy. - return; - } - - BLI_assert(DEG_is_evaluated_object(context->object)); - /* XXX This can lead to too many XForms being written. For example, a camera writer can refuse - * to write an orthographic camera. By the time that this is known, the XForm has already been - * written. */ - transform_writer->write(*context); - - if (!context->weak_export) { - make_writers_particle_systems(context); - make_writer_object_data(context); - } - - // Recurse into this object's children. - make_writers(context); - } - - // TODO(Sybren): iterate over all unused writers and call unused_during_iteration() or something. -} - -void AbstractHierarchyIterator::make_writer_object_data(const HierarchyContext *context) -{ - if (context->object->data == nullptr) { - return; - } - - HierarchyContext data_context = *context; - data_context.export_path = get_object_data_path(context); - - /* data_context.original_export_path is just a copy from the context. It points to the object, - * but needs to point to the object data. */ - if (data_context.is_instance()) { - ID *object_data = static_cast(context->object->data); - data_context.original_export_path = duplisource_export_path_[object_data]; - - /* If the object is marked as an instance, so should the object data. */ - BLI_assert(data_context.is_instance()); - } - - AbstractHierarchyWriter *data_writer; - data_writer = ensure_writer(&data_context, &AbstractHierarchyIterator::create_data_writer); - if (data_writer == nullptr) { - return; - } - - data_writer->write(data_context); -} - -void AbstractHierarchyIterator::make_writers_particle_systems( - const HierarchyContext *transform_context) -{ - Object *object = transform_context->object; - ParticleSystem *psys = static_cast(object->particlesystem.first); - for (; psys; psys = psys->next) { - if (!psys_check_enabled(object, psys, true)) { - continue; - } - - HierarchyContext hair_context = *transform_context; - hair_context.export_path = path_concatenate(transform_context->export_path, - get_id_name(&psys->part->id)); - hair_context.particle_system = psys; - - AbstractHierarchyWriter *writer = nullptr; - switch (psys->part->type) { - case PART_HAIR: - writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_hair_writer); - break; - case PART_EMITTER: - writer = ensure_writer(&hair_context, &AbstractHierarchyIterator::create_particle_writer); - break; - } - - if (writer != nullptr) { - writer->write(hair_context); - } - } -} - -std::string AbstractHierarchyIterator::get_object_name(const Object *object) const -{ - return get_id_name(&object->id); -} - -std::string AbstractHierarchyIterator::get_object_data_name(const Object *object) const -{ - ID *object_data = static_cast(object->data); - return get_id_name(object_data); -} - -AbstractHierarchyWriter *AbstractHierarchyIterator::get_writer(const std::string &export_path) -{ - WriterMap::iterator it = writers_.find(export_path); - - if (it == writers_.end()) { - return nullptr; - } - return it->second; -} - -AbstractHierarchyWriter *AbstractHierarchyIterator::ensure_writer( - HierarchyContext *context, AbstractHierarchyIterator::create_writer_func create_func) -{ - AbstractHierarchyWriter *writer = get_writer(context->export_path); - if (writer != nullptr) { - return writer; - } - - writer = (this->*create_func)(context); - if (writer == nullptr) { - return nullptr; - } - - writers_[context->export_path] = writer; - - return writer; -} - -std::string AbstractHierarchyIterator::path_concatenate(const std::string &parent_path, - const std::string &child_path) const -{ - return parent_path + "/" + child_path; -} - -bool AbstractHierarchyIterator::mark_as_weak_export(const Object * /*object*/) const -{ - return false; -} -bool AbstractHierarchyIterator::should_visit_dupli_object(const DupliObject *dupli_object) const -{ - // Removing dupli_object->no_draw hides things like custom bone shapes. - return !dupli_object->no_draw; -} - -} // namespace USD diff --git a/source/blender/usd/intern/abstract_hierarchy_iterator.h b/source/blender/usd/intern/abstract_hierarchy_iterator.h deleted file mode 100644 index 8bca2ddd447..00000000000 --- a/source/blender/usd/intern/abstract_hierarchy_iterator.h +++ /dev/null @@ -1,251 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ - -/* - * This file contains the AbstractHierarchyIterator. It is intended for exporters for file - * formats that concern an entire hierarchy of objects (rather than, for example, an OBJ file that - * contains only a single mesh). Examples are Universal Scene Description (USD) and Alembic. - * AbstractHierarchyIterator is intended to be subclassed to support concrete file formats. - * - * The AbstractHierarchyIterator makes a distinction between the actual object hierarchy and the - * export hierarchy. The former is the parent/child structure in Blender, which can have multiple - * parent-like objects. For example, a duplicated object can have both a duplicator and a parent, - * both determining the final transform. The export hierarchy is the hierarchy as written to the - * file, and every object has only one export-parent. - * - * Currently the AbstractHierarchyIterator does not make any decisions about *what* to export. - * Selections like "selected only" or "no hair systems" are left to concrete subclasses. - */ - -#ifndef __ABSTRACT_HIERARCHY_ITERATOR_H__ -#define __ABSTRACT_HIERARCHY_ITERATOR_H__ - -#include -#include -#include - -struct Base; -struct Depsgraph; -struct DupliObject; -struct ID; -struct Object; -struct ParticleSystem; -struct ViewLayer; - -namespace USD { - -class AbstractHierarchyWriter; - -/* HierarchyContext structs are created by the AbstractHierarchyIterator. Each HierarchyContext - * struct contains everything necessary to export a single object to a file. */ -struct HierarchyContext { - /*********** Determined during hierarchy iteration: ***************/ - Object *object; - Object *export_parent; - Object *duplicator; - float matrix_world[4][4]; - std::string export_name; - - /* When weak_export=true, the object will be exported only as transform, and only if is an - * ancestor of an object with weak_export=false. - * - * In other words: when weak_export=true but this object has no children, or all decendants also - * have weak_export=true, this object (and by recursive reasoning all its decendants) will be - * excluded from the export. - * - * The export hierarchy is kept as close to the the hierarchy in Blender as possible. As such, an - * object that serves as a parent for another object, but which should NOT be exported itself, is - * exported only as transform (i.e. as empty). This happens with objects that are part of a - * holdout collection (which prevents them from being exported) but also parent of an exported - * object. */ - bool weak_export; - - /* When true, this object should check its parents for animation data when determining whether - * it's animated. This is necessary when a parent object in Blender is not part of the export. */ - bool animation_check_include_parent; - - /*********** Determined during writer creation: ***************/ - float parent_matrix_inv_world[4][4]; // Inverse of the parent's world matrix. - std::string export_path; // Hierarchical path, such as "/grandparent/parent/objectname". - ParticleSystem *particle_system; // Only set for particle/hair writers. - - /* Hierarchical path of the object this object is duplicating; only set when this object should - * be stored as a reference to its original. It can happen that the original is not part of the - * exported objects, in which case this string is empty even though 'duplicator' is set. */ - std::string original_export_path; - - bool operator<(const HierarchyContext &other) const; - - /* Return a HierarchyContext representing the root of the export hierarchy. */ - static const HierarchyContext *root(); - - /* For handling instanced collections, instances created by particles, etc. */ - bool is_instance() const; - void mark_as_instance_of(const std::string &reference_export_path); - void mark_as_not_instanced(); -}; - -/* Abstract writer for objects. Create concrete subclasses to write to USD, Alembic, etc. - * - * Instantiated by the AbstractHierarchyIterator on the first frame an object exists. Generally - * that's the first frame to be exported, but can be later, for example when objects are - * instantiated by particles. The AbstractHierarchyWriter::write() function is called on every - * frame the object exists in the dependency graph and should be exported. - */ -class AbstractHierarchyWriter { - public: - virtual ~AbstractHierarchyWriter(); - virtual void write(HierarchyContext &context) = 0; - // TODO(Sybren): add function like absent() that's called when a writer was previously created, - // but wasn't used while exporting the current frame (for example, a particle-instanced mesh of - // which the particle is no longer alive). -}; - -/* AbstractHierarchyIterator iterates over objects in a dependency graph, and constructs export - * writers. These writers are then called to perform the actual writing to a USD or Alembic file. - * - * Dealing with file- and scene-level data (for example, creating a USD scene, setting the frame - * rate, etc.) is not part of the AbstractHierarchyIterator class structure, and should be done - * in separate code. - */ -class AbstractHierarchyIterator { - public: - /* Mapping from export path to writer. */ - typedef std::map WriterMap; - /* Pair of a (potentially duplicated) object and its duplicator (or nullptr). - * This is typically used to store a pair of HierarchyContext::object and - * HierarchyContext::duplicator. */ - typedef std::pair DupliAndDuplicator; - /* All the children of some object, as per the export hierarchy. */ - typedef std::set ExportChildren; - /* Mapping from an object and its duplicator to the object's export-children. */ - typedef std::map ExportGraph; - /* Mapping from ID to its export path. This is used for instancing; given an - * instanced datablock, the export path of the original can be looked up. */ - typedef std::map ExportPathMap; - - protected: - ExportGraph export_graph_; - ExportPathMap duplisource_export_path_; - Depsgraph *depsgraph_; - WriterMap writers_; - - public: - explicit AbstractHierarchyIterator(Depsgraph *depsgraph); - virtual ~AbstractHierarchyIterator(); - - /* Iterate over the depsgraph, create writers, and tell the writers to write. - * Main entry point for the AbstractHierarchyIterator, must be called for every to-be-exported - * frame. */ - void iterate_and_write(); - - /* Release all writers. Call after all frames have been exported. */ - void release_writers(); - - /* Convert the given name to something that is valid for the exported file format. - * This base implementation is a no-op; override in a concrete subclass. */ - virtual std::string make_valid_name(const std::string &name) const; - - /* Return the name of this ID datablock that is valid for the exported file format. Overriding is - * only necessary if make_valid_name(id->name+2) is not suitable for the exported file format. - * NULL-safe: when `id == nullptr` this returns an empty string. */ - virtual std::string get_id_name(const ID *id) const; - - /* Given a HierarchyContext of some Object *, return an export path that is valid for its - * object->data. Overriding is necessary when the exported format does NOT expect the object's - * data to be a child of the object. */ - virtual std::string get_object_data_path(const HierarchyContext *context) const; - - private: - void debug_print_export_graph(const ExportGraph &graph) const; - - void export_graph_construct(); - void connect_loose_objects(); - void export_graph_prune(); - void export_graph_clear(); - - void visit_object(Object *object, Object *export_parent, bool weak_export); - void visit_dupli_object(DupliObject *dupli_object, - Object *duplicator, - const std::set &dupli_set); - - ExportChildren &graph_children(const HierarchyContext *parent_context); - - void determine_export_paths(const HierarchyContext *parent_context); - void determine_duplication_references(const HierarchyContext *parent_context, - std::string indent); - - void make_writers(const HierarchyContext *parent_context); - void make_writer_object_data(const HierarchyContext *context); - void make_writers_particle_systems(const HierarchyContext *context); - - /* Convenience wrappers around get_id_name(). */ - std::string get_object_name(const Object *object) const; - std::string get_object_data_name(const Object *object) const; - - AbstractHierarchyWriter *get_writer(const std::string &export_path); - - typedef AbstractHierarchyWriter *(AbstractHierarchyIterator::*create_writer_func)( - const HierarchyContext *); - /* Ensure that a writer exists; if it doesn't, call create_func(context). The create_func - * function should be one of the create_XXXX_writer(context) functions declared below. */ - AbstractHierarchyWriter *ensure_writer(HierarchyContext *context, - create_writer_func create_func); - - protected: - /* Construct a valid path for the export file format. This class concatenates by using '/' as a - * path separator, which is valid for both Alembic and USD. */ - virtual std::string path_concatenate(const std::string &parent_path, - const std::string &child_path) const; - - /* Return whether this object should be marked as 'weak export' or not. - * - * When this returns false, writers for the transform and data are created, - * and dupli-objects dupli-object generated from this object will be passed to - * should_visit_dupli_object(). - * - * When this returns true, only a transform writer is created and marked as - * 'weak export'. In this case, the transform writer will be removed before - * exporting starts, unless a decendant of this object is to be exported. - * Dupli-object generated from this object will also be skipped. - * - * See HierarchyContext::weak_export. - */ - virtual bool mark_as_weak_export(const Object *object) const; - - virtual bool should_visit_dupli_object(const DupliObject *dupli_object) const; - - /* These functions should create an AbstractHierarchyWriter subclass instance, or return - * nullptr if the object or its data should not be exported. Returning a nullptr for - * data/hair/particle will NOT prevent the transform to be written. - * - * The returned writer is owned by the AbstractHierarchyWriter, and should be freed in - * delete_object_writer(). */ - virtual AbstractHierarchyWriter *create_transform_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) = 0; - virtual AbstractHierarchyWriter *create_particle_writer(const HierarchyContext *context) = 0; - - /* Called by release_writers() to free what the create_XXX_writer() functions allocated. */ - virtual void delete_object_writer(AbstractHierarchyWriter *writer) = 0; -}; - -} // namespace USD - -#endif /* __ABSTRACT_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/usd/intern/usd_capi.cc b/source/blender/usd/intern/usd_capi.cc deleted file mode 100644 index 83e11cd7bf3..00000000000 --- a/source/blender/usd/intern/usd_capi.cc +++ /dev/null @@ -1,233 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ - -#include "usd.h" -#include "usd_hierarchy_iterator.h" - -#include -#include -#include - -#include "MEM_guardedalloc.h" - -extern "C" { -#include "DEG_depsgraph.h" -#include "DEG_depsgraph_build.h" -#include "DEG_depsgraph_query.h" - -#include "DNA_scene_types.h" - -#include "BKE_blender_version.h" -#include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_scene.h" - -#include "BLI_fileops.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "WM_api.h" -#include "WM_types.h" -} - -namespace USD { - -struct ExportJobData { - ViewLayer *view_layer; - Main *bmain; - Depsgraph *depsgraph; - wmWindowManager *wm; - - char filename[FILE_MAX]; - USDExportParams params; - - short *stop; - short *do_update; - float *progress; - - bool was_canceled; - bool export_ok; -}; - -static void export_startjob(void *customdata, short *stop, short *do_update, float *progress) -{ - ExportJobData *data = static_cast(customdata); - - data->stop = stop; - data->do_update = do_update; - data->progress = progress; - data->was_canceled = false; - - G.is_rendering = true; - WM_set_locked_interface(data->wm, true); - G.is_break = false; - - // Construct the depsgraph for exporting. - Scene *scene = DEG_get_input_scene(data->depsgraph); - ViewLayer *view_layer = DEG_get_input_view_layer(data->depsgraph); - DEG_graph_build_from_view_layer(data->depsgraph, data->bmain, scene, view_layer); - BKE_scene_graph_update_tagged(data->depsgraph, data->bmain); - - *progress = 0.0f; - *do_update = true; - - // For restoring the current frame after exporting animation is done. - const int orig_frame = CFRA; - - pxr::UsdStageRefPtr usd_stage = pxr::UsdStage::CreateNew(data->filename); - if (!usd_stage) { - /* This happens when the USD JSON files cannot be found. When that happens, - * the USD library doesn't know it has the functionality to write USDA and - * USDC files, and creating a new UsdStage fails. */ - WM_reportf( - RPT_ERROR, "USD Export: unable to find suitable USD plugin to write %s", data->filename); - data->export_ok = false; - return; - } - - usd_stage->SetMetadata(pxr::UsdGeomTokens->upAxis, pxr::VtValue(pxr::UsdGeomTokens->z)); - usd_stage->SetMetadata(pxr::UsdGeomTokens->metersPerUnit, - pxr::VtValue(scene->unit.scale_length)); - usd_stage->GetRootLayer()->SetDocumentation(std::string("Blender ") + versionstr); - - // Set up the stage for animated data. - if (data->params.export_animation) { - usd_stage->SetTimeCodesPerSecond(FPS); - usd_stage->SetStartTimeCode(scene->r.sfra); - usd_stage->SetEndTimeCode(scene->r.efra); - } - - USDHierarchyIterator iter(data->depsgraph, usd_stage, data->params); - - if (data->params.export_animation) { - // Writing the animated frames is not 100% of the work, but it's our best guess. - float progress_per_frame = 1.0f / std::max(1, (scene->r.efra - scene->r.sfra + 1)); - - for (float frame = scene->r.sfra; frame <= scene->r.efra; frame++) { - if (G.is_break || (stop != nullptr && *stop)) { - break; - } - - // Update the scene for the next frame to render. - scene->r.cfra = static_cast(frame); - scene->r.subframe = frame - scene->r.cfra; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); - - iter.set_export_frame(frame); - iter.iterate_and_write(); - - *progress += progress_per_frame; - *do_update = true; - } - } - else { - // If we're not animating, a single iteration over all objects is enough. - iter.iterate_and_write(); - } - - iter.release_writers(); - usd_stage->GetRootLayer()->Save(); - - // Finish up by going back to the keyframe that was current before we started. - if (CFRA != orig_frame) { - CFRA = orig_frame; - BKE_scene_graph_update_for_newframe(data->depsgraph, data->bmain); - } - - data->export_ok = !data->was_canceled; - - *progress = 1.0f; - *do_update = true; -} - -static void export_endjob(void *customdata) -{ - ExportJobData *data = static_cast(customdata); - - DEG_graph_free(data->depsgraph); - - if (data->was_canceled && BLI_exists(data->filename)) { - BLI_delete(data->filename, false, false); - } - - G.is_rendering = false; - WM_set_locked_interface(data->wm, false); -} - -} // namespace USD - -bool USD_export(bContext *C, - const char *filepath, - const USDExportParams *params, - bool as_background_job) -{ - ViewLayer *view_layer = CTX_data_view_layer(C); - Scene *scene = CTX_data_scene(C); - - USD::ExportJobData *job = static_cast( - MEM_mallocN(sizeof(USD::ExportJobData), "ExportJobData")); - - job->bmain = CTX_data_main(C); - job->wm = CTX_wm_manager(C); - job->export_ok = false; - BLI_strncpy(job->filename, filepath, sizeof(job->filename)); - - job->depsgraph = DEG_graph_new(job->bmain, scene, view_layer, params->evaluation_mode); - job->params = *params; - - bool export_ok = false; - if (as_background_job) { - wmJob *wm_job = WM_jobs_get( - job->wm, CTX_wm_window(C), scene, "USD Export", WM_JOB_PROGRESS, WM_JOB_TYPE_ALEMBIC); - - /* setup job */ - WM_jobs_customdata_set(wm_job, job, MEM_freeN); - WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_FRAME, NC_SCENE | ND_FRAME); - WM_jobs_callbacks(wm_job, USD::export_startjob, NULL, NULL, USD::export_endjob); - - WM_jobs_start(CTX_wm_manager(C), wm_job); - } - else { - /* Fake a job context, so that we don't need NULL pointer checks while exporting. */ - short stop = 0, do_update = 0; - float progress = 0.f; - - USD::export_startjob(job, &stop, &do_update, &progress); - USD::export_endjob(job); - export_ok = job->export_ok; - - MEM_freeN(job); - } - - return export_ok; -} - -int USD_get_version(void) -{ - /* USD 19.11 defines: - * - * #define PXR_MAJOR_VERSION 0 - * #define PXR_MINOR_VERSION 19 - * #define PXR_PATCH_VERSION 11 - * #define PXR_VERSION 1911 - * - * So the major version is implicit/invisible in the public version number. - */ - return PXR_VERSION; -} diff --git a/source/blender/usd/intern/usd_exporter_context.h b/source/blender/usd/intern/usd_exporter_context.h deleted file mode 100644 index 4ae415b3d34..00000000000 --- a/source/blender/usd/intern/usd_exporter_context.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_EXPORTER_CONTEXT_H__ -#define __USD_EXPORTER_CONTEXT_H__ - -#include "usd.h" - -#include -#include - -struct Depsgraph; -struct Object; - -namespace USD { - -class USDHierarchyIterator; - -struct USDExporterContext { - Depsgraph *depsgraph; - const pxr::UsdStageRefPtr stage; - const pxr::SdfPath usd_path; - const USDHierarchyIterator *hierarchy_iterator; - const USDExportParams &export_params; -}; - -} // namespace USD - -#endif /* __USD_EXPORTER_CONTEXT_H__ */ diff --git a/source/blender/usd/intern/usd_hierarchy_iterator.cc b/source/blender/usd/intern/usd_hierarchy_iterator.cc deleted file mode 100644 index fd888f39adc..00000000000 --- a/source/blender/usd/intern/usd_hierarchy_iterator.cc +++ /dev/null @@ -1,150 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd.h" - -#include "usd_hierarchy_iterator.h" -#include "usd_writer_abstract.h" -#include "usd_writer_camera.h" -#include "usd_writer_hair.h" -#include "usd_writer_light.h" -#include "usd_writer_mesh.h" -#include "usd_writer_metaball.h" -#include "usd_writer_transform.h" - -#include - -#include - -extern "C" { -#include "BKE_anim.h" - -#include "BLI_assert.h" - -#include "DEG_depsgraph_query.h" - -#include "DNA_ID.h" -#include "DNA_layer_types.h" -#include "DNA_object_types.h" -} - -namespace USD { - -USDHierarchyIterator::USDHierarchyIterator(Depsgraph *depsgraph, - pxr::UsdStageRefPtr stage, - const USDExportParams ¶ms) - : AbstractHierarchyIterator(depsgraph), stage_(stage), params_(params) -{ -} - -bool USDHierarchyIterator::mark_as_weak_export(const Object *object) const -{ - if (params_.selected_objects_only && (object->base_flag & BASE_SELECTED) == 0) { - return true; - } - return false; -} - -void USDHierarchyIterator::delete_object_writer(AbstractHierarchyWriter *writer) -{ - delete static_cast(writer); -} - -std::string USDHierarchyIterator::make_valid_name(const std::string &name) const -{ - return pxr::TfMakeValidIdentifier(name); -} - -void USDHierarchyIterator::set_export_frame(float frame_nr) -{ - // The USD stage is already set up to have FPS timecodes per frame. - export_time_ = pxr::UsdTimeCode(frame_nr); -} - -const pxr::UsdTimeCode &USDHierarchyIterator::get_export_time_code() const -{ - return export_time_; -} - -USDExporterContext USDHierarchyIterator::create_usd_export_context(const HierarchyContext *context) -{ - return USDExporterContext{depsgraph_, stage_, pxr::SdfPath(context->export_path), this, params_}; -} - -AbstractHierarchyWriter *USDHierarchyIterator::create_transform_writer( - const HierarchyContext *context) -{ - return new USDTransformWriter(create_usd_export_context(context)); -} - -AbstractHierarchyWriter *USDHierarchyIterator::create_data_writer(const HierarchyContext *context) -{ - USDExporterContext usd_export_context = create_usd_export_context(context); - USDAbstractWriter *data_writer = nullptr; - - switch (context->object->type) { - case OB_MESH: - data_writer = new USDMeshWriter(usd_export_context); - break; - case OB_CAMERA: - data_writer = new USDCameraWriter(usd_export_context); - break; - case OB_LAMP: - data_writer = new USDLightWriter(usd_export_context); - break; - case OB_MBALL: - data_writer = new USDMetaballWriter(usd_export_context); - break; - - case OB_EMPTY: - case OB_CURVE: - case OB_SURF: - case OB_FONT: - case OB_SPEAKER: - case OB_LIGHTPROBE: - case OB_LATTICE: - case OB_ARMATURE: - case OB_GPENCIL: - return nullptr; - case OB_TYPE_MAX: - BLI_assert(!"OB_TYPE_MAX should not be used"); - return nullptr; - } - - if (!data_writer->is_supported(context)) { - delete data_writer; - return nullptr; - } - - return data_writer; -} - -AbstractHierarchyWriter *USDHierarchyIterator::create_hair_writer(const HierarchyContext *context) -{ - if (!params_.export_hair) { - return nullptr; - } - return new USDHairWriter(create_usd_export_context(context)); -} - -AbstractHierarchyWriter *USDHierarchyIterator::create_particle_writer(const HierarchyContext *) -{ - return nullptr; -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_hierarchy_iterator.h b/source/blender/usd/intern/usd_hierarchy_iterator.h deleted file mode 100644 index 90c82c6e551..00000000000 --- a/source/blender/usd/intern/usd_hierarchy_iterator.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_HIERARCHY_ITERATOR_H__ -#define __USD_HIERARCHY_ITERATOR_H__ - -#include "abstract_hierarchy_iterator.h" -#include "usd_exporter_context.h" -#include "usd.h" - -#include - -#include -#include - -struct Depsgraph; -struct ID; -struct Object; - -namespace USD { - -class USDHierarchyIterator : public AbstractHierarchyIterator { - private: - const pxr::UsdStageRefPtr stage_; - pxr::UsdTimeCode export_time_; - const USDExportParams ¶ms_; - - public: - USDHierarchyIterator(Depsgraph *depsgraph, - pxr::UsdStageRefPtr stage, - const USDExportParams ¶ms); - - void set_export_frame(float frame_nr); - const pxr::UsdTimeCode &get_export_time_code() const; - - virtual std::string make_valid_name(const std::string &name) const override; - - protected: - virtual bool mark_as_weak_export(const Object *object) const override; - - virtual AbstractHierarchyWriter *create_transform_writer( - const HierarchyContext *context) override; - virtual AbstractHierarchyWriter *create_data_writer(const HierarchyContext *context) override; - virtual AbstractHierarchyWriter *create_hair_writer(const HierarchyContext *context) override; - virtual AbstractHierarchyWriter *create_particle_writer( - const HierarchyContext *context) override; - - virtual void delete_object_writer(AbstractHierarchyWriter *writer) override; - - private: - USDExporterContext create_usd_export_context(const HierarchyContext *context); -}; - -} // namespace USD - -#endif /* __USD_HIERARCHY_ITERATOR_H__ */ diff --git a/source/blender/usd/intern/usd_writer_abstract.cc b/source/blender/usd/intern/usd_writer_abstract.cc deleted file mode 100644 index 4d0b4364fb5..00000000000 --- a/source/blender/usd/intern/usd_writer_abstract.cc +++ /dev/null @@ -1,147 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_abstract.h" -#include "usd_hierarchy_iterator.h" - -#include - -extern "C" { -#include "BKE_animsys.h" -#include "BKE_key.h" - -#include "DNA_modifier_types.h" -} - -/* TfToken objects are not cheap to construct, so we do it once. */ -namespace usdtokens { -// Materials -static const pxr::TfToken diffuse_color("diffuseColor", pxr::TfToken::Immortal); -static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal); -static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal); -static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal); -static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal); -static const pxr::TfToken surface("surface", pxr::TfToken::Immortal); -} // namespace usdtokens - -namespace USD { - -USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context) - : usd_export_context_(usd_export_context), - usd_value_writer_(), - frame_has_been_written_(false), - is_animated_(false) -{ -} - -USDAbstractWriter::~USDAbstractWriter() -{ -} - -bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const -{ - return true; -} - -pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const -{ - if (is_animated_) { - return usd_export_context_.hierarchy_iterator->get_export_time_code(); - } - // By using the default timecode USD won't even write a single `timeSample` for non-animated - // data. Instead, it writes it as non-timesampled. - static pxr::UsdTimeCode default_timecode = pxr::UsdTimeCode::Default(); - return default_timecode; -} - -void USDAbstractWriter::write(HierarchyContext &context) -{ - if (!frame_has_been_written_) { - is_animated_ = usd_export_context_.export_params.export_animation && - check_is_animated(context); - } - else if (!is_animated_) { - /* A frame has already been written, and without animation one frame is enough. */ - return; - } - - do_write(context); - - frame_has_been_written_ = true; -} - -bool USDAbstractWriter::check_is_animated(const HierarchyContext &context) const -{ - const Object *object = context.object; - - if (BKE_animdata_id_is_animated(static_cast(object->data))) { - return true; - } - if (BKE_key_from_object(object) != nullptr) { - return true; - } - - /* Test modifiers. */ - /* TODO(Sybren): replace this with a check on the depsgraph to properly check for dependency on - * time. */ - ModifierData *md = static_cast(object->modifiers.first); - while (md) { - if (md->type != eModifierType_Subsurf) { - return true; - } - md = md->next; - } - - return false; -} - -const pxr::SdfPath &USDAbstractWriter::usd_path() const -{ - return usd_export_context_.usd_path; -} - -pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material) -{ - static pxr::SdfPath material_library_path("/_materials"); - pxr::UsdStageRefPtr stage = usd_export_context_.stage; - - // Construct the material. - pxr::TfToken material_name(usd_export_context_.hierarchy_iterator->get_id_name(&material->id)); - pxr::SdfPath usd_path = material_library_path.AppendChild(material_name); - pxr::UsdShadeMaterial usd_material = pxr::UsdShadeMaterial::Get(stage, usd_path); - if (usd_material) { - return usd_material; - } - usd_material = pxr::UsdShadeMaterial::Define(stage, usd_path); - - // Construct the shader. - pxr::SdfPath shader_path = usd_path.AppendChild(usdtokens::preview_shader); - pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(stage, shader_path); - shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface)); - shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f) - .Set(pxr::GfVec3f(material->r, material->g, material->b)); - shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness); - shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic); - - // Connect the shader and the material together. - usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface); - - return usd_material; -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_abstract.h b/source/blender/usd/intern/usd_writer_abstract.h deleted file mode 100644 index 835d3a42c80..00000000000 --- a/source/blender/usd/intern/usd_writer_abstract.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_ABSTRACT_H__ -#define __USD_WRITER_ABSTRACT_H__ - -#include "usd_exporter_context.h" -#include "abstract_hierarchy_iterator.h" - -#include -#include -#include -#include - -#include - -extern "C" { -#include "DEG_depsgraph_query.h" -#include "DNA_material_types.h" -} - -struct Material; -struct Object; - -namespace USD { - -class USDAbstractWriter : public AbstractHierarchyWriter { - protected: - const USDExporterContext usd_export_context_; - pxr::UsdUtilsSparseValueWriter usd_value_writer_; - - bool frame_has_been_written_; - bool is_animated_; - - public: - USDAbstractWriter(const USDExporterContext &usd_export_context); - virtual ~USDAbstractWriter(); - - virtual void write(HierarchyContext &context) override; - - /* Returns true if the data to be written is actually supported. This would, for example, allow a - * hypothetical camera writer accept a perspective camera but reject an orthogonal one. - * - * Returning false from a transform writer will prevent the object and all its decendants from - * being exported. Returning false from a data writer (object data, hair, or particles) will - * only prevent that data from being written (and thus cause the object to be exported as an - * Empty). */ - virtual bool is_supported(const HierarchyContext *context) const; - - const pxr::SdfPath &usd_path() const; - - protected: - virtual void do_write(HierarchyContext &context) = 0; - virtual bool check_is_animated(const HierarchyContext &context) const; - pxr::UsdTimeCode get_export_time_code() const; - - pxr::UsdShadeMaterial ensure_usd_material(Material *material); -}; - -} // namespace USD - -#endif /* __USD_WRITER_ABSTRACT_H__ */ diff --git a/source/blender/usd/intern/usd_writer_camera.cc b/source/blender/usd/intern/usd_writer_camera.cc deleted file mode 100644 index 9b85d69559c..00000000000 --- a/source/blender/usd/intern/usd_writer_camera.cc +++ /dev/null @@ -1,111 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_camera.h" -#include "usd_hierarchy_iterator.h" - -#include -#include - -extern "C" { -#include "BKE_camera.h" -#include "BLI_assert.h" - -#include "DNA_camera_types.h" -#include "DNA_scene_types.h" -} - -namespace USD { - -USDCameraWriter::USDCameraWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) -{ -} - -bool USDCameraWriter::is_supported(const HierarchyContext *context) const -{ - Camera *camera = static_cast(context->object->data); - return camera->type == CAM_PERSP; -} - -static void camera_sensor_size_for_render(const Camera *camera, - const struct RenderData *rd, - float *r_sensor_x, - float *r_sensor_y) -{ - /* Compute the final image size in pixels. */ - float sizex = rd->xsch * rd->xasp; - float sizey = rd->ysch * rd->yasp; - - int sensor_fit = BKE_camera_sensor_fit(camera->sensor_fit, sizex, sizey); - - switch (sensor_fit) { - case CAMERA_SENSOR_FIT_HOR: - *r_sensor_x = camera->sensor_x; - *r_sensor_y = camera->sensor_x * sizey / sizex; - break; - case CAMERA_SENSOR_FIT_VERT: - *r_sensor_x = camera->sensor_y * sizex / sizey; - *r_sensor_y = camera->sensor_y; - break; - case CAMERA_SENSOR_FIT_AUTO: - BLI_assert(!"Camera fit should be either horizontal or vertical"); - break; - } -} - -void USDCameraWriter::do_write(HierarchyContext &context) -{ - pxr::UsdTimeCode timecode = get_export_time_code(); - pxr::UsdGeomCamera usd_camera = pxr::UsdGeomCamera::Define(usd_export_context_.stage, - usd_export_context_.usd_path); - - Camera *camera = static_cast(context.object->data); - Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); - - usd_camera.CreateProjectionAttr().Set(pxr::UsdGeomTokens->perspective); - - /* USD stores the focal length in "millimeters or tenths of world units", because at some point - * they decided world units might be centimeters. Quite confusing, as the USD Viewer shows the - * correct FoV when we write millimeters and not "tenths of world units". - */ - usd_camera.CreateFocalLengthAttr().Set(camera->lens, timecode); - - float aperture_x, aperture_y; - camera_sensor_size_for_render(camera, &scene->r, &aperture_x, &aperture_y); - - float film_aspect = aperture_x / aperture_y; - usd_camera.CreateHorizontalApertureAttr().Set(aperture_x, timecode); - usd_camera.CreateVerticalApertureAttr().Set(aperture_y, timecode); - usd_camera.CreateHorizontalApertureOffsetAttr().Set(aperture_x * camera->shiftx, timecode); - usd_camera.CreateVerticalApertureOffsetAttr().Set(aperture_y * camera->shifty * film_aspect, - timecode); - - usd_camera.CreateClippingRangeAttr().Set( - pxr::VtValue(pxr::GfVec2f(camera->clip_start, camera->clip_end)), timecode); - - // Write DoF-related attributes. - if (camera->dof.flag & CAM_DOF_ENABLED) { - usd_camera.CreateFStopAttr().Set(camera->dof.aperture_fstop, timecode); - - float focus_distance = scene->unit.scale_length * - BKE_camera_object_dof_distance(context.object); - usd_camera.CreateFocusDistanceAttr().Set(focus_distance, timecode); - } -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_camera.h b/source/blender/usd/intern/usd_writer_camera.h deleted file mode 100644 index 971264ef11e..00000000000 --- a/source/blender/usd/intern/usd_writer_camera.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_CAMERA_H__ -#define __USD_WRITER_CAMERA_H__ - -#include "usd_writer_abstract.h" - -namespace USD { - -/* Writer for writing camera data to UsdGeomCamera. */ -class USDCameraWriter : public USDAbstractWriter { - public: - USDCameraWriter(const USDExporterContext &ctx); - - protected: - virtual bool is_supported(const HierarchyContext *context) const override; - virtual void do_write(HierarchyContext &context) override; -}; - -} // namespace USD - -#endif /* __USD_WRITER_CAMERA_H__ */ diff --git a/source/blender/usd/intern/usd_writer_hair.cc b/source/blender/usd/intern/usd_writer_hair.cc deleted file mode 100644 index 9251425c0b8..00000000000 --- a/source/blender/usd/intern/usd_writer_hair.cc +++ /dev/null @@ -1,90 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_hair.h" -#include "usd_hierarchy_iterator.h" - -#include -#include - -extern "C" { -#include "BKE_particle.h" - -#include "DNA_particle_types.h" -} - -namespace USD { - -USDHairWriter::USDHairWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) -{ -} - -void USDHairWriter::do_write(HierarchyContext &context) -{ - ParticleSystem *psys = context.particle_system; - ParticleCacheKey **cache = psys->pathcache; - if (cache == nullptr) { - return; - } - - pxr::UsdTimeCode timecode = get_export_time_code(); - pxr::UsdGeomBasisCurves curves = pxr::UsdGeomBasisCurves::Define(usd_export_context_.stage, - usd_export_context_.usd_path); - - // TODO(Sybren): deal with (psys->part->flag & PART_HAIR_BSPLINE) - curves.CreateBasisAttr(pxr::VtValue(pxr::UsdGeomTokens->bspline)); - curves.CreateTypeAttr(pxr::VtValue(pxr::UsdGeomTokens->cubic)); - - pxr::VtArray points; - pxr::VtIntArray curve_point_counts; - curve_point_counts.reserve(psys->totpart); - - ParticleCacheKey *strand; - for (int strand_index = 0; strand_index < psys->totpart; ++strand_index) { - strand = cache[strand_index]; - - int point_count = strand->segments + 1; - curve_point_counts.push_back(point_count); - - for (int point_index = 0; point_index < point_count; ++point_index, ++strand) { - points.push_back(pxr::GfVec3f(strand->co)); - } - } - - pxr::UsdAttribute attr_points = curves.CreatePointsAttr(pxr::VtValue(), true); - pxr::UsdAttribute attr_vertex_counts = curves.CreateCurveVertexCountsAttr(pxr::VtValue(), true); - if (!attr_points.HasValue()) { - attr_points.Set(points, pxr::UsdTimeCode::Default()); - attr_vertex_counts.Set(curve_point_counts, pxr::UsdTimeCode::Default()); - } - usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(points), timecode); - usd_value_writer_.SetAttribute(attr_vertex_counts, pxr::VtValue(curve_point_counts), timecode); - - if (psys->totpart > 0) { - pxr::VtArray colors; - colors.push_back(pxr::GfVec3f(cache[0]->col)); - curves.CreateDisplayColorAttr(pxr::VtValue(colors)); - } -} - -bool USDHairWriter::check_is_animated(const HierarchyContext &) const -{ - return true; -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_hair.h b/source/blender/usd/intern/usd_writer_hair.h deleted file mode 100644 index 1e882fa1654..00000000000 --- a/source/blender/usd/intern/usd_writer_hair.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_HAIR_H__ -#define __USD_WRITER_HAIR_H__ - -#include "usd_writer_abstract.h" - -namespace USD { - -/* Writer for writing hair particle data as USD curves. */ -class USDHairWriter : public USDAbstractWriter { - public: - USDHairWriter(const USDExporterContext &ctx); - - protected: - virtual void do_write(HierarchyContext &context) override; - virtual bool check_is_animated(const HierarchyContext &context) const override; -}; - -} // namespace USD - -#endif /* __USD_WRITER_HAIR_H__ */ diff --git a/source/blender/usd/intern/usd_writer_light.cc b/source/blender/usd/intern/usd_writer_light.cc deleted file mode 100644 index e13e2c58a79..00000000000 --- a/source/blender/usd/intern/usd_writer_light.cc +++ /dev/null @@ -1,112 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_light.h" -#include "usd_hierarchy_iterator.h" - -#include -#include -#include -#include - -extern "C" { -#include "BLI_assert.h" -#include "BLI_utildefines.h" - -#include "DNA_light_types.h" -#include "DNA_object_types.h" -} - -namespace USD { - -USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) -{ -} - -bool USDLightWriter::is_supported(const HierarchyContext *context) const -{ - Light *light = static_cast(context->object->data); - return ELEM(light->type, LA_AREA, LA_LOCAL, LA_SUN); -} - -void USDLightWriter::do_write(HierarchyContext &context) -{ - pxr::UsdStageRefPtr stage = usd_export_context_.stage; - const pxr::SdfPath &usd_path = usd_export_context_.usd_path; - pxr::UsdTimeCode timecode = get_export_time_code(); - - Light *light = static_cast(context.object->data); - pxr::UsdLuxLight usd_light; - - switch (light->type) { - case LA_AREA: - switch (light->area_shape) { - case LA_AREA_DISK: - case LA_AREA_ELLIPSE: { /* An ellipse light will deteriorate into a disk light. */ - pxr::UsdLuxDiskLight disk_light = pxr::UsdLuxDiskLight::Define(stage, usd_path); - disk_light.CreateRadiusAttr().Set(light->area_size, timecode); - usd_light = disk_light; - break; - } - case LA_AREA_RECT: { - pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path); - rect_light.CreateWidthAttr().Set(light->area_size, timecode); - rect_light.CreateHeightAttr().Set(light->area_sizey, timecode); - usd_light = rect_light; - break; - } - case LA_AREA_SQUARE: { - pxr::UsdLuxRectLight rect_light = pxr::UsdLuxRectLight::Define(stage, usd_path); - rect_light.CreateWidthAttr().Set(light->area_size, timecode); - rect_light.CreateHeightAttr().Set(light->area_size, timecode); - usd_light = rect_light; - break; - } - } - break; - case LA_LOCAL: { - pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path); - sphere_light.CreateRadiusAttr().Set(light->area_size, timecode); - usd_light = sphere_light; - break; - } - case LA_SUN: - usd_light = pxr::UsdLuxDistantLight::Define(stage, usd_path); - break; - default: - BLI_assert(!"is_supported() returned true for unsupported light type"); - } - - /* Scale factor to get to somewhat-similar illumination. Since the USDViewer had similar - * over-exposure as Blender Internal with the same values, this code applies the reverse of the - * versioning code in light_emission_unify(). */ - float usd_intensity; - if (light->type == LA_SUN) { - /* Untested, as the Hydra GL viewport of USDViewer doesn't support distant lights. */ - usd_intensity = light->energy; - } - else { - usd_intensity = light->energy / 100.f; - } - usd_light.CreateIntensityAttr().Set(usd_intensity, timecode); - - usd_light.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode); - usd_light.CreateSpecularAttr().Set(light->spec_fac, timecode); -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_light.h b/source/blender/usd/intern/usd_writer_light.h deleted file mode 100644 index 349c034b6bc..00000000000 --- a/source/blender/usd/intern/usd_writer_light.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_LIGHT_H__ -#define __USD_WRITER_LIGHT_H__ - -#include "usd_writer_abstract.h" - -namespace USD { - -class USDLightWriter : public USDAbstractWriter { - public: - USDLightWriter(const USDExporterContext &ctx); - - protected: - virtual bool is_supported(const HierarchyContext *context) const override; - virtual void do_write(HierarchyContext &context) override; -}; - -} // namespace USD - -#endif /* __USD_WRITER_LIGHT_H__ */ diff --git a/source/blender/usd/intern/usd_writer_mesh.cc b/source/blender/usd/intern/usd_writer_mesh.cc deleted file mode 100644 index 74005afaf31..00000000000 --- a/source/blender/usd/intern/usd_writer_mesh.cc +++ /dev/null @@ -1,489 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_mesh.h" -#include "usd_hierarchy_iterator.h" - -#include -#include -#include - -extern "C" { -#include "BLI_assert.h" -#include "BLI_math_vector.h" - -#include "BKE_anim.h" -#include "BKE_customdata.h" -#include "BKE_lib_id.h" -#include "BKE_material.h" -#include "BKE_mesh.h" -#include "BKE_modifier.h" -#include "BKE_object.h" - -#include "DEG_depsgraph.h" - -#include "DNA_layer_types.h" -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" -#include "DNA_modifier_types.h" -#include "DNA_object_fluidsim_types.h" -#include "DNA_particle_types.h" -} - -namespace USD { - -USDGenericMeshWriter::USDGenericMeshWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) -{ -} - -bool USDGenericMeshWriter::is_supported(const HierarchyContext *context) const -{ - Object *object = context->object; - bool is_dupli = context->duplicator != nullptr; - int base_flag; - - if (is_dupli) { - /* Construct the object's base flags from its dupliparent, just like is done in - * deg_objects_dupli_iterator_next(). Without this, the visibility check below will fail. Doing - * this here, instead of a more suitable location in AbstractHierarchyIterator, prevents - * copying the Object for every dupli. */ - base_flag = object->base_flag; - object->base_flag = context->duplicator->base_flag | BASE_FROM_DUPLI; - } - - int visibility = BKE_object_visibility(object, - usd_export_context_.export_params.evaluation_mode); - - if (is_dupli) { - object->base_flag = base_flag; - } - - return (visibility & OB_VISIBLE_SELF) != 0; -} - -void USDGenericMeshWriter::do_write(HierarchyContext &context) -{ - Object *object_eval = context.object; - bool needsfree = false; - Mesh *mesh = get_export_mesh(object_eval, needsfree); - - if (mesh == NULL) { - return; - } - - try { - write_mesh(context, mesh); - - if (needsfree) { - free_export_mesh(mesh); - } - } - catch (...) { - if (needsfree) { - free_export_mesh(mesh); - } - throw; - } -} - -void USDGenericMeshWriter::free_export_mesh(Mesh *mesh) -{ - BKE_id_free(NULL, mesh); -} - -struct USDMeshData { - pxr::VtArray points; - pxr::VtIntArray face_vertex_counts; - pxr::VtIntArray face_indices; - std::map face_groups; - - /* The length of this array specifies the number of creases on the surface. Each element gives - * the number of (must be adjacent) vertices in each crease, whose indices are linearly laid out - * in the 'creaseIndices' attribute. Since each crease must be at least one edge long, each - * element of this array should be greater than one. */ - pxr::VtIntArray crease_lengths; - /* The indices of all vertices forming creased edges. The size of this array must be equal to the - * sum of all elements of the 'creaseLengths' attribute. */ - pxr::VtIntArray crease_vertex_indices; - /* The per-crease or per-edge sharpness for all creases (Usd.Mesh.SHARPNESS_INFINITE for a - * perfectly sharp crease). Since 'creaseLengths' encodes the number of vertices in each crease, - * the number of elements in this array will be either len(creaseLengths) or the sum over all X - * of (creaseLengths[X] - 1). Note that while the RI spec allows each crease to have either a - * single sharpness or a value per-edge, USD will encode either a single sharpness per crease on - * a mesh, or sharpnesses for all edges making up the creases on a mesh. */ - pxr::VtFloatArray crease_sharpnesses; -}; - -void USDGenericMeshWriter::write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) -{ - pxr::UsdTimeCode timecode = get_export_time_code(); - - const CustomData *ldata = &mesh->ldata; - for (int layer_idx = 0; layer_idx < ldata->totlayer; layer_idx++) { - const CustomDataLayer *layer = &ldata->layers[layer_idx]; - if (layer->type != CD_MLOOPUV) { - continue; - } - - /* UV coordinates are stored in a Primvar on the Mesh, and can be referenced from materials. - * The primvar name is the same as the UV Map name. This is to allow the standard name "st" - * for texture coordinates by naming the UV Map as such, without having to guess which UV Map - * is the "standard" one. */ - pxr::TfToken primvar_name(pxr::TfMakeValidIdentifier(layer->name)); - pxr::UsdGeomPrimvar uv_coords_primvar = usd_mesh.CreatePrimvar( - primvar_name, pxr::SdfValueTypeNames->TexCoord2fArray, pxr::UsdGeomTokens->faceVarying); - - MLoopUV *mloopuv = static_cast(layer->data); - pxr::VtArray uv_coords; - for (int loop_idx = 0; loop_idx < mesh->totloop; loop_idx++) { - uv_coords.push_back(pxr::GfVec2f(mloopuv[loop_idx].uv)); - } - - if (!uv_coords_primvar.HasValue()) { - uv_coords_primvar.Set(uv_coords, pxr::UsdTimeCode::Default()); - } - const pxr::UsdAttribute &uv_coords_attr = uv_coords_primvar.GetAttr(); - usd_value_writer_.SetAttribute(uv_coords_attr, pxr::VtValue(uv_coords), timecode); - } -} - -void USDGenericMeshWriter::write_mesh(HierarchyContext &context, Mesh *mesh) -{ - pxr::UsdTimeCode timecode = get_export_time_code(); - pxr::UsdTimeCode defaultTime = pxr::UsdTimeCode::Default(); - pxr::UsdStageRefPtr stage = usd_export_context_.stage; - const pxr::SdfPath &usd_path = usd_export_context_.usd_path; - - pxr::UsdGeomMesh usd_mesh = pxr::UsdGeomMesh::Define(stage, usd_path); - USDMeshData usd_mesh_data; - get_geometry_data(mesh, usd_mesh_data); - - if (usd_export_context_.export_params.use_instancing && context.is_instance()) { - // This object data is instanced, just reference the original instead of writing a copy. - if (context.export_path == context.original_export_path) { - printf("USD ref error: export path is reference path: %s\n", context.export_path.c_str()); - BLI_assert(!"USD reference error"); - return; - } - pxr::SdfPath ref_path(context.original_export_path); - if (!usd_mesh.GetPrim().GetReferences().AddInternalReference(ref_path)) { - /* See this URL for a description fo why referencing may fail" - * https://graphics.pixar.com/usd/docs/api/class_usd_references.html#Usd_Failing_References - */ - printf("USD Export warning: unable to add reference from %s to %s, not instancing object\n", - context.export_path.c_str(), - context.original_export_path.c_str()); - return; - } - /* The material path will be of the form , which is outside the - subtree pointed to by ref_path. As a result, the referenced data is not allowed to point out - of its own subtree. It does work when we override the material with exactly the same path, - though.*/ - if (usd_export_context_.export_params.export_materials) { - assign_materials(context, usd_mesh, usd_mesh_data.face_groups); - } - return; - } - - pxr::UsdAttribute attr_points = usd_mesh.CreatePointsAttr(pxr::VtValue(), true); - pxr::UsdAttribute attr_face_vertex_counts = usd_mesh.CreateFaceVertexCountsAttr(pxr::VtValue(), - true); - pxr::UsdAttribute attr_face_vertex_indices = usd_mesh.CreateFaceVertexIndicesAttr(pxr::VtValue(), - true); - - if (!attr_points.HasValue()) { - // Provide the initial value as default. This makes USD write the value as constant if they - // don't change over time. - attr_points.Set(usd_mesh_data.points, defaultTime); - attr_face_vertex_counts.Set(usd_mesh_data.face_vertex_counts, defaultTime); - attr_face_vertex_indices.Set(usd_mesh_data.face_indices, defaultTime); - } - - usd_value_writer_.SetAttribute(attr_points, pxr::VtValue(usd_mesh_data.points), timecode); - usd_value_writer_.SetAttribute( - attr_face_vertex_counts, pxr::VtValue(usd_mesh_data.face_vertex_counts), timecode); - usd_value_writer_.SetAttribute( - attr_face_vertex_indices, pxr::VtValue(usd_mesh_data.face_indices), timecode); - - if (!usd_mesh_data.crease_lengths.empty()) { - pxr::UsdAttribute attr_crease_lengths = usd_mesh.CreateCreaseLengthsAttr(pxr::VtValue(), true); - pxr::UsdAttribute attr_crease_indices = usd_mesh.CreateCreaseIndicesAttr(pxr::VtValue(), true); - pxr::UsdAttribute attr_crease_sharpness = usd_mesh.CreateCreaseSharpnessesAttr(pxr::VtValue(), - true); - - if (!attr_crease_lengths.HasValue()) { - attr_crease_lengths.Set(usd_mesh_data.crease_lengths, defaultTime); - attr_crease_indices.Set(usd_mesh_data.crease_vertex_indices, defaultTime); - attr_crease_sharpness.Set(usd_mesh_data.crease_sharpnesses, defaultTime); - } - - usd_value_writer_.SetAttribute( - attr_crease_lengths, pxr::VtValue(usd_mesh_data.crease_lengths), timecode); - usd_value_writer_.SetAttribute( - attr_crease_indices, pxr::VtValue(usd_mesh_data.crease_vertex_indices), timecode); - usd_value_writer_.SetAttribute( - attr_crease_sharpness, pxr::VtValue(usd_mesh_data.crease_sharpnesses), timecode); - } - - if (usd_export_context_.export_params.export_uvmaps) { - write_uv_maps(mesh, usd_mesh); - } - if (usd_export_context_.export_params.export_normals) { - write_normals(mesh, usd_mesh); - } - write_surface_velocity(context.object, mesh, usd_mesh); - - // TODO(Sybren): figure out what happens when the face groups change. - if (frame_has_been_written_) { - return; - } - - usd_mesh.CreateSubdivisionSchemeAttr().Set(pxr::UsdGeomTokens->none); - - if (usd_export_context_.export_params.export_materials) { - assign_materials(context, usd_mesh, usd_mesh_data.face_groups); - } -} - -static void get_vertices(const Mesh *mesh, USDMeshData &usd_mesh_data) -{ - usd_mesh_data.points.reserve(mesh->totvert); - - const MVert *verts = mesh->mvert; - for (int i = 0; i < mesh->totvert; ++i) { - usd_mesh_data.points.push_back(pxr::GfVec3f(verts[i].co)); - } -} - -static void get_loops_polys(const Mesh *mesh, USDMeshData &usd_mesh_data) -{ - /* Only construct face groups (a.k.a. geometry subsets) when we need them for material - * assignments. */ - bool construct_face_groups = mesh->totcol > 1; - - usd_mesh_data.face_vertex_counts.reserve(mesh->totpoly); - usd_mesh_data.face_indices.reserve(mesh->totloop); - - MLoop *mloop = mesh->mloop; - MPoly *mpoly = mesh->mpoly; - for (int i = 0; i < mesh->totpoly; ++i, ++mpoly) { - MLoop *loop = mloop + mpoly->loopstart; - usd_mesh_data.face_vertex_counts.push_back(mpoly->totloop); - for (int j = 0; j < mpoly->totloop; ++j, ++loop) { - usd_mesh_data.face_indices.push_back(loop->v); - } - - if (construct_face_groups) { - usd_mesh_data.face_groups[mpoly->mat_nr].push_back(i); - } - } -} - -static void get_creases(const Mesh *mesh, USDMeshData &usd_mesh_data) -{ - const float factor = 1.0f / 255.0f; - - MEdge *edge = mesh->medge; - float sharpness; - for (int edge_idx = 0, totedge = mesh->totedge; edge_idx < totedge; ++edge_idx, ++edge) { - if (edge->crease == 0) { - continue; - } - - if (edge->crease == 255) { - sharpness = pxr::UsdGeomMesh::SHARPNESS_INFINITE; - } - else { - sharpness = static_cast(edge->crease) * factor; - } - - usd_mesh_data.crease_vertex_indices.push_back(edge->v1); - usd_mesh_data.crease_vertex_indices.push_back(edge->v2); - usd_mesh_data.crease_lengths.push_back(2); - usd_mesh_data.crease_sharpnesses.push_back(sharpness); - } -} - -void USDGenericMeshWriter::get_geometry_data(const Mesh *mesh, USDMeshData &usd_mesh_data) -{ - get_vertices(mesh, usd_mesh_data); - get_loops_polys(mesh, usd_mesh_data); - get_creases(mesh, usd_mesh_data); -} - -void USDGenericMeshWriter::assign_materials(const HierarchyContext &context, - pxr::UsdGeomMesh usd_mesh, - const MaterialFaceGroups &usd_face_groups) -{ - if (context.object->totcol == 0) { - return; - } - - /* Binding a material to a geometry subset isn't supported by the Hydra GL viewport yet, - * which is why we always bind the first material to the entire mesh. See - * https://github.com/PixarAnimationStudios/USD/issues/542 for more info. */ - bool mesh_material_bound = false; - for (short mat_num = 0; mat_num < context.object->totcol; mat_num++) { - Material *material = BKE_object_material_get(context.object, mat_num + 1); - if (material == nullptr) { - continue; - } - - pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); - usd_material.Bind(usd_mesh.GetPrim()); - - /* USD seems to support neither per-material nor per-face-group double-sidedness, so we just - * use the flag from the first non-empty material slot. */ - usd_mesh.CreateDoubleSidedAttr( - pxr::VtValue((material->blend_flag & MA_BL_CULL_BACKFACE) == 0)); - - mesh_material_bound = true; - break; - } - - if (!mesh_material_bound) { - /* Blender defaults to double-sided, but USD to single-sided. */ - usd_mesh.CreateDoubleSidedAttr(pxr::VtValue(true)); - } - - if (!mesh_material_bound || usd_face_groups.size() < 2) { - /* Either all material slots were empty or there is only one material in use. As geometry - * subsets are only written when actually used to assign a material, and the mesh already has - * the material assigned, there is no need to continue. */ - return; - } - - // Define a geometry subset per material. - for (const MaterialFaceGroups::value_type &face_group : usd_face_groups) { - short material_number = face_group.first; - const pxr::VtIntArray &face_indices = face_group.second; - - Material *material = BKE_object_material_get(context.object, material_number + 1); - if (material == nullptr) { - continue; - } - - pxr::UsdShadeMaterial usd_material = ensure_usd_material(material); - pxr::TfToken material_name = usd_material.GetPath().GetNameToken(); - - pxr::UsdShadeMaterialBindingAPI api = pxr::UsdShadeMaterialBindingAPI(usd_mesh); - pxr::UsdGeomSubset usd_face_subset = api.CreateMaterialBindSubset(material_name, face_indices); - usd_material.Bind(usd_face_subset.GetPrim()); - } -} - -void USDGenericMeshWriter::write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh) -{ - pxr::UsdTimeCode timecode = get_export_time_code(); - const float(*lnors)[3] = static_cast(CustomData_get_layer(&mesh->ldata, CD_NORMAL)); - - pxr::VtVec3fArray loop_normals; - loop_normals.reserve(mesh->totloop); - - if (lnors != nullptr) { - /* Export custom loop normals. */ - for (int loop_idx = 0, totloop = mesh->totloop; loop_idx < totloop; ++loop_idx) { - loop_normals.push_back(pxr::GfVec3f(lnors[loop_idx])); - } - } - else { - /* Compute the loop normals based on the 'smooth' flag. */ - float normal[3]; - MPoly *mpoly = mesh->mpoly; - const MVert *mvert = mesh->mvert; - for (int poly_idx = 0, totpoly = mesh->totpoly; poly_idx < totpoly; ++poly_idx, ++mpoly) { - MLoop *mloop = mesh->mloop + mpoly->loopstart; - - if ((mpoly->flag & ME_SMOOTH) == 0) { - /* Flat shaded, use common normal for all verts. */ - BKE_mesh_calc_poly_normal(mpoly, mloop, mvert, normal); - pxr::GfVec3f pxr_normal(normal); - for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx) { - loop_normals.push_back(pxr_normal); - } - } - else { - /* Smooth shaded, use individual vert normals. */ - for (int loop_idx = 0; loop_idx < mpoly->totloop; ++loop_idx, ++mloop) { - normal_short_to_float_v3(normal, mvert[mloop->v].no); - loop_normals.push_back(pxr::GfVec3f(normal)); - } - } - } - } - - pxr::UsdAttribute attr_normals = usd_mesh.CreateNormalsAttr(pxr::VtValue(), true); - if (!attr_normals.HasValue()) { - attr_normals.Set(loop_normals, pxr::UsdTimeCode::Default()); - } - usd_value_writer_.SetAttribute(attr_normals, pxr::VtValue(loop_normals), timecode); - usd_mesh.SetNormalsInterpolation(pxr::UsdGeomTokens->faceVarying); -} - -void USDGenericMeshWriter::write_surface_velocity(Object *object, - const Mesh *mesh, - pxr::UsdGeomMesh usd_mesh) -{ - /* Only velocities from the fluid simulation are exported. This is the most important case, - * though, as the baked mesh changes topology all the time, and thus computing the velocities - * at import time in a post-processing step is hard. */ - ModifierData *md = modifiers_findByType(object, eModifierType_Fluidsim); - if (md == nullptr) { - return; - } - - /* Check that the fluid sim modifier is enabled and has useful data. */ - const bool use_render = (DEG_get_mode(usd_export_context_.depsgraph) == DAG_EVAL_RENDER); - const ModifierMode required_mode = use_render ? eModifierMode_Render : eModifierMode_Realtime; - const Scene *scene = DEG_get_evaluated_scene(usd_export_context_.depsgraph); - if (!modifier_isEnabled(scene, md, required_mode)) { - return; - } - FluidsimModifierData *fsmd = reinterpret_cast(md); - if (!fsmd->fss || fsmd->fss->type != OB_FLUIDSIM_DOMAIN) { - return; - } - FluidsimSettings *fss = fsmd->fss; - if (!fss->meshVelocities) { - return; - } - - /* Export per-vertex velocity vectors. */ - pxr::VtVec3fArray usd_velocities; - usd_velocities.reserve(mesh->totvert); - - FluidVertexVelocity *mesh_velocities = fss->meshVelocities; - for (int vertex_idx = 0, totvert = mesh->totvert; vertex_idx < totvert; - ++vertex_idx, ++mesh_velocities) { - usd_velocities.push_back(pxr::GfVec3f(mesh_velocities->vel)); - } - - pxr::UsdTimeCode timecode = get_export_time_code(); - usd_mesh.CreateVelocitiesAttr().Set(usd_velocities, timecode); -} - -USDMeshWriter::USDMeshWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx) -{ -} - -Mesh *USDMeshWriter::get_export_mesh(Object *object_eval, bool & /*r_needsfree*/) -{ - return BKE_object_get_evaluated_mesh(object_eval); -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_mesh.h b/source/blender/usd/intern/usd_writer_mesh.h deleted file mode 100644 index 4175e2b7e27..00000000000 --- a/source/blender/usd/intern/usd_writer_mesh.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_MESH_H__ -#define __USD_WRITER_MESH_H__ - -#include "usd_writer_abstract.h" - -#include - -namespace USD { - -struct USDMeshData; - -/* Writer for USD geometry. Does not assume the object is a mesh object. */ -class USDGenericMeshWriter : public USDAbstractWriter { - public: - USDGenericMeshWriter(const USDExporterContext &ctx); - - protected: - virtual bool is_supported(const HierarchyContext *context) const override; - virtual void do_write(HierarchyContext &context) override; - - virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) = 0; - virtual void free_export_mesh(Mesh *mesh); - - private: - /* Mapping from material slot number to array of face indices with that material. */ - typedef std::map MaterialFaceGroups; - - void write_mesh(HierarchyContext &context, Mesh *mesh); - void get_geometry_data(const Mesh *mesh, struct USDMeshData &usd_mesh_data); - void assign_materials(const HierarchyContext &context, - pxr::UsdGeomMesh usd_mesh, - const MaterialFaceGroups &usd_face_groups); - void write_uv_maps(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); - void write_normals(const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); - void write_surface_velocity(Object *object, const Mesh *mesh, pxr::UsdGeomMesh usd_mesh); -}; - -class USDMeshWriter : public USDGenericMeshWriter { - public: - USDMeshWriter(const USDExporterContext &ctx); - - protected: - virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; -}; - -} // namespace USD - -#endif /* __USD_WRITER_MESH_H__ */ diff --git a/source/blender/usd/intern/usd_writer_metaball.cc b/source/blender/usd/intern/usd_writer_metaball.cc deleted file mode 100644 index 25b216d20f3..00000000000 --- a/source/blender/usd/intern/usd_writer_metaball.cc +++ /dev/null @@ -1,81 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2020 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_metaball.h" -#include "usd_hierarchy_iterator.h" - -#include -#include -#include - -extern "C" { -#include "BLI_assert.h" - -#include "BKE_displist.h" -#include "BKE_lib_id.h" -#include "BKE_mball.h" -#include "BKE_mesh.h" -#include "BKE_object.h" - -#include "DNA_mesh_types.h" -#include "DNA_meta_types.h" -} - -namespace USD { - -USDMetaballWriter::USDMetaballWriter(const USDExporterContext &ctx) : USDGenericMeshWriter(ctx) -{ -} - -bool USDMetaballWriter::is_supported(const HierarchyContext *context) const -{ - Scene *scene = DEG_get_input_scene(usd_export_context_.depsgraph); - return is_basis_ball(scene, context->object) && USDGenericMeshWriter::is_supported(context); -} - -bool USDMetaballWriter::check_is_animated(const HierarchyContext & /*context*/) const -{ - /* We assume that metaballs are always animated, as the current object may - * not be animated but another ball in the same group may be. */ - return true; -} - -Mesh *USDMetaballWriter::get_export_mesh(Object *object_eval, bool &r_needsfree) -{ - Mesh *mesh_eval = BKE_object_get_evaluated_mesh(object_eval); - if (mesh_eval != nullptr) { - /* Mesh_eval only exists when generative modifiers are in use. */ - r_needsfree = false; - return mesh_eval; - } - r_needsfree = true; - return BKE_mesh_new_from_object(usd_export_context_.depsgraph, object_eval, false); -} - -void USDMetaballWriter::free_export_mesh(Mesh *mesh) -{ - BKE_id_free(nullptr, mesh); -} - -bool USDMetaballWriter::is_basis_ball(Scene *scene, Object *ob) const -{ - Object *basis_ob = BKE_mball_basis_find(scene, ob); - return ob == basis_ob; -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_metaball.h b/source/blender/usd/intern/usd_writer_metaball.h deleted file mode 100644 index 1a86daae2ae..00000000000 --- a/source/blender/usd/intern/usd_writer_metaball.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2020 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_METABALL_H__ -#define __USD_WRITER_METABALL_H__ - -#include "usd_writer_mesh.h" - -namespace USD { - -class USDMetaballWriter : public USDGenericMeshWriter { - public: - USDMetaballWriter(const USDExporterContext &ctx); - - protected: - virtual Mesh *get_export_mesh(Object *object_eval, bool &r_needsfree) override; - virtual void free_export_mesh(Mesh *mesh) override; - virtual bool is_supported(const HierarchyContext *context) const override; - virtual bool check_is_animated(const HierarchyContext &context) const override; - - private: - bool is_basis_ball(Scene *scene, Object *ob) const; -}; - -} // namespace USD - -#endif /* __USD_WRITER_METABALL_H__ */ diff --git a/source/blender/usd/intern/usd_writer_transform.cc b/source/blender/usd/intern/usd_writer_transform.cc deleted file mode 100644 index 321b516221a..00000000000 --- a/source/blender/usd/intern/usd_writer_transform.cc +++ /dev/null @@ -1,64 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#include "usd_writer_transform.h" -#include "usd_hierarchy_iterator.h" - -#include -#include - -extern "C" { -#include "BKE_object.h" - -#include "BLI_math_matrix.h" - -#include "DNA_layer_types.h" -} - -namespace USD { - -USDTransformWriter::USDTransformWriter(const USDExporterContext &ctx) : USDAbstractWriter(ctx) -{ -} - -void USDTransformWriter::do_write(HierarchyContext &context) -{ - float parent_relative_matrix[4][4]; // The object matrix relative to the parent. - mul_m4_m4m4(parent_relative_matrix, context.parent_matrix_inv_world, context.matrix_world); - - // Write the transform relative to the parent. - pxr::UsdGeomXform xform = pxr::UsdGeomXform::Define(usd_export_context_.stage, - usd_export_context_.usd_path); - if (!xformOp_) { - xformOp_ = xform.AddTransformOp(); - } - xformOp_.Set(pxr::GfMatrix4d(parent_relative_matrix), get_export_time_code()); -} - -bool USDTransformWriter::check_is_animated(const HierarchyContext &context) const -{ - if (context.duplicator != NULL) { - /* This object is being duplicated, so could be emitted by a particle system and thus - * influenced by forces. TODO(Sybren): Make this more strict. Probably better to get from the - * depsgraph whether this object instance has a time source. */ - return true; - } - return BKE_object_moves_in_time(context.object, context.animation_check_include_parent); -} - -} // namespace USD diff --git a/source/blender/usd/intern/usd_writer_transform.h b/source/blender/usd/intern/usd_writer_transform.h deleted file mode 100644 index 52c4a657f33..00000000000 --- a/source/blender/usd/intern/usd_writer_transform.h +++ /dev/null @@ -1,42 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ -#ifndef __USD_WRITER_TRANSFORM_H__ -#define __USD_WRITER_TRANSFORM_H__ - -#include "usd_writer_abstract.h" - -#include - -namespace USD { - -class USDTransformWriter : public USDAbstractWriter { - private: - pxr::UsdGeomXformOp xformOp_; - - public: - USDTransformWriter(const USDExporterContext &ctx); - - protected: - void do_write(HierarchyContext &context) override; - bool check_is_animated(const HierarchyContext &context) const override; -}; - -} // namespace USD - -#endif /* __USD_WRITER_TRANSFORM_H__ */ diff --git a/source/blender/usd/usd.h b/source/blender/usd/usd.h deleted file mode 100644 index 8a5575d53cf..00000000000 --- a/source/blender/usd/usd.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2019 Blender Foundation. - * All rights reserved. - */ - -#ifndef __USD_H__ -#define __USD_H__ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "DEG_depsgraph.h" - -struct Scene; -struct bContext; - -struct USDExportParams { - bool export_animation; - bool export_hair; - bool export_uvmaps; - bool export_normals; - bool export_materials; - bool selected_objects_only; - bool use_instancing; - enum eEvaluationMode evaluation_mode; -}; - -/* The USD_export takes a as_background_job parameter, and returns a boolean. - * - * When as_background_job=true, returns false immediately after scheduling - * a background job. - * - * When as_background_job=false, performs the export synchronously, and returns - * true when the export was ok, and false if there were any errors. - */ - -bool USD_export(struct bContext *C, - const char *filepath, - const struct USDExportParams *params, - bool as_background_job); - -int USD_get_version(void); - -#ifdef __cplusplus -} -#endif - -#endif /* __USD_H__ */ diff --git a/tests/gtests/alembic/CMakeLists.txt b/tests/gtests/alembic/CMakeLists.txt index f8b2c373029..6ba1c4465d9 100644 --- a/tests/gtests/alembic/CMakeLists.txt +++ b/tests/gtests/alembic/CMakeLists.txt @@ -23,7 +23,7 @@ set(INC .. ../../../source/blender/blenlib ../../../source/blender/blenkernel - ../../../source/blender/alembic + ../../../source/blender/io/alembic ../../../source/blender/makesdna ../../../source/blender/depsgraph ${ALEMBIC_INCLUDE_DIRS} diff --git a/tests/gtests/usd/CMakeLists.txt b/tests/gtests/usd/CMakeLists.txt index df4c865e976..56759f4ccea 100644 --- a/tests/gtests/usd/CMakeLists.txt +++ b/tests/gtests/usd/CMakeLists.txt @@ -35,7 +35,7 @@ set(INC .. ../../../source/blender/blenlib ../../../source/blender/blenkernel - ../../../source/blender/usd + ../../../source/blender/io/usd ../../../source/blender/makesdna ../../../source/blender/depsgraph ${USD_INCLUDE_DIRS} -- cgit v1.2.3