c-d-a / io_export_qmap

.map exporter for Blender
GNU General Public License v3.0
84 stars 18 forks source link

[Request] Import Conversion #22

Closed samstommy closed 1 month ago

samstommy commented 1 month ago

Like the title say, could we request a version of the script to import instead, are there any plans for it in sight?

c-d-a commented 1 month ago

I have a work-in-progress version, mostly for testing UV code. It only handles brush geometry, so the usefulness is limited (since editors often allow you to export to obj directly). It does not import materials, so ideally you'll want all the materials in the scene before import. I've ran into some Blender bugs, so the plan is to see if they get fixed for v4.2, and maybe finalize the importer then. Depending on what the new "extension platform" will look like in 4.2, I'll also decide if import/export should be a combined repo or two separate ones. I also have some optimizations and rewrites for the exporter, but I've been putting them off for months now. I'll try to squeeze them in by the end of the week. Closing this one, but feel free to post if you run into problems.

samstommy commented 1 month ago

Thanks alot for the support, i've tried with 3.6. It seams at the moment creates a base mesh of the map. Maybe you dont need it however made a script for importing the textures based on the materials name since they're the same. at least for quake.

material

Script 1: Q4TextureFinder.py This is to be called by the material wilst being added, it searches in the main folder path of quake 4 first if its not there or doesn't exist it looks over the related .PAK files

import zipfile
import os
import bpy

# .pk4 files with textures
pk4_files = [
    "pak010.pk4", "pak011.pk4", "pak012.pk4",
    "pak013.pk4", "pak014.pk4", "pak016.pk4", "pak019.pk4"
]

# Preferred file type
preferred_extension = ".tga"

def search_in_base_path(base_path, search_path):
    full_path = os.path.join(base_path, search_path)
    if os.path.exists(full_path):
        print(f"Found in base path: {full_path}")
        return [full_path]
    return None

def search_in_pk4_files(pk4_files, search_path):
    matches = []
    for pk4_file in pk4_files:
        with zipfile.ZipFile(pk4_file, 'r') as pk4:
            file_list = pk4.namelist()
            matches.extend([file for file in file_list if file.startswith(search_path)])
            if matches:
                print(f"Found matches in {pk4_file}:")
                for match in matches:
                    print(match)
                # Check for preferred file type
                preferred_file = next((file for file in matches if file.endswith(preferred_extension)), None)
                if preferred_file:
                    print(f"Preferred file found: {preferred_file}")
                    return preferred_file
    return None

def extract_and_import_texture(pk4, file_path, material_name):
    temp_dir = bpy.app.tempdir
    temp_file_path = os.path.join(temp_dir, os.path.basename(file_path))
    with pk4.open(file_path) as source, open(temp_file_path, 'wb') as target:
        target.write(source.read())

    # Import
    if temp_file_path.endswith(".tga") or temp_file_path.endswith(".dds"):
        img = bpy.data.images.load(temp_file_path)
        print(f"Imported texture: {img.name}")

        # Apply
        material = bpy.data.materials.get(material_name)
        if material:
            apply_texture_to_material(material, img)
    else:
        print(f"File type of {temp_file_path} is not supported for import into Blender.")

def apply_texture_to_material(material, texture):
    # Enable 'Use Nodes' for the material
    material.use_nodes = True
    bsdf = material.node_tree.nodes.get('Principled BSDF')

    if not bsdf:
        bsdf = material.node_tree.nodes.new('ShaderNodeBsdfPrincipled')

    # Create a new texture node
    tex_image = material.node_tree.nodes.new('ShaderNodeTexImage')
    tex_image.image = texture

    # Link the texture
    material.node_tree.links.new(bsdf.inputs['Base Color'], tex_image.outputs['Color'])

def search_and_apply_texture_to_material(base_path, material_name):
    # Check first the base path
    result = search_in_base_path(base_path, material_name)

    # If not found using .pk4 files instead
    if not result:
        pk4_paths = [os.path.join(base_path, pk4) for pk4 in pk4_files]
        preferred_file = search_in_pk4_files(pk4_paths, material_name)
        if preferred_file:
            # Extract and import texture
            with zipfile.ZipFile(preferred_file, 'r') as pk4:
                extract_and_import_texture(pk4, preferred_file, material_name)

    print("Search and application process completed.")

# Example usage:
#base_path = "/Q4/q4base/"
#material_name = "textures/example_material"
#search_and_apply_texture_to_material(base_path, material_name)

Script 2: Q4TextureMaterials.py After the map is imported it looks over the material names and searches for the respective textures

import zipfile
import os
import bpy

# Base path and .pk4 files
base_path = "/Q4/q4base/"
pk4_files = [
    "pak010.pk4", "pak011.pk4", "pak012.pk4",
    "pak013.pk4", "pak014.pk4", "pak016.pk4", "pak019.pk4"
]

# Preferred file type
preferred_extension = ".tga"

def search_in_base_path(base_path, search_path):
    full_path = os.path.join(base_path, search_path)
    if os.path.exists(full_path):
        print(f"Found in base path: {full_path}")
        return [full_path]
    return None

def search_in_pk4_files(pk4_files, search_path):
    matches = []
    for pk4_file in pk4_files:
        with zipfile.ZipFile(pk4_file, 'r') as pk4:
            file_list = pk4.namelist()
            matches.extend([file for file in file_list if file.startswith(search_path)])
            if matches:
                print(f"Found matches in {pk4_file}:")
                for match in matches:
                    print(match)
                # Check preferred file type
                preferred_file = next((file for file in matches if file.endswith(preferred_extension)), None)
                if preferred_file:
                    print(f"Preferred file found: {preferred_file}")
                    extract_and_import_texture(pk4, preferred_file, search_path)
                return matches
    return matches

def extract_and_import_texture(pk4, file_path, material_name):
    temp_dir = bpy.app.tempdir
    temp_file_path = os.path.join(temp_dir, os.path.basename(file_path))
    with pk4.open(file_path) as source, open(temp_file_path, 'wb') as target:
        target.write(source.read())

    # Import
    if temp_file_path.endswith(".tga") or temp_file_path.endswith(".dds"):
        img = bpy.data.images.load(temp_file_path)
        print(f"Imported texture: {img.name}")

        # Apply
        material = bpy.data.materials.get(material_name)
        if material:
            apply_texture_to_material(material, img)
    else:
        print(f"File type of {temp_file_path} is not supported for import into Blender.")

def apply_texture_to_material(material, texture):
    # Enable 'Use Nodes' for the material
    material.use_nodes = True
    bsdf = material.node_tree.nodes.get('Principled BSDF')

    if not bsdf:
        bsdf = material.node_tree.nodes.new('ShaderNodeBsdfPrincipled')

    # Create a new texture node
    tex_image = material.node_tree.nodes.new('ShaderNodeTexImage')
    tex_image.image = texture

    # Link the texture node to the BSDF node
    material.node_tree.links.new(bsdf.inputs['Base Color'], tex_image.outputs['Color'])

def main():
    # Seach all materials
    for material in bpy.data.materials:
        if material.name.startswith("textures"):
            search_path = material.name
            print(f"Searching for material: {search_path}")

            # Check first the base path
            result = search_in_base_path(base_path, search_path)

            # If not found using .pk4 files instead
            if not result:
                pk4_paths = [os.path.join(base_path, pk4) for pk4 in pk4_files]
                result = search_in_pk4_files(pk4_paths, search_path)

    print("Search and application process completed.")

# Run the main function
main()
c-d-a commented 1 month ago

Some notes, not necessarily for you, but also for people potentially stumbling upon this: