def gather_image(blender_image, export_settings): if not blender_image: return None name, _extension = os.path.splitext( os.path.basename(blender_image.filepath)) if export_settings["gltf_image_format"] == "AUTO": if blender_image.file_format == "HDR": mime_type = "image/vnd.radiance" else: mime_type = "image/png" else: mime_type = "image/jpeg" data = HubsExportImage.from_blender_image(blender_image).encode(mime_type) if export_settings[gltf2_blender_export_keys.FORMAT] == 'GLTF_SEPARATE': uri = HubsImageData(data=data, mime_type=mime_type, name=name) buffer_view = None else: uri = None buffer_view = BinaryData(data=data) return Image(buffer_view=buffer_view, extensions=None, extras=None, mime_type=mime_type, name=name, uri=uri) # export_user_extensions('gather_image_hook', export_settings, image, blender_shader_sockets) return None
def __compress_primitive(primitive, dll, export_settings): attributes = primitive.attributes # Begin mesh. compressor = dll.createCompressor() # Process position attributes. dll.addPositionAttribute(compressor, attributes['POSITION'].count, attributes['POSITION'].buffer_view.data) # Process normal attributes. dll.addNormalAttribute(compressor, attributes['NORMAL'].count, attributes['NORMAL'].buffer_view.data) # Process texture coordinate attributes. for attribute in [ attributes[attr] for attr in attributes if attr.startswith('TEXCOORD_') ]: dll.addTexCoordAttribute(compressor, attribute.count, attribute.buffer_view.data) # Process faces. index_byte_length = { 'Byte': 1, 'UnsignedByte': 1, 'Short': 2, 'UnsignedShort': 2, 'UnsignedInt': 4, } indices = primitive.indices dll.setFaces(compressor, indices.count, index_byte_length[indices.component_type.name], indices.buffer_view.data) # Set compression parameters. dll.setCompressionLevel( compressor, export_settings['gltf_draco_mesh_compression_level']) dll.setPositionQuantizationBits( compressor, export_settings['gltf_draco_position_quantization']) dll.setNormalQuantizationBits( compressor, export_settings['gltf_draco_normal_quantization']) dll.setTexCoordQuantizationBits( compressor, export_settings['gltf_draco_texcoord_quantization']) # After all point and connectivity data has been written to the compressor, # it can finally be compressed. if dll.compress(compressor): # Compression was successful. # Move compressed data into a bytes object, # which is referenced by a 'gltf2_io_binary_data.BinaryData': # # "KHR_draco_mesh_compression": { # .... # "buffer_view": Compressed data inside a 'gltf2_io_binary_data.BinaryData'. # } # Query size necessary to hold all the compressed data. compression_size = dll.compressedSize(compressor) # Allocate byte buffer and write compressed data to it. compressed_data = bytes(compression_size) dll.copyToBytes(compressor, compressed_data) if primitive.extensions is None: primitive.extensions = {} tex_coord_ids = {} for id in range(0, dll.getTexCoordAttributeIdCount(compressor)): tex_coord_ids["TEXCOORD_" + str(id)] = dll.getTexCoordAttributeId( compressor, id) # Register draco compression extension into primitive. primitive.extensions["KHR_draco_mesh_compression"] = { 'bufferView': BinaryData(compressed_data), 'attributes': { 'POSITION': dll.getPositionAttributeId(compressor), 'NORMAL': dll.getNormalAttributeId(compressor), **tex_coord_ids, } } # Set to triangle list mode. primitive.mode = 4 # Afterwards, the compressor can be released. dll.disposeCompressor(compressor) pass
def __encode_primitive(primitive, dll, export_settings, encoded_primitives_cache): attributes = primitive.attributes indices = primitive.indices # Check if this primitive has already been encoded. # This usually happens when nodes are duplicated in Blender, thus their indices/attributes are shared data. if primitive in encoded_primitives_cache: if primitive.extensions is None: primitive.extensions = {} primitive.extensions['KHR_draco_mesh_compression'] = encoded_primitives_cache[primitive] return # Only do TRIANGLES primitives if primitive.mode not in [None, 4]: return if 'POSITION' not in attributes: print_console('WARNING', 'Draco encoder: Primitive without positions encountered. Skipping.') return positions = attributes['POSITION'] # Skip nodes without a position buffer, e.g. a primitive from a Blender shared instance. if attributes['POSITION'].buffer_view is None: return encoder = dll.encoderCreate(positions.count) draco_ids = {} for attr_name in attributes: attr = attributes[attr_name] draco_id = dll.encoderSetAttribute(encoder, attr_name.encode(), attr.component_type, attr.type.encode(), attr.buffer_view.data) draco_ids[attr_name] = draco_id dll.encoderSetIndices(encoder, indices.component_type, indices.count, indices.buffer_view.data) dll.encoderSetCompressionLevel(encoder, export_settings['gltf_draco_mesh_compression_level']) dll.encoderSetQuantizationBits(encoder, export_settings['gltf_draco_position_quantization'], export_settings['gltf_draco_normal_quantization'], export_settings['gltf_draco_texcoord_quantization'], export_settings['gltf_draco_color_quantization'], export_settings['gltf_draco_generic_quantization']) preserve_triangle_order = primitive.targets is not None and len(primitive.targets) > 0 if not dll.encoderEncode(encoder, preserve_triangle_order): print_console('ERROR', 'Could not encode primitive. Skipping primitive.') byte_length = dll.encoderGetByteLength(encoder) encoded_data = bytes(byte_length) dll.encoderCopy(encoder, encoded_data) if primitive.extensions is None: primitive.extensions = {} extension_info = { 'bufferView': BinaryData(encoded_data), 'attributes': draco_ids } primitive.extensions['KHR_draco_mesh_compression'] = extension_info encoded_primitives_cache[primitive] = extension_info # Set to triangle list mode. primitive.mode = 4 # Update accessors to match encoded data. indices.count = dll.encoderGetEncodedIndexCount(encoder) encoded_vertices = dll.encoderGetEncodedVertexCount(encoder) for attr_name in attributes: attributes[attr_name].count = encoded_vertices dll.encoderRelease(encoder)
def __compress_primitive(primitive, dll, export_settings): attributes = primitive.attributes indices = primitive.indices # Maps component types to their byte length. component_type_byte_length = { 'Byte': 1, 'UnsignedByte': 1, 'Short': 2, 'UnsignedShort': 2, 'UnsignedInt': 4, } # Positions are the only attribute type required to be present. if 'POSITION' not in attributes: print_console( 'WARNING', 'Draco exporter: Primitive without positions encountered. Skipping.' ) return positions = attributes['POSITION'] normals = attributes['NORMAL'] if 'NORMAL' in attributes else None uvs = [ attributes[attr] for attr in attributes if attr.startswith('TEXCOORD_') ] weights = [ attributes[attr] for attr in attributes if attr.startswith('WEIGHTS_') ] joints = [ attributes[attr] for attr in attributes if attr.startswith('JOINTS_') ] print_console( 'INFO', 'Draco exporter: %s normals, %d uvs, %d weights, %d joints' % ('without' if normals is None else 'with', len(uvs), len(weights), len(joints))) # Begin mesh. compressor = dll.create_compressor() # Each attribute must have the same count of elements. count = positions.count # Add attributes to mesh compressor, remembering each attribute's Draco id. position_id = dll.add_positions_f32(compressor, count, positions.buffer_view.data) normal_id = None if normals is not None: if normals.count != count: print_console( 'INFO', 'Draco exporter: Mismatching normal count. Skipping.') dll.disposeCompressor(compressor) return normal_id = dll.add_normals_f32(compressor, normals.count, normals.buffer_view.data) uv_ids = [] for uv in uvs: if uv.count != count: print_console('INFO', 'Draco exporter: Mismatching uv count. Skipping.') dll.disposeCompressor(compressor) return uv_ids.append( dll.add_uvs_f32(compressor, uv.count, uv.buffer_view.data)) weight_ids = [] for weight in weights: if weight.count != count: print_console( 'INFO', 'Draco exporter: Mismatching weight count. Skipping.') dll.disposeCompressor(compressor) return weight_ids.append( dll.add_weights_f32(compressor, weight.count, weight.buffer_view.data)) joint_ids = [] for joint in joints: if joint.count != count: print_console( 'INFO', 'Draco exporter: Mismatching joint count. Skipping.') dll.disposeCompressor(compressor) return joint_ids.append( dll.add_joints_u16(compressor, joint.count, joint.buffer_view.data)) # Add face indices to mesh compressor. dll.set_faces(compressor, indices.count, component_type_byte_length[indices.component_type.name], indices.buffer_view.data) # Set compression parameters. dll.set_compression_level( compressor, export_settings['gltf_draco_mesh_compression_level']) dll.set_position_quantization( compressor, export_settings['gltf_draco_position_quantization']) dll.set_normal_quantization( compressor, export_settings['gltf_draco_normal_quantization']) dll.set_uv_quantization( compressor, export_settings['gltf_draco_texcoord_quantization']) dll.set_generic_quantization( compressor, export_settings['gltf_draco_generic_quantization']) compress_fn = dll.compress if not primitive.targets else dll.compress_morphed # After all point and connectivity data has been written to the compressor, # it can finally be compressed. if dll.compress(compressor): # Compression was successful. # Move compressed data into a bytes object, # which is referenced by a 'gltf2_io_binary_data.BinaryData': # # "KHR_draco_mesh_compression": { # .... # "buffer_view": Compressed data inside a 'gltf2_io_binary_data.BinaryData'. # } # Query size necessary to hold all the compressed data. compression_size = dll.get_compressed_size(compressor) # Allocate byte buffer and write compressed data to it. compressed_data = bytes(compression_size) dll.copy_to_bytes(compressor, compressed_data) if primitive.extensions is None: primitive.extensions = {} # Write Draco extension into primitive, including attribute ids: extension = { 'bufferView': BinaryData(compressed_data), 'attributes': { 'POSITION': position_id } } if normals is not None: extension['attributes']['NORMAL'] = normal_id for (k, id) in enumerate(uvs): extension['attributes']['TEXCOORD_' + str(k)] = id for (k, id) in enumerate(weight_ids): extension['attributes']['WEIGHTS_' + str(k)] = id for (k, id) in enumerate(joint_ids): extension['attributes']['JOINTS_' + str(k)] = id primitive.extensions['KHR_draco_mesh_compression'] = extension # Remove buffer views from the accessors of the attributes which compressed: positions.buffer_view = None if normals is not None: normals.buffer_view = None for uv in uvs: uv.buffer_view = None for weight in weights: weight.buffer_view = None for joint in joints: joint.buffer_view = None # Set to triangle list mode. primitive.mode = 4 # Afterwards, the compressor can be released. dll.destroy_compressor(compressor) return
def __encode_primitive(primitive, dll, export_settings): attributes = primitive.attributes indices = primitive.indices if 'POSITION' not in attributes: print_console( 'WARNING', 'Draco encoder: Primitive without positions encountered. Skipping.' ) return positions = attributes['POSITION'] # Skip nodes without a position buffer, e.g. a primitive from a Blender shared instance. if attributes['POSITION'].buffer_view is None: return encoder = dll.encoderCreate(positions.count) draco_ids = {} for attr_name in attributes: attr = attributes[attr_name] draco_id = dll.encoderSetAttribute(encoder, attr_name.encode(), attr.component_type, attr.type.encode(), attr.buffer_view.data) draco_ids[attr_name] = draco_id attr.buffer_view = None dll.encoderSetIndices(encoder, indices.component_type, indices.count, indices.buffer_view.data) indices.buffer_view = None dll.encoderSetCompressionLevel( encoder, export_settings['gltf_draco_mesh_compression_level']) dll.encoderSetQuantizationBits( encoder, export_settings['gltf_draco_position_quantization'], export_settings['gltf_draco_normal_quantization'], 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.') byte_length = dll.encoderGetByteLength(encoder) encoded_data = bytes(byte_length) dll.encoderCopy(encoder, encoded_data) if primitive.extensions is None: primitive.extensions = {} primitive.extensions['KHR_draco_mesh_compression'] = { 'bufferView': BinaryData(encoded_data), 'attributes': draco_ids } # Set to triangle list mode. primitive.mode = 4 # Update accessors to match encoded data. indices.count = dll.encoderGetEncodedIndexCount(encoder) encoded_vertices = dll.encoderGetEncodedVertexCount(encoder) for attr_name in attributes: attributes[attr_name].count = encoded_vertices dll.encoderRelease(encoder)
def __compress_primitive(primitive, dll, export_settings): attributes = primitive.attributes # Positions are the only attribute type required to be present. if 'POSITION' not in attributes: print_console( 'WARNING', 'Draco exporter: Primitive without positions encountered. Skipping.' ) pass # Both, normals and texture coordinates are optional attribute types. enable_normals = 'NORMAL' in attributes tex_coord_attrs = [ attributes[attr] for attr in attributes if attr.startswith('TEXCOORD_') ] print_console('INFO', ( 'Draco exporter: Compressing primitive %s normal attribute and with %d ' + 'texture coordinate attributes, along with positions.') % ('with' if enable_normals else 'without', len(tex_coord_attrs))) # Begin mesh. compressor = dll.createCompressor() # Process position attributes. dll.addPositionAttribute(compressor, attributes['POSITION'].count, attributes['POSITION'].buffer_view.data) # Process normal attributes. if enable_normals: dll.addNormalAttribute(compressor, attributes['NORMAL'].count, attributes['NORMAL'].buffer_view.data) # Process texture coordinate attributes. for attribute in tex_coord_attrs: dll.addTexCoordAttribute(compressor, attribute.count, attribute.buffer_view.data) # Process faces. index_byte_length = { 'Byte': 1, 'UnsignedByte': 1, 'Short': 2, 'UnsignedShort': 2, 'UnsignedInt': 4, } indices = primitive.indices dll.setFaces(compressor, indices.count, index_byte_length[indices.component_type.name], indices.buffer_view.data) # Set compression parameters. dll.setCompressionLevel( compressor, export_settings['gltf_draco_mesh_compression_level']) dll.setPositionQuantizationBits( compressor, export_settings['gltf_draco_position_quantization']) dll.setNormalQuantizationBits( compressor, export_settings['gltf_draco_normal_quantization']) dll.setTexCoordQuantizationBits( compressor, export_settings['gltf_draco_texcoord_quantization']) # After all point and connectivity data has been written to the compressor, # it can finally be compressed. if dll.compress(compressor): # Compression was successful. # Move compressed data into a bytes object, # which is referenced by a 'gltf2_io_binary_data.BinaryData': # # "KHR_draco_mesh_compression": { # .... # "buffer_view": Compressed data inside a 'gltf2_io_binary_data.BinaryData'. # } # Query size necessary to hold all the compressed data. compression_size = dll.compressedSize(compressor) # Allocate byte buffer and write compressed data to it. compressed_data = bytes(compression_size) dll.copyToBytes(compressor, compressed_data) if primitive.extensions is None: primitive.extensions = {} # Register draco compression extension into primitive. extension = { 'bufferView': BinaryData(compressed_data), 'attributes': { 'POSITION': dll.getPositionAttributeId(compressor) } } if enable_normals: extension['attributes']['NORMAL'] = dll.getNormalAttributeId( compressor) for id in range(0, dll.getTexCoordAttributeIdCount(compressor)): extension['attributes']['TEXCOORD_' + str(id)] = dll.getTexCoordAttributeId( compressor, id) primitive.extensions['KHR_draco_mesh_compression'] = extension # Set to triangle list mode. primitive.mode = 4 # Afterwards, the compressor can be released. dll.disposeCompressor(compressor) pass