Skip to content

Instantly share code, notes, and snippets.

@garyo
Last active February 12, 2020 20:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save garyo/e0fe3605d2be7c6308d795c988ec3b4b to your computer and use it in GitHub Desktop.
Save garyo/e0fe3605d2be7c6308d795c988ec3b4b to your computer and use it in GitHub Desktop.
Patch to improve speed of glTF2 import into Blender
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
index 5904a974..ed652676 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_gltf.py
@@ -23,7 +23,26 @@ class BlenderGlTF():
raise RuntimeError("%s should not be instantiated" % cls)
@staticmethod
- def create(gltf):
+ def create(gltf, profile=True):
+ """create method with optional profiling"""
+ if profile:
+ import cProfile, pstats, io
+ from pstats import SortKey
+ pr = cProfile.Profile()
+ pr.enable()
+ result = BlenderGlTF._create(gltf)
+ pr.disable()
+ s = io.StringIO()
+ sortby = SortKey.CUMULATIVE
+ ps = pstats.Stats(pr, stream=s).sort_stats(sortby)
+ ps.print_stats()
+ print(s.getvalue())
+ return result
+ else:
+ return BlenderGlTF._create(gltf)
+
+ @staticmethod
+ def _create(gltf):
"""Create glTF main method."""
if bpy.context.scene.render.engine not in ['CYCLES', 'BLENDER_EEVEE']:
bpy.context.scene.render.engine = 'BLENDER_EEVEE'
@@ -327,4 +346,3 @@ class BlenderGlTF():
suffix = '.%03d' % cntr
cntr += 1
-
diff --git a/io_scene_gltf2/blender/imp/gltf2_blender_primitive.py b/io_scene_gltf2/blender/imp/gltf2_blender_primitive.py
index 35e0d6d9..69d98294 100755
--- a/io_scene_gltf2/blender/imp/gltf2_blender_primitive.py
+++ b/io_scene_gltf2/blender/imp/gltf2_blender_primitive.py
@@ -14,6 +14,8 @@
import bpy
from mathutils import Vector
+import numpy as np
+import time
from .gltf2_blender_material import BlenderMaterial
from ..com.gltf2_blender_conversion import loc_gltf_to_blender
@@ -129,26 +131,34 @@ class BlenderPrimitive():
layer_name = 'COLOR_%d' % set_num
layer = BlenderPrimitive.get_layer(bme.loops.layers.color, layer_name)
- colors = BinaryData.get_data_from_accessor(gltf, attributes[layer_name], cache=True)
+ # colors is a 2d array: [N][3 or 4]
+ colors_raw = BinaryData.get_data_from_accessor(gltf, attributes[layer_name], cache=True)
+ colors = np.array(colors_raw, dtype=np.float32)
# Check whether Blender takes RGB or RGBA colors (old versions only take RGB)
num_components = len(colors[0])
blender_num_components = len(bme_verts[0].link_loops[0][layer])
if num_components == 3 and blender_num_components == 4:
# RGB -> RGBA
- colors = [color+(1,) for color in colors]
+ ones = np.ones((colors.shape[0], 1))
+ colors = np.concatenate((colors, ones), axis=1) # add alpha channel
if num_components == 4 and blender_num_components == 3:
# RGBA -> RGB
- colors = [color[:3] for color in colors]
+ colors = colors[:, :-1]
gltf2_io_debug.print_console("WARNING",
"this Blender doesn't support RGBA vertex colors; dropping A"
)
-
+ srgb_colors = color_linear_to_srgb(colors)
+ # t = time.perf_counter()
+ # This needs to be a tight loop because it runs over all vertices,
+ # which is why this code looks a little odd.
for bidx, pidx in vert_idxs:
+ color = srgb_colors[pidx]
+ if blender_num_components == 4: # most common case
+ col = (color[0], color[1], color[2], color[3]) # fastest this way
+ else:
+ col = tuple(c for c in color)
for loop in bme_verts[bidx].link_loops:
- loop[layer] = tuple(
- color_linear_to_srgb(c)
- for c in colors[pidx]
- )
-
+ loop[layer] = col
+ # print(f'store colors: {time.perf_counter() - t}')
set_num += 1
# Set texcoords
@@ -290,4 +300,3 @@ class BlenderPrimitive():
raise Exception('primitive mode unimplemented: %d' % mode)
return es, fs
-
diff --git a/io_scene_gltf2/io/com/gltf2_io_color_management.py b/io_scene_gltf2/io/com/gltf2_io_color_management.py
index b1ebdb4a..932b7753 100644
--- a/io_scene_gltf2/io/com/gltf2_io_color_management.py
+++ b/io_scene_gltf2/io/com/gltf2_io_color_management.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import numpy as np
def color_srgb_to_scene_linear(c):
"""
@@ -29,9 +30,49 @@ def color_linear_to_srgb(c):
Convert from linear to sRGB color space.
Source: Cycles addon implementation, node_color.h.
+ c may be a single color value or an array.
"""
- if c < 0.0031308:
- return 0.0 if c < 0.0 else c * 12.92
+ if type(c) in ([], np.ndarray):
+ colors = np.array(c, np.float32)
+ not_small = colors >= 0.0031308
+ small_result = np.where(colors<0.0, 0.0, colors * 12.92)
+ large_result = 1.055 * np.power(colors, 1.0 / 2.4, where=not_small) - 0.055
+ return np.where(not_small, large_result, small_result)
else:
- return 1.055 * pow(c, 1.0 / 2.4) - 0.055
+ if c < 0.0031308:
+ return 0.0 if c < 0.0 else c * 12.92
+ else:
+ return 1.055 * pow(c, 1.0 / 2.4) - 0.055
+def test_color_linear_to_srgb():
+ """Ensure the array version gives the right results and is fast"""
+ from pytest import approx
+ from numpy.random import default_rng
+ import time
+
+ rng = default_rng()
+ a = rng.uniform(-10, 100, size=1000000)
+ # Should be fast with numpy
+ t0 = time.perf_counter()
+ srgb = color_linear_to_srgb(a)
+ assert time.perf_counter() - t0 < 0.1 # in sec
+ assert a.shape == srgb.shape
+ # Just compare some of them, for speed
+ for i in range(0, min(10000, len(a))):
+ assert srgb[i] == approx(color_linear_to_srgb(a[i]))
+
+def test_color_linear_to_srgb_2d():
+ """Ensure it works with a 2d array of colors where each element is RGB/RGBA"""
+ from pytest import approx
+
+ a = np.array([[0, 1, 2], [2, 3, 4]], dtype=np.float32)
+ srgb = color_linear_to_srgb(a)
+ assert a.shape == srgb.shape
+ for i in range(len(a.flatten())):
+ assert srgb.flatten()[i] == approx(color_linear_to_srgb(a.flatten()[i]))
+
+ a = np.array([[0, 1, 2, 0.1], [2, 3, 4, 0.5]], dtype=np.float32)
+ srgb = color_linear_to_srgb(a)
+ assert a.shape == srgb.shape
+ for i in range(len(a.flatten())):
+ assert srgb.flatten()[i] == approx(color_linear_to_srgb(a.flatten()[i]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment