예제 #1
0
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
예제 #3
0
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
예제 #5
0
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