vibephysics 0.2.2__tar.gz → 0.2.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {vibephysics-0.2.2/src/vibephysics.egg-info → vibephysics-0.2.3}/PKG-INFO +65 -35
  2. {vibephysics-0.2.2 → vibephysics-0.2.3}/README.md +64 -34
  3. {vibephysics-0.2.2 → vibephysics-0.2.3}/pyproject.toml +1 -1
  4. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/__init__.py +1 -1
  5. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/mapping/__init__.py +2 -0
  6. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/mapping/colmap.py +2 -2
  7. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/mapping/glomap.py +1 -1
  8. vibephysics-0.2.3/src/vibephysics/mapping/map_visual.py +274 -0
  9. {vibephysics-0.2.2 → vibephysics-0.2.3/src/vibephysics.egg-info}/PKG-INFO +65 -35
  10. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics.egg-info/SOURCES.txt +1 -0
  11. {vibephysics-0.2.2 → vibephysics-0.2.3}/LICENSE +0 -0
  12. {vibephysics-0.2.2 → vibephysics-0.2.3}/MANIFEST.in +0 -0
  13. {vibephysics-0.2.2 → vibephysics-0.2.3}/setup.cfg +0 -0
  14. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/__init__.py +0 -0
  15. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/base.py +0 -0
  16. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/bbox.py +0 -0
  17. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/manager.py +0 -0
  18. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/motion_trail.py +0 -0
  19. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/annotation/point_tracking.py +0 -0
  20. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/__init__.py +0 -0
  21. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/base.py +0 -0
  22. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/center.py +0 -0
  23. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/following.py +0 -0
  24. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/manager.py +0 -0
  25. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/camera/mounted.py +0 -0
  26. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/README.md +0 -0
  27. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/__init__.py +0 -0
  28. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/go2.py +0 -0
  29. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/ground.py +0 -0
  30. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/lighting.py +0 -0
  31. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/materials.py +0 -0
  32. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/objects.py +0 -0
  33. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/open_duck.py +0 -0
  34. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/physics.py +0 -0
  35. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/robot.py +0 -0
  36. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/trajectory.py +0 -0
  37. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/foundation/water.py +0 -0
  38. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/mapping/pipeline.py +0 -0
  39. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/mapping/utils.py +0 -0
  40. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/__init__.py +0 -0
  41. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/exporter.py +0 -0
  42. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/gsplat.py +0 -0
  43. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/importer.py +0 -0
  44. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/scene.py +0 -0
  45. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics/setup/viewport.py +0 -0
  46. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics.egg-info/dependency_links.txt +0 -0
  47. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics.egg-info/entry_points.txt +0 -0
  48. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics.egg-info/requires.txt +0 -0
  49. {vibephysics-0.2.2 → vibephysics-0.2.3}/src/vibephysics.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibephysics
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Physics simulation and annotation tools for Blender
5
5
  Author-email: tsunyi <tsunyi@mimiaigen.com>
6
6
  Project-URL: Homepage, https://github.com/yourusername/vibephysics
@@ -33,6 +33,47 @@ Requires-Dist: ruff; extra == "dev"
33
33
 
34
34
  **A lightweight Blender physics simulation framework for creating realistic robot animations, rigid body physics, water dynamics, and comprehensive annotation tools — all running efficiently on CPU.**
35
35
 
36
+ ## ⚙️ Installation (MacOS)
37
+
38
+ ```bash
39
+ # 1. Create environment
40
+ conda create -n vibephysics python=3.11
41
+ conda activate vibephysics
42
+
43
+ # 2. Install core package (includes COLMAP mapping & Blender simulation)
44
+ pip install vibephysics
45
+
46
+ # 3. (Optional) Install GLOMAP backend
47
+ # Linux users: refer to "Linux System Dependencies" below first
48
+ pip install git+https://github.com/shamangary/glomap.git
49
+ ```
50
+
51
+ ## 🐧 Linux (Ubuntu) System Dependencies
52
+ If you are on Linux and want to use the **GLOMAP** or **COLMAP** backends, you must install the following C++ development libraries to enable successful compilation:
53
+
54
+ ```bash
55
+ sudo apt-get update
56
+ sudo apt-get install -y \
57
+ libeigen3-dev \
58
+ libceres-dev \
59
+ libgoogle-glog-dev \
60
+ libboost-all-dev \
61
+ libsuitesparse-dev \
62
+ libsqlite3-dev \
63
+ libgflags-dev \
64
+ libfreeimage-dev \
65
+ libmetis-dev
66
+ ```
67
+
68
+ ## ⚠️ Troubleshooting (Linker Errors)
69
+ If you are using **Anaconda** on Linux and see an error like `relocation R_X86_64_TPOFF32 ... can not be used when making a shared object`, it is due to a conflict with the Anaconda linker. Fix it by forcing the compiler to use the global-dynamic TLS model:
70
+
71
+ ```bash
72
+ export CXXFLAGS="$CXXFLAGS -fPIC -ftls-model=global-dynamic"
73
+ export CFLAGS="$CFLAGS -fPIC"
74
+ pip install git+https://github.com/shamangary/glomap.git
75
+ ```
76
+
36
77
  ## 🎬 Example Results (`sh run_robot.sh`)
37
78
 
38
79
  ![Result Demo](assets/result_demo.gif)
@@ -93,37 +134,6 @@ Perfect for researchers, animators, and robotics engineers who need physics simu
93
134
  - **Open Duck**: We use the [Open Duck blender model](https://github.com/pollen-robotics/Open_Duck_Blender) as demo. We do not own the model. Please refer to the original github repo.
94
135
  - **Unitree Go2**: We use the [Unitree Go2 USD model](https://huggingface.co/datasets/unitreerobotics/unitree_model). The model is auto-downloaded when running Go2 examples. We do not own the model.
95
136
 
96
- ## ⚙️ Installation
97
-
98
- ```bash
99
- # Create and activate environment
100
- conda create -n vibephysics python=3.11
101
- conda activate vibephysics
102
-
103
- # Install vibephysics
104
- pip install vibephysics
105
-
106
- # Or install from source
107
- git clone https://github.com/mimiaigen/vibephysics
108
- cd vibephysics
109
- pip install -e .
110
- ```
111
-
112
- ### 🗺️ Mapping & Reconstruction
113
- The core mapping tools are now built-in!
114
-
115
- 1. **Incremental COLMAP**: Available immediately after `pip install vibephysics`.
116
- 2. **Global GLOMAP**: Just run your first mapping task! `vibephysics` will automatically prompt and install the optimized GLOMAP backend from GitHub.
117
- *(Note: This requires a one-time C++ build which takes a few minutes).*
118
-
119
- ### Requirements for Simulations
120
- If you intend to run physics simulations, you also need to install Blender's Python module:
121
-
122
- ```bash
123
- # Install Blender module
124
- pip install bpy
125
- ```
126
-
127
137
  ## Quick Start
128
138
 
129
139
  ```bash
@@ -310,7 +320,7 @@ VibePhysics integrates high-performance Structure-from-Motion (SfM) engines to c
310
320
  from vibephysics import mapping
311
321
 
312
322
  # 1. Simple Usage (Only image_path is REQUIRED)
313
- # Defaults: glomap engine, exhaustive matcher, SIMPLE_RADIAL camera
323
+ # Defaults: glomap engine, exhaustive matcher, PINHOLE camera
314
324
  mapping.glomap_pipeline(image_path="path/to/images")
315
325
 
316
326
  # 2. COLMAP Incremental Pipeline
@@ -322,7 +332,7 @@ mapping.glomap_pipeline(
322
332
  output_path="output/dir", # Optional: Defaults to image_path/../mapping_output/
323
333
  database_path="path/to/database.db", # Optional: Defaults to output_path/sparse/database.db
324
334
  matcher="exhaustive", # Optional: "exhaustive" (default) or "sequential"
325
- camera_model="SIMPLE_RADIAL", # Optional: "PINHOLE", "SIMPLE_RADIAL" (default), "OPENCV", etc.
335
+ camera_model="PINHOLE", # Optional: "PINHOLE" (default), "SIMPLE_RADIAL", "OPENCV", etc.
326
336
  verbose=True # Optional: Set to False to suppress logs
327
337
  )
328
338
  ```
@@ -333,7 +343,27 @@ mapping.glomap_pipeline(
333
343
  | **`output_path`** | No | `mapping_output/` | Directory for results. Creates `sparse/0` and symlinked `images/`. |
334
344
  | **`database_path`** | No | `database.db` | Optional path to an existing COLMAP database. |
335
345
  | **`matcher`** | No | `exhaustive` | Matching algorithm: `exhaustive` or `sequential`. |
336
- | **`camera_model`** | No | `SIMPLE_RADIAL` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
346
+ | **`camera_model`** | No | `PINHOLE` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
347
+
348
+ ### 🎨 Visualization in Blender
349
+
350
+ You can load your Colmap/GLOMAP reconstruction directly into Blender for inspection, featuring colored point clouds with high-visibility Geometry Node spheres and correct camera poses.
351
+
352
+ ```python
353
+ from vibephysics import mapping
354
+
355
+ # Load a sparse model folder (containing cameras.bin, points3D.bin etc.)
356
+ mapping.load_colmap_reconstruction(
357
+ input_path="output/mapping_output/sparse/0",
358
+ point_size=0.01 # Adjust point blob size for visibility
359
+ )
360
+ ```
361
+
362
+ **Run the Demo:**
363
+ ```bash
364
+ # Visualize an existing reconstruction
365
+ python examples/colmap_format/demo_glomap.py --sparse /path/to/sparse/0 --point-size 0.02
366
+ ```
337
367
 
338
368
  ## Gaussian Splatting (3DGS) (BETA)
339
369
 
@@ -4,6 +4,47 @@
4
4
 
5
5
  **A lightweight Blender physics simulation framework for creating realistic robot animations, rigid body physics, water dynamics, and comprehensive annotation tools — all running efficiently on CPU.**
6
6
 
7
+ ## ⚙️ Installation (MacOS)
8
+
9
+ ```bash
10
+ # 1. Create environment
11
+ conda create -n vibephysics python=3.11
12
+ conda activate vibephysics
13
+
14
+ # 2. Install core package (includes COLMAP mapping & Blender simulation)
15
+ pip install vibephysics
16
+
17
+ # 3. (Optional) Install GLOMAP backend
18
+ # Linux users: refer to "Linux System Dependencies" below first
19
+ pip install git+https://github.com/shamangary/glomap.git
20
+ ```
21
+
22
+ ## 🐧 Linux (Ubuntu) System Dependencies
23
+ If you are on Linux and want to use the **GLOMAP** or **COLMAP** backends, you must install the following C++ development libraries to enable successful compilation:
24
+
25
+ ```bash
26
+ sudo apt-get update
27
+ sudo apt-get install -y \
28
+ libeigen3-dev \
29
+ libceres-dev \
30
+ libgoogle-glog-dev \
31
+ libboost-all-dev \
32
+ libsuitesparse-dev \
33
+ libsqlite3-dev \
34
+ libgflags-dev \
35
+ libfreeimage-dev \
36
+ libmetis-dev
37
+ ```
38
+
39
+ ## ⚠️ Troubleshooting (Linker Errors)
40
+ If you are using **Anaconda** on Linux and see an error like `relocation R_X86_64_TPOFF32 ... can not be used when making a shared object`, it is due to a conflict with the Anaconda linker. Fix it by forcing the compiler to use the global-dynamic TLS model:
41
+
42
+ ```bash
43
+ export CXXFLAGS="$CXXFLAGS -fPIC -ftls-model=global-dynamic"
44
+ export CFLAGS="$CFLAGS -fPIC"
45
+ pip install git+https://github.com/shamangary/glomap.git
46
+ ```
47
+
7
48
  ## 🎬 Example Results (`sh run_robot.sh`)
8
49
 
9
50
  ![Result Demo](assets/result_demo.gif)
@@ -64,37 +105,6 @@ Perfect for researchers, animators, and robotics engineers who need physics simu
64
105
  - **Open Duck**: We use the [Open Duck blender model](https://github.com/pollen-robotics/Open_Duck_Blender) as demo. We do not own the model. Please refer to the original github repo.
65
106
  - **Unitree Go2**: We use the [Unitree Go2 USD model](https://huggingface.co/datasets/unitreerobotics/unitree_model). The model is auto-downloaded when running Go2 examples. We do not own the model.
66
107
 
67
- ## ⚙️ Installation
68
-
69
- ```bash
70
- # Create and activate environment
71
- conda create -n vibephysics python=3.11
72
- conda activate vibephysics
73
-
74
- # Install vibephysics
75
- pip install vibephysics
76
-
77
- # Or install from source
78
- git clone https://github.com/mimiaigen/vibephysics
79
- cd vibephysics
80
- pip install -e .
81
- ```
82
-
83
- ### 🗺️ Mapping & Reconstruction
84
- The core mapping tools are now built-in!
85
-
86
- 1. **Incremental COLMAP**: Available immediately after `pip install vibephysics`.
87
- 2. **Global GLOMAP**: Just run your first mapping task! `vibephysics` will automatically prompt and install the optimized GLOMAP backend from GitHub.
88
- *(Note: This requires a one-time C++ build which takes a few minutes).*
89
-
90
- ### Requirements for Simulations
91
- If you intend to run physics simulations, you also need to install Blender's Python module:
92
-
93
- ```bash
94
- # Install Blender module
95
- pip install bpy
96
- ```
97
-
98
108
  ## Quick Start
99
109
 
100
110
  ```bash
@@ -281,7 +291,7 @@ VibePhysics integrates high-performance Structure-from-Motion (SfM) engines to c
281
291
  from vibephysics import mapping
282
292
 
283
293
  # 1. Simple Usage (Only image_path is REQUIRED)
284
- # Defaults: glomap engine, exhaustive matcher, SIMPLE_RADIAL camera
294
+ # Defaults: glomap engine, exhaustive matcher, PINHOLE camera
285
295
  mapping.glomap_pipeline(image_path="path/to/images")
286
296
 
287
297
  # 2. COLMAP Incremental Pipeline
@@ -293,7 +303,7 @@ mapping.glomap_pipeline(
293
303
  output_path="output/dir", # Optional: Defaults to image_path/../mapping_output/
294
304
  database_path="path/to/database.db", # Optional: Defaults to output_path/sparse/database.db
295
305
  matcher="exhaustive", # Optional: "exhaustive" (default) or "sequential"
296
- camera_model="SIMPLE_RADIAL", # Optional: "PINHOLE", "SIMPLE_RADIAL" (default), "OPENCV", etc.
306
+ camera_model="PINHOLE", # Optional: "PINHOLE" (default), "SIMPLE_RADIAL", "OPENCV", etc.
297
307
  verbose=True # Optional: Set to False to suppress logs
298
308
  )
299
309
  ```
@@ -304,7 +314,27 @@ mapping.glomap_pipeline(
304
314
  | **`output_path`** | No | `mapping_output/` | Directory for results. Creates `sparse/0` and symlinked `images/`. |
305
315
  | **`database_path`** | No | `database.db` | Optional path to an existing COLMAP database. |
306
316
  | **`matcher`** | No | `exhaustive` | Matching algorithm: `exhaustive` or `sequential`. |
307
- | **`camera_model`** | No | `SIMPLE_RADIAL` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
317
+ | **`camera_model`** | No | `PINHOLE` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
318
+
319
+ ### 🎨 Visualization in Blender
320
+
321
+ You can load your Colmap/GLOMAP reconstruction directly into Blender for inspection, featuring colored point clouds with high-visibility Geometry Node spheres and correct camera poses.
322
+
323
+ ```python
324
+ from vibephysics import mapping
325
+
326
+ # Load a sparse model folder (containing cameras.bin, points3D.bin etc.)
327
+ mapping.load_colmap_reconstruction(
328
+ input_path="output/mapping_output/sparse/0",
329
+ point_size=0.01 # Adjust point blob size for visibility
330
+ )
331
+ ```
332
+
333
+ **Run the Demo:**
334
+ ```bash
335
+ # Visualize an existing reconstruction
336
+ python examples/colmap_format/demo_glomap.py --sparse /path/to/sparse/0 --point-size 0.02
337
+ ```
308
338
 
309
339
  ## Gaussian Splatting (3DGS) (BETA)
310
340
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "vibephysics"
7
- version = "0.2.2"
7
+ version = "0.2.3"
8
8
  description = "Physics simulation and annotation tools for Blender"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -16,7 +16,7 @@ Usage:
16
16
  Note: This package requires Blender 5.0's Python environment (bpy) for simulation.
17
17
  """
18
18
 
19
- __version__ = "0.2.2"
19
+ __version__ = "0.2.3"
20
20
  __author__ = "Tsun-Yi Yang"
21
21
 
22
22
  # Core modules (non-Blender)
@@ -1,5 +1,6 @@
1
1
  from .colmap import colmap_pipeline, run_colmap_stage, run_incremental_mapping_stage
2
2
  from .glomap import glomap_pipeline, run_glomap_stage
3
+ from .map_visual import load_colmap_reconstruction
3
4
  from .utils import prepare_output_directory
4
5
 
5
6
  __all__ = [
@@ -8,5 +9,6 @@ __all__ = [
8
9
  "run_colmap_stage",
9
10
  "run_incremental_mapping_stage",
10
11
  "run_glomap_stage",
12
+ "load_colmap_reconstruction",
11
13
  "prepare_output_directory"
12
14
  ]
@@ -7,7 +7,7 @@ from .utils import prepare_output_directory
7
7
  def run_colmap_stage(
8
8
  image_path: Path,
9
9
  database_path: Path,
10
- camera_model: str = "SIMPLE_RADIAL",
10
+ camera_model: str = "PINHOLE",
11
11
  matcher: str = "exhaustive",
12
12
  verbose: bool = True
13
13
  ) -> int:
@@ -86,7 +86,7 @@ def colmap_pipeline(
86
86
  output_path: str | Path | None = None,
87
87
  database_path: str | Path | None = None,
88
88
  matcher: str = "exhaustive",
89
- camera_model: str = "SIMPLE_RADIAL",
89
+ camera_model: str = "PINHOLE",
90
90
  verbose: bool = True
91
91
  ) -> int:
92
92
  """
@@ -41,7 +41,7 @@ def glomap_pipeline(
41
41
  output_path: str | Path | None = None,
42
42
  database_path: str | Path | None = None,
43
43
  matcher: str = "exhaustive",
44
- camera_model: str = "SIMPLE_RADIAL",
44
+ camera_model: str = "PINHOLE",
45
45
  verbose: bool = True
46
46
  ) -> int:
47
47
  """
@@ -0,0 +1,274 @@
1
+ import bpy
2
+ import pycolmap
3
+ import numpy as np
4
+ from pathlib import Path
5
+ import math
6
+
7
+ def create_camera_object(image, camera, collection, scale=0.1):
8
+ """
9
+ Create a Blender camera object from a Colmap image and camera.
10
+ """
11
+ name = image.name
12
+
13
+ # Colmap pose is World-to-Camera (R, t)
14
+ # pycolmap 3.x uses image.cam_from_world() method
15
+ if hasattr(image, "cam_from_world") and callable(image.cam_from_world):
16
+ pose = image.cam_from_world()
17
+ rot_mat = pose.rotation.matrix()
18
+ tvec = pose.translation
19
+ else:
20
+ # Fallback for older pycolmap
21
+ qvec = getattr(image, "qvec", np.array([1, 0, 0, 0]))
22
+ tvec = getattr(image, "tvec", np.zeros(3))
23
+ # Manual qvec to rotmat if function is missing
24
+ if hasattr(pycolmap, "qvec_to_rotmat"):
25
+ rot_mat = pycolmap.qvec_to_rotmat(qvec)
26
+ else:
27
+ # Standard Hamilton quaternion to rotation matrix
28
+ w, x, y, z = qvec
29
+ rot_mat = np.array([
30
+ [1 - 2*y**2 - 2*z**2, 2*x*y - 2*z*w, 2*x*z + 2*y*w],
31
+ [2*x*y + 2*z*w, 1 - 2*x**2 - 2*z**2, 2*y*z - 2*x*w],
32
+ [2*x*z - 2*y*w, 2*y*z + 2*x*w, 1 - 2*x**2 - 2*y**2]
33
+ ])
34
+
35
+ # Inverse rotation and translation to get Camera-to-World
36
+ rot_mat_inv = rot_mat.T
37
+ tvec_inv = -rot_mat_inv @ tvec
38
+
39
+ # Create Camera Data
40
+ cam_data = bpy.data.cameras.new(name=f"CamData_{name}")
41
+ cam_obj = bpy.data.objects.new(name=name, object_data=cam_data)
42
+ collection.objects.link(cam_obj)
43
+
44
+ # Construct 4x4 matrix in OpenCV frame (X-right, Y-down, Z-forward)
45
+ mat_world_cv = np.eye(4)
46
+ mat_world_cv[:3, :3] = rot_mat_inv
47
+ mat_world_cv[:3, 3] = tvec_inv
48
+
49
+ # Convert from OpenCV to Blender coordinate system
50
+ # Blender camera looks down -Z, Y is up.
51
+ # We rotate 180 degrees around X to flip Y and Z.
52
+ transform_matrix = np.array([
53
+ [1, 0, 0, 0],
54
+ [0, -1, 0, 0],
55
+ [0, 0, -1, 0],
56
+ [0, 0, 0, 1]
57
+ ])
58
+
59
+ final_matrix = mat_world_cv @ transform_matrix
60
+
61
+ # Assign to object
62
+ import mathutils
63
+ m = mathutils.Matrix(final_matrix.tolist())
64
+ cam_obj.matrix_world = m
65
+
66
+ # Set camera intrinsics
67
+ # Params mapping depends on camera model
68
+ width = camera.width
69
+ height = camera.height
70
+ cam_data.sensor_width = 36.0 # standard 36mm sensor
71
+
72
+ # f_mm = f_px * sensor_width / width_px
73
+ f_px = 1000.0 # fallback
74
+
75
+ # Get camera model name
76
+ if hasattr(camera, "model") and hasattr(camera.model, "name"):
77
+ model = camera.model.name
78
+ else:
79
+ model = getattr(camera, "model_name", "UNKNOWN")
80
+
81
+ params = camera.params
82
+
83
+ if model in ["SIMPLE_PINHOLE", "SIMPLE_RADIAL"]:
84
+ # f, cx, cy
85
+ f_px = params[0]
86
+ elif model in ["PINHOLE", "OPENCV", "FULL_OPENCV"]:
87
+ # fx, fy, cx, cy
88
+ f_px = (params[0] + params[1]) / 2.0
89
+
90
+ cam_data.lens = f_px * cam_data.sensor_width / width
91
+
92
+ return cam_obj
93
+
94
+ def create_point_cloud(points3D, collection, name="PointCloud", point_size=0.03):
95
+ """
96
+ Create a point cloud visualization using a mesh with vertices and colors.
97
+ Uses Geometry Nodes to make points visible as spheres.
98
+ """
99
+ mesh = bpy.data.meshes.new(name=name)
100
+ obj = bpy.data.objects.new(name=name, object_data=mesh)
101
+ collection.objects.link(obj)
102
+
103
+ # Extract points and colors
104
+ xyz = []
105
+ rgb = []
106
+
107
+ for p_id, p in points3D.items():
108
+ xyz.append(p.xyz)
109
+ rgb.append(p.color / 255.0) # Normalize to 0-1
110
+
111
+ if not xyz:
112
+ print("No points found in reconstruction.")
113
+ return obj
114
+
115
+ # Create mesh
116
+ mesh.from_pydata(xyz, [], [])
117
+ mesh.update()
118
+
119
+ # Add colors as a generic attribute
120
+ if rgb:
121
+ # Check Blender version for attribute creation
122
+ if hasattr(mesh.attributes, "new"):
123
+ color_attr = mesh.attributes.new(name="Color", type='FLOAT_COLOR', domain='POINT')
124
+ color_attr.data.foreach_set("color", [c for color in rgb for c in (*color, 1.0)]) # RGBA
125
+
126
+ # Simple Geometry Nodes setup to render points as spheres
127
+ modifier = obj.modifiers.new(name="PointVisualizer", type='NODES')
128
+
129
+ # Setup node tree
130
+ node_tree = bpy.data.node_groups.new(name="PointVisualizerTree", type='GeometryNodeTree')
131
+
132
+ # In Blender 4.0+, we use interface to add sockets
133
+ if hasattr(node_tree, "interface"):
134
+ if not any(item.name == "Geometry" for item in node_tree.interface.items_tree if item.item_type == 'SOCKET'):
135
+ node_tree.interface.new_socket(name="Geometry", in_out='INPUT', socket_type='NodeSocketGeometry')
136
+ node_tree.interface.new_socket(name="Geometry", in_out='OUTPUT', socket_type='NodeSocketGeometry')
137
+ else:
138
+ # Older Blender fallback
139
+ if "Geometry" not in node_tree.inputs:
140
+ node_tree.inputs.new('NodeSocketGeometry', "Geometry")
141
+ if "Geometry" not in node_tree.outputs:
142
+ node_tree.outputs.new('NodeSocketGeometry', "Geometry")
143
+
144
+ links = node_tree.links
145
+ nodes = node_tree.nodes
146
+
147
+ # Clear default nodes
148
+ nodes.clear()
149
+
150
+ # Input/Output
151
+ node_in = nodes.new('NodeGroupInput')
152
+ node_out = nodes.new('NodeGroupOutput')
153
+
154
+ # Point to Volume / Instances
155
+ node_m2p = nodes.new('GeometryNodeMeshToPoints')
156
+ node_inst = nodes.new('GeometryNodeInstanceOnPoints')
157
+ node_sph = nodes.new('GeometryNodeMeshIcoSphere')
158
+ node_sph.inputs['Radius'].default_value = point_size
159
+ node_sph.inputs['Subdivisions'].default_value = 1
160
+
161
+ # Realize Instances to propagate attributes (like Color)
162
+ node_realize = nodes.new('GeometryNodeRealizeInstances')
163
+
164
+ # Material
165
+ node_mat = nodes.new('GeometryNodeSetMaterial')
166
+
167
+ # Material setup
168
+ mat_name = "PointCloudMaterial"
169
+ if mat_name not in bpy.data.materials:
170
+ mat = bpy.data.materials.new(name=mat_name)
171
+ mat.use_nodes = True
172
+ nodes_mat = mat.node_tree.nodes
173
+ links_mat = mat.node_tree.links
174
+ nodes_mat.clear()
175
+
176
+ node_out_mat = nodes_mat.new('ShaderNodeOutputMaterial')
177
+ node_principled = nodes_mat.new('ShaderNodeBsdfPrincipled')
178
+ # Use Attribute node to get the vertex color
179
+ node_attr = nodes_mat.new('ShaderNodeAttribute')
180
+ node_attr.attribute_name = "Color"
181
+ node_attr.attribute_type = 'GEOMETRY'
182
+
183
+ links_mat.new(node_attr.outputs['Color'], node_principled.inputs['Base Color'])
184
+ links_mat.new(node_principled.outputs['BSDF'], node_out_mat.inputs['Surface'])
185
+ else:
186
+ mat = bpy.data.materials[mat_name]
187
+
188
+ # Assign material to object slots (important for some Blender versions to see attributes)
189
+ if mat.name not in obj.data.materials:
190
+ obj.data.materials.append(mat)
191
+
192
+ node_mat.inputs['Material'].default_value = mat
193
+
194
+ # Link nodes
195
+ links.new(node_in.outputs[0], node_m2p.inputs['Mesh'])
196
+ links.new(node_m2p.outputs['Points'], node_inst.inputs['Points'])
197
+ links.new(node_sph.outputs['Mesh'], node_inst.inputs['Instance'])
198
+ links.new(node_inst.outputs['Instances'], node_realize.inputs['Geometry'])
199
+ links.new(node_realize.outputs['Geometry'], node_mat.inputs['Geometry'])
200
+ links.new(node_mat.outputs['Geometry'], node_out.inputs[0])
201
+
202
+ modifier.node_group = node_tree
203
+
204
+ # Attempt to set viewport shading to MATERIAL for better UX
205
+ if bpy.context.screen:
206
+ for area in bpy.context.screen.areas:
207
+ if area.type == 'VIEW_3D':
208
+ for space in area.spaces:
209
+ if space.type == 'VIEW_3D':
210
+ space.shading.type = 'MATERIAL'
211
+
212
+ return obj
213
+
214
+ def load_colmap_reconstruction(
215
+ input_path: str, # Path to sparse/0 folder
216
+ collection_name: str = "Reconstruction",
217
+ import_cameras: bool = True,
218
+ import_points: bool = True,
219
+ camera_scale: float = 0.1,
220
+ point_size: float = 0.03,
221
+ rotation: tuple = (-90, 0, 0) # Global rotation in degrees (Euler XYZ)
222
+ ):
223
+ """
224
+ Load Colmap reconstruction output (sparse folder) into Blender.
225
+ """
226
+ input_dir = Path(input_path)
227
+ if not input_dir.exists():
228
+ print(f"Error: Path {input_dir} does not exist.")
229
+ return
230
+
231
+ print(f"Loading Colmap reconstruction from {input_dir}...")
232
+ recon = pycolmap.Reconstruction(input_dir)
233
+
234
+ # Create collection
235
+ if collection_name in bpy.data.collections:
236
+ col = bpy.data.collections[collection_name]
237
+ else:
238
+ col = bpy.data.collections.new(collection_name)
239
+ bpy.context.scene.collection.children.link(col)
240
+
241
+ # Create a root object for the whole reconstruction to allow global manipulation
242
+ root_name = f"{collection_name}_Root"
243
+ if root_name in bpy.data.objects:
244
+ root_obj = bpy.data.objects[root_name]
245
+ else:
246
+ root_obj = bpy.data.objects.new(root_name, None)
247
+ col.objects.link(root_obj)
248
+
249
+ # Apply global rotation to the root
250
+ root_obj.rotation_mode = 'XYZ'
251
+ root_obj.rotation_euler = [math.radians(a) for a in rotation]
252
+
253
+ if import_points:
254
+ print(f"Importing {len(recon.points3D)} points...")
255
+ pc_obj = create_point_cloud(recon.points3D, col, point_size=point_size)
256
+ pc_obj.parent = root_obj
257
+
258
+ if import_cameras:
259
+ print(f"Importing {len(recon.images)} cameras...")
260
+ for img_id, image in recon.images.items():
261
+ if image.camera_id in recon.cameras:
262
+ cam = recon.cameras[image.camera_id]
263
+ cam_obj = create_camera_object(image, cam, col, scale=camera_scale)
264
+ cam_obj.parent = root_obj
265
+ # Keep the same world matrix when parenting if the root has no rotation yet
266
+ # or just set it relative to parent.
267
+ # Since we set root rotation ABOVE, we should set matrix_world AFTER parenting
268
+ # or use matrix_local if we want it to be relative.
269
+ # The create_camera_object returns an object with matrix_world set for the SfM space.
270
+ # If we parent it to root_obj, and then set matrix_world again,
271
+ # it will stay in the correct spot while being a child.
272
+
273
+ print("Done.")
274
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: vibephysics
3
- Version: 0.2.2
3
+ Version: 0.2.3
4
4
  Summary: Physics simulation and annotation tools for Blender
5
5
  Author-email: tsunyi <tsunyi@mimiaigen.com>
6
6
  Project-URL: Homepage, https://github.com/yourusername/vibephysics
@@ -33,6 +33,47 @@ Requires-Dist: ruff; extra == "dev"
33
33
 
34
34
  **A lightweight Blender physics simulation framework for creating realistic robot animations, rigid body physics, water dynamics, and comprehensive annotation tools — all running efficiently on CPU.**
35
35
 
36
+ ## ⚙️ Installation (MacOS)
37
+
38
+ ```bash
39
+ # 1. Create environment
40
+ conda create -n vibephysics python=3.11
41
+ conda activate vibephysics
42
+
43
+ # 2. Install core package (includes COLMAP mapping & Blender simulation)
44
+ pip install vibephysics
45
+
46
+ # 3. (Optional) Install GLOMAP backend
47
+ # Linux users: refer to "Linux System Dependencies" below first
48
+ pip install git+https://github.com/shamangary/glomap.git
49
+ ```
50
+
51
+ ## 🐧 Linux (Ubuntu) System Dependencies
52
+ If you are on Linux and want to use the **GLOMAP** or **COLMAP** backends, you must install the following C++ development libraries to enable successful compilation:
53
+
54
+ ```bash
55
+ sudo apt-get update
56
+ sudo apt-get install -y \
57
+ libeigen3-dev \
58
+ libceres-dev \
59
+ libgoogle-glog-dev \
60
+ libboost-all-dev \
61
+ libsuitesparse-dev \
62
+ libsqlite3-dev \
63
+ libgflags-dev \
64
+ libfreeimage-dev \
65
+ libmetis-dev
66
+ ```
67
+
68
+ ## ⚠️ Troubleshooting (Linker Errors)
69
+ If you are using **Anaconda** on Linux and see an error like `relocation R_X86_64_TPOFF32 ... can not be used when making a shared object`, it is due to a conflict with the Anaconda linker. Fix it by forcing the compiler to use the global-dynamic TLS model:
70
+
71
+ ```bash
72
+ export CXXFLAGS="$CXXFLAGS -fPIC -ftls-model=global-dynamic"
73
+ export CFLAGS="$CFLAGS -fPIC"
74
+ pip install git+https://github.com/shamangary/glomap.git
75
+ ```
76
+
36
77
  ## 🎬 Example Results (`sh run_robot.sh`)
37
78
 
38
79
  ![Result Demo](assets/result_demo.gif)
@@ -93,37 +134,6 @@ Perfect for researchers, animators, and robotics engineers who need physics simu
93
134
  - **Open Duck**: We use the [Open Duck blender model](https://github.com/pollen-robotics/Open_Duck_Blender) as demo. We do not own the model. Please refer to the original github repo.
94
135
  - **Unitree Go2**: We use the [Unitree Go2 USD model](https://huggingface.co/datasets/unitreerobotics/unitree_model). The model is auto-downloaded when running Go2 examples. We do not own the model.
95
136
 
96
- ## ⚙️ Installation
97
-
98
- ```bash
99
- # Create and activate environment
100
- conda create -n vibephysics python=3.11
101
- conda activate vibephysics
102
-
103
- # Install vibephysics
104
- pip install vibephysics
105
-
106
- # Or install from source
107
- git clone https://github.com/mimiaigen/vibephysics
108
- cd vibephysics
109
- pip install -e .
110
- ```
111
-
112
- ### 🗺️ Mapping & Reconstruction
113
- The core mapping tools are now built-in!
114
-
115
- 1. **Incremental COLMAP**: Available immediately after `pip install vibephysics`.
116
- 2. **Global GLOMAP**: Just run your first mapping task! `vibephysics` will automatically prompt and install the optimized GLOMAP backend from GitHub.
117
- *(Note: This requires a one-time C++ build which takes a few minutes).*
118
-
119
- ### Requirements for Simulations
120
- If you intend to run physics simulations, you also need to install Blender's Python module:
121
-
122
- ```bash
123
- # Install Blender module
124
- pip install bpy
125
- ```
126
-
127
137
  ## Quick Start
128
138
 
129
139
  ```bash
@@ -310,7 +320,7 @@ VibePhysics integrates high-performance Structure-from-Motion (SfM) engines to c
310
320
  from vibephysics import mapping
311
321
 
312
322
  # 1. Simple Usage (Only image_path is REQUIRED)
313
- # Defaults: glomap engine, exhaustive matcher, SIMPLE_RADIAL camera
323
+ # Defaults: glomap engine, exhaustive matcher, PINHOLE camera
314
324
  mapping.glomap_pipeline(image_path="path/to/images")
315
325
 
316
326
  # 2. COLMAP Incremental Pipeline
@@ -322,7 +332,7 @@ mapping.glomap_pipeline(
322
332
  output_path="output/dir", # Optional: Defaults to image_path/../mapping_output/
323
333
  database_path="path/to/database.db", # Optional: Defaults to output_path/sparse/database.db
324
334
  matcher="exhaustive", # Optional: "exhaustive" (default) or "sequential"
325
- camera_model="SIMPLE_RADIAL", # Optional: "PINHOLE", "SIMPLE_RADIAL" (default), "OPENCV", etc.
335
+ camera_model="PINHOLE", # Optional: "PINHOLE" (default), "SIMPLE_RADIAL", "OPENCV", etc.
326
336
  verbose=True # Optional: Set to False to suppress logs
327
337
  )
328
338
  ```
@@ -333,7 +343,27 @@ mapping.glomap_pipeline(
333
343
  | **`output_path`** | No | `mapping_output/` | Directory for results. Creates `sparse/0` and symlinked `images/`. |
334
344
  | **`database_path`** | No | `database.db` | Optional path to an existing COLMAP database. |
335
345
  | **`matcher`** | No | `exhaustive` | Matching algorithm: `exhaustive` or `sequential`. |
336
- | **`camera_model`** | No | `SIMPLE_RADIAL` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
346
+ | **`camera_model`** | No | `PINHOLE` | COLMAP camera model (e.g., `PINHOLE`, `OPENCV`). |
347
+
348
+ ### 🎨 Visualization in Blender
349
+
350
+ You can load your Colmap/GLOMAP reconstruction directly into Blender for inspection, featuring colored point clouds with high-visibility Geometry Node spheres and correct camera poses.
351
+
352
+ ```python
353
+ from vibephysics import mapping
354
+
355
+ # Load a sparse model folder (containing cameras.bin, points3D.bin etc.)
356
+ mapping.load_colmap_reconstruction(
357
+ input_path="output/mapping_output/sparse/0",
358
+ point_size=0.01 # Adjust point blob size for visibility
359
+ )
360
+ ```
361
+
362
+ **Run the Demo:**
363
+ ```bash
364
+ # Visualize an existing reconstruction
365
+ python examples/colmap_format/demo_glomap.py --sparse /path/to/sparse/0 --point-size 0.02
366
+ ```
337
367
 
338
368
  ## Gaussian Splatting (3DGS) (BETA)
339
369
 
@@ -36,6 +36,7 @@ src/vibephysics/foundation/water.py
36
36
  src/vibephysics/mapping/__init__.py
37
37
  src/vibephysics/mapping/colmap.py
38
38
  src/vibephysics/mapping/glomap.py
39
+ src/vibephysics/mapping/map_visual.py
39
40
  src/vibephysics/mapping/pipeline.py
40
41
  src/vibephysics/mapping/utils.py
41
42
  src/vibephysics/setup/__init__.py
File without changes
File without changes
File without changes