Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add preliminary support for loose edges and points #717

Merged
merged 9 commits into from
Feb 24, 2021
37 changes: 34 additions & 3 deletions addons/io_scene_gltf2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,22 @@ def __init__(self):
default=True
)

use_mesh_edges: BoolProperty(
name='Loose Edges',
description=(
'Export loose edges as lines, using the material from the first material slot'
),
default=False,
)

use_mesh_vertices: BoolProperty(
name='Loose Points',
description=(
'Export loose points as glTF points, using the material from the first material slot'
),
default=False,
)

export_cameras: BoolProperty(
name='Cameras',
description='Export cameras',
Expand Down Expand Up @@ -439,10 +455,18 @@ def invoke(self, context, event):
return ExportHelper.invoke(self, context, event)

def save_settings(self, context):
# find all export_ props
# find all props to save
exceptional = [
# options that don't start with 'export_'
'use_selection',
'use_mesh_edges',
'use_mesh_vertices',
]
all_props = self.properties
export_props = {x: getattr(self, x) for x in dir(all_props)
if (x.startswith("export_") or x == "use_selection") and all_props.get(x) is not None}
export_props = {
x: getattr(self, x) for x in dir(all_props)
if (x.startswith("export_") or x in exceptional) and all_props.get(x) is not None
}

context.scene[self.scene_key] = export_props

Expand Down Expand Up @@ -474,6 +498,8 @@ def execute(self, context):
export_settings['gltf_texcoords'] = self.export_texcoords
export_settings['gltf_normals'] = self.export_normals
export_settings['gltf_tangents'] = self.export_tangents and self.export_normals
export_settings['gltf_loose_edges'] = self.use_mesh_edges
export_settings['gltf_loose_points'] = self.use_mesh_vertices

if self.is_draco_available:
export_settings['gltf_draco_mesh_compression'] = self.export_draco_mesh_compression_enable
Expand Down Expand Up @@ -687,6 +713,11 @@ def draw(self, context):
col.active = operator.export_normals
col.prop(operator, 'export_tangents')
layout.prop(operator, 'export_colors')

col = layout.column()
col.prop(operator, 'use_mesh_edges')
col.prop(operator, 'use_mesh_vertices')

layout.prop(operator, 'export_materials')
col = layout.column()
col.active = operator.export_materials == "EXPORT"
Expand Down
84 changes: 84 additions & 0 deletions addons/io_scene_gltf2/blender/exp/gltf2_blender_extract.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,90 @@ def extract_primitives(glTF, blender_mesh, library, blender_object, blender_vert
'material': material_idx,
})

if export_settings['gltf_loose_edges']:
# Find loose edges
loose_edges = [e for e in blender_mesh.edges if e.is_loose]
blender_idxs = [vi for e in loose_edges for vi in e.vertices]

if blender_idxs:
# Export one glTF vert per unique Blender vert in a loose edge
blender_idxs = np.array(blender_idxs, dtype=np.uint32)
blender_idxs, indices = np.unique(blender_idxs, return_inverse=True)

attributes = {}

attributes['POSITION'] = locs[blender_idxs]

for morph_i, vs in enumerate(morph_locs):
attributes['MORPH_POSITION_%d' % morph_i] = vs[blender_idxs]

if skin:
joints = [[] for _ in range(num_joint_sets)]
weights = [[] for _ in range(num_joint_sets)]

for vi in blender_idxs:
bones = vert_bones[vi]
for j in range(0, 4 * num_joint_sets):
if j < len(bones):
joint, weight = bones[j]
else:
joint, weight = 0, 0.0
joints[j//4].append(joint)
weights[j//4].append(weight)

for i, (js, ws) in enumerate(zip(joints, weights)):
attributes['JOINTS_%d' % i] = js
attributes['WEIGHTS_%d' % i] = ws

primitives.append({
'attributes': attributes,
'indices': indices,
'mode': 1, # LINES
'material': 0,
})

donmccurdy marked this conversation as resolved.
Show resolved Hide resolved
if export_settings['gltf_loose_points']:
# Find loose points
verts_in_edge = set(vi for e in blender_mesh.edges for vi in e.vertices)
blender_idxs = [
vi for vi, _ in enumerate(blender_mesh.vertices)
if vi not in verts_in_edge
]

if blender_idxs:
blender_idxs = np.array(blender_idxs, dtype=np.uint32)

attributes = {}

attributes['POSITION'] = locs[blender_idxs]

for morph_i, vs in enumerate(morph_locs):
attributes['MORPH_POSITION_%d' % morph_i] = vs[blender_idxs]

if skin:
joints = [[] for _ in range(num_joint_sets)]
weights = [[] for _ in range(num_joint_sets)]

for vi in blender_idxs:
bones = vert_bones[vi]
for j in range(0, 4 * num_joint_sets):
if j < len(bones):
joint, weight = bones[j]
else:
joint, weight = 0, 0.0
joints[j//4].append(joint)
weights[j//4].append(weight)

for i, (js, ws) in enumerate(zip(joints, weights)):
attributes['JOINTS_%d' % i] = js
attributes['WEIGHTS_%d' % i] = ws

primitives.append({
'attributes': attributes,
'mode': 0, # POINTS
'material': 0,
})

print_console('INFO', 'Primitives created: %d' % len(primitives))

return primitives
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ def gather_primitives(
material_idx = internal_primitive['material']
material = None

if export_settings['gltf_materials'] == "EXPORT":
if export_settings['gltf_materials'] == "EXPORT" and material_idx is not None:
blender_material = None
if material_names:
i = material_idx if material_idx < len(material_names) else -1
Expand All @@ -73,7 +73,7 @@ def gather_primitives(
extras=None,
indices=internal_primitive['indices'],
material=material,
mode=None,
mode=internal_primitive['mode'],
targets=internal_primitive['targets']
)
primitives.append(primitive)
Expand Down Expand Up @@ -101,15 +101,18 @@ def __gather_cache_primitives(
primitive = {
"attributes": __gather_attributes(internal_primitive, blender_mesh, modifiers, export_settings),
"indices": __gather_indices(internal_primitive, blender_mesh, modifiers, export_settings),
"material": internal_primitive['material'],
"mode": internal_primitive.get('mode'),
"material": internal_primitive.get('material'),
"targets": __gather_targets(internal_primitive, blender_mesh, modifiers, export_settings)
}
primitives.append(primitive)

return primitives

def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings):
indices = blender_primitive['indices']
indices = blender_primitive.get('indices')
if indices is None:
return None

# NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed.
# Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def encode_scene_primitives(scenes, export_settings):

dll.encoderSetQuantizationBits.restype = None
dll.encoderSetQuantizationBits.argtypes = [c_void_p, c_uint32, c_uint32, c_uint32, c_uint32, c_uint32]

dll.encoderSetIndices.restype = None
dll.encoderSetIndices.argtypes = [c_void_p, c_size_t, c_uint32, c_void_p]

Expand Down Expand Up @@ -85,6 +85,10 @@ def __encode_primitive(primitive, dll, export_settings):
attributes = primitive.attributes
indices = primitive.indices

# Only do TRIANGLES primitives
if primitive.mode not in [None, 4]:
return
donmccurdy marked this conversation as resolved.
Show resolved Hide resolved

if 'POSITION' not in attributes:
print_console('WARNING', 'Draco encoder: Primitive without positions encountered. Skipping.')
return
Expand Down Expand Up @@ -114,7 +118,7 @@ def __encode_primitive(primitive, dll, export_settings):
export_settings['gltf_draco_texcoord_quantization'],
export_settings['gltf_draco_color_quantization'],
export_settings['gltf_draco_generic_quantization'])

if not dll.encoderEncode(encoder, primitive.targets is not None and len(primitive.targets) > 0):
print_console('ERROR', 'Could not encode primitive. Skipping primitive.')

Expand All @@ -124,7 +128,7 @@ def __encode_primitive(primitive, dll, export_settings):

if primitive.extensions is None:
primitive.extensions = {}

primitive.extensions['KHR_draco_mesh_compression'] = {
'bufferView': BinaryData(encoded_data),
'attributes': draco_ids
Expand Down
Binary file added tests/scenes/11_loose_geometry.blend
Binary file not shown.
15 changes: 15 additions & 0 deletions tests/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,21 @@ describe('Exporter', function() {
assert.deepStrictEqual(rounded, [0, 0, 1]);
}
});

it('exports loose edges/points', function() {
let gltfPath = path.resolve(outDirPath, '11_loose_geometry.gltf');
const asset = JSON.parse(fs.readFileSync(gltfPath));
assert.strictEqual(asset.meshes.length, 1);

const prims = asset.meshes[0].primitives;
let tri_prims = prims.filter(prim => prim.mode === 4 || prim.mode === undefined);
let edge_prims = prims.filter(prim => prim.mode === 1);
let point_prims = prims.filter(prim => prim.mode === 0);

assert.strictEqual(tri_prims.length, 1);
assert.strictEqual(edge_prims.length, 1);
assert.strictEqual(point_prims.length, 1);
})
});
});
});
Expand Down