Last active
February 12, 2020 20:55
-
-
Save garyo/e0fe3605d2be7c6308d795c988ec3b4b to your computer and use it in GitHub Desktop.
Patch to improve speed of glTF2 import into Blender
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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