Skip to content

Commit

Permalink
refined codes, added comments
Browse files Browse the repository at this point in the history
  • Loading branch information
matyalatte committed Jan 19, 2023
1 parent af537d8 commit 58d5458
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 57 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ htmlcov
!tests/cube.dds
*.tga
*.png
*.jpg
*.hdr
*.xcf

Expand Down
76 changes: 37 additions & 39 deletions addons/blender_dds_addon/directx/texconv.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,6 @@ def load_dll(self, dll_path=None):
dll_path = os.path.join(dirname, dll_name)
dll_path2 = os.path.join(os.path.dirname(dirname), dll_name) # allow ../texconv.dll

dll_path = os.path.abspath(dll_path)

if not os.path.exists(dll_path):
if os.path.exists(dll_path2):
dll_path = dll_path2
Expand All @@ -43,28 +41,6 @@ def load_dll(self, dll_path=None):

self.dll = ctypes.cdll.LoadLibrary(dll_path)

def texconv(self, file, args, out=None, verbose=True, allow_slow_codec=False):
"""Run texconv."""
if out is not None and isinstance(out, str):
args += ['-o', out]
else:
out = '.'

if out not in ['.', ''] and not os.path.exists(out):
util.mkdir(out)

args += ["-y"]
args += [os.path.normpath(file)]

args_p = [ctypes.c_wchar_p(arg) for arg in args]
args_p = (ctypes.c_wchar_p*len(args_p))(*args_p)
err_buf = ctypes.create_unicode_buffer(512)
result = self.dll.texconv(len(args), args_p, verbose, False, allow_slow_codec, err_buf, 512)
if result != 0:
raise RuntimeError(err_buf.value)

return out

def convert_to_tga(self, file, out=None, cubemap_layout="h-cross", invert_normals=False, verbose=True):
"""Convert dds to tga."""
dds_header = DDSHeader.read_from_file(file)
Expand Down Expand Up @@ -98,14 +74,14 @@ def convert_to_tga(self, file, out=None, cubemap_layout="h-cross", invert_normal
if invert_normals:
args += ['-inverty']

out = self.__texconv(file, args, out=out, verbose=verbose)

name = os.path.join(out, os.path.basename(file))
name = ".".join(name.split(".")[:-1] + [fmt])

if dds_header.is_cube():
name = os.path.join(out, os.path.basename(file))
name = ".".join(name.split(".")[:-1] + [fmt])
self.cube_to_image(file, name, args, cubemap_layout=cubemap_layout, verbose=verbose)
else:
out = self.texconv(file, args, out=out, verbose=verbose)
name = os.path.join(out, os.path.basename(file))
name = '.'.join(name.split('.')[:-1] + [fmt])
self.__cube_to_image(file, name, args, cubemap_layout=cubemap_layout, verbose=verbose)

return name

def convert_to_dds(self, file, dds_fmt, out=None,
Expand Down Expand Up @@ -145,27 +121,49 @@ def convert_to_dds(self, file, dds_fmt, out=None,
temp_args = ['-f', 'rgba']
with tempfile.TemporaryDirectory() as temp_dir:
temp_dds = os.path.join(temp_dir, base_name)
self.image_to_cube(file, temp_dds, temp_args, cubemap_layout=cubemap_layout, verbose=verbose)
out = self.texconv(temp_dds, args, out=out, verbose=verbose, allow_slow_codec=allow_slow_codec)
self.__image_to_cube(file, temp_dds, temp_args, cubemap_layout=cubemap_layout, verbose=verbose)
out = self.__texconv(temp_dds, args, out=out, verbose=verbose, allow_slow_codec=allow_slow_codec)
else:
out = self.texconv(file, args, out=out, verbose=verbose, allow_slow_codec=allow_slow_codec)
out = self.__texconv(file, args, out=out, verbose=verbose, allow_slow_codec=allow_slow_codec)
name = os.path.join(out, base_name)
return name

def cube_to_image(self, file, new_file, args, cubemap_layout="h-cross", verbose=True):
def __texconv(self, file, args, out=None, verbose=True, allow_slow_codec=False):
"""Run texconv."""
if out is not None and isinstance(out, str):
args += ['-o', out]
else:
out = '.'

if out not in ['.', ''] and not os.path.exists(out):
util.mkdir(out)

args += ["-y"]
args += [os.path.normpath(file)]

args_p = [ctypes.c_wchar_p(arg) for arg in args]
args_p = (ctypes.c_wchar_p*len(args_p))(*args_p)
err_buf = ctypes.create_unicode_buffer(512)
result = self.dll.texconv(len(args), args_p, verbose, False, allow_slow_codec, err_buf, 512)
if result != 0:
raise RuntimeError(err_buf.value)

return out

def __cube_to_image(self, file, new_file, args, cubemap_layout="h-cross", verbose=True):
"""Genarate an image from a cubemap with texassemble."""
if cubemap_layout.endswith("-fnz"):
cubemap_layout = cubemap_layout[:-4]
args = [cubemap_layout] + args
self.texassemble(file, new_file, args, verbose=verbose)
self.__texassemble(file, new_file, args, verbose=verbose)

def image_to_cube(self, file, new_file, args, cubemap_layout="h-cross", verbose=True):
def __image_to_cube(self, file, new_file, args, cubemap_layout="h-cross", verbose=True):
"""Generate a cubemap from an image with texassemble."""
cmd = "cube-from-" + cubemap_layout[0] + cubemap_layout[2]
args = [cmd] + args
self.texassemble(file, new_file, args, verbose=verbose)
self.__texassemble(file, new_file, args, verbose=verbose)

def texassemble(self, file, new_file, args, verbose=True):
def __texassemble(self, file, new_file, args, verbose=True):
"""Run texassemble."""
out = os.path.dirname(new_file)
if out not in ['.', ''] and not os.path.exists(out):
Expand Down
4 changes: 4 additions & 0 deletions addons/blender_dds_addon/ui/bpy_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
import bpy


def flush_stdout():
print("", end="", flush=True)


def dds_properties_exist():
return hasattr(bpy.types.Image, "dds_props")

Expand Down
11 changes: 9 additions & 2 deletions addons/blender_dds_addon/ui/export_dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from ..directx.dds import is_hdr
from ..directx.texconv import Texconv
from .bpy_util import save_texture, dds_properties_exist, get_selected_tex
from .bpy_util import save_texture, dds_properties_exist, get_selected_tex, flush_stdout


def save_dds(tex, file, dds_fmt, invert_normals=False, no_mip=False,
Expand Down Expand Up @@ -135,12 +135,14 @@ def export_as_dds(context, tex, file):
dds_options = context.scene.dds_options

if dds_properties_exist():
# Use image's properties
props = tex.dds_props
dxgi = props.dxgi_format
is_cube = props.is_cube
cubemap_layout = props.cubemap_layout
no_mip = props.no_mip
else:
# Use scene's properties
dxgi = dds_options.dxgi_format
is_cube = dds_options.export_as_cubemap
cubemap_layout = dds_options.cubemap_layout
Expand Down Expand Up @@ -170,18 +172,21 @@ def put_export_options(context, layout):


class DDS_OT_export_base(Operator):
"""Base class for export operators."""

def draw(self, context):
"""Draw options for file picker."""
layout = self.layout
layout.use_property_split = False
layout.use_property_decorate = False # No animation.
layout.use_property_decorate = False
put_export_options(context, layout)

def execute_base(self, context, file=None, directory=None, is_dir=False):
try:
start_time = time.time()

if is_dir:
# For DDS_OT_export_all
count = 0
for tex in bpy.data.images:
if dds_properties_exist() and tex.dds_props.dxgi_format == "NONE":
Expand All @@ -191,8 +196,10 @@ def execute_base(self, context, file=None, directory=None, is_dir=False):
name += ".dds"
file = os.path.join(directory, name)
export_as_dds(context, tex, file)
flush_stdout()
count += 1
else:
# For DDS_OT_export_dds
tex = get_selected_tex(context)
export_as_dds(context, tex, file)
count = 1
Expand Down
44 changes: 28 additions & 16 deletions addons/blender_dds_addon/ui/import_dds.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from ..directx.dds import DDSHeader
from ..directx.texconv import Texconv
from .bpy_util import get_image_editor_space, load_texture, dds_properties_exist
from .bpy_util import get_image_editor_space, load_texture, dds_properties_exist, flush_stdout
from .custom_properties import DDS_FMT_NAMES


Expand All @@ -35,12 +35,13 @@ def load_dds(file, invert_normals=False, cubemap_layout='h-cross', texconv=None)
with tempfile.TemporaryDirectory() as temp_dir:
temp = os.path.join(temp_dir, os.path.basename(file))
shutil.copyfile(file, temp)

# Convert dds to tga
if texconv is None:
texconv = Texconv()

temp_tga = texconv.convert_to_tga(temp, out=temp_dir, cubemap_layout=cubemap_layout,
invert_normals=invert_normals)
if temp_tga is None: # if texconv doesn't exist
if temp_tga is None:
raise RuntimeError('Failed to convert texture.')

# Check dxgi_format
Expand All @@ -50,33 +51,38 @@ def load_dds(file, invert_normals=False, cubemap_layout='h-cross', texconv=None)
else:
color_space = 'Non-Color'

# Load tga file
tex = load_texture(temp_tga, name=os.path.basename(temp_tga)[:-4], color_space=color_space)

dxgi = dds_header.get_format_as_str()
if dds_properties_exist():
# Update custom properties
props = tex.dds_props
dxgi = dds_header.get_format_as_str()
if dxgi in DDS_FMT_NAMES:
props.dxgi_format = dds_header.get_format_as_str()
props.dxgi_format = dxgi
props.no_mip = dds_header.mipmap_num <= 1
props.is_cube = dds_header.is_cube()
if props.is_cube:
props.cubemap_layout = cubemap_layout

if cubemap_layout.endswith("-fnz"):
# Flip -z face for cubemaps
w, h = tex.size
pix = np.array(tex.pixels).reshape((h, w, -1))
if cubemap_layout[0] == "v":
pix[h//4 * 0: h//4 * 1, w//3 * 1: w//3 * 2] = (pix[h//4 * 0: h//4 * 1, w//3 * 1: w//3 * 2])[::-1, ::-1]
else:
pix[h//3 * 1: h//3 * 2, w//4 * 3: w//4 * 4] = (pix[h//3 * 1: h//3 * 2, w//4 * 3: w//4 * 4])[::-1, ::-1]
pix = pix.flatten()
tex.pixels = list(pix)

tex.update()

except Exception as e:
if tex is not None:
bpy.data.images.remove(tex)
raise e

if cubemap_layout.endswith("-fnz"):
w, h = tex.size
pix = np.array(tex.pixels).reshape((h, w, -1))
if cubemap_layout[0] == "v":
pix[h//4 * 0: h//4 * 1, w//3 * 1: w//3 * 2] = (pix[h//4 * 0: h//4 * 1, w//3 * 1: w//3 * 2])[::-1, ::-1]
else:
pix[h//3 * 1: h//3 * 2, w//4 * 3: w//4 * 4] = (pix[h//3 * 1: h//3 * 2, w//4 * 3: w//4 * 4])[::-1, ::-1]
pix = pix.flatten()
tex.pixels = list(pix)
tex.update()
return tex


Expand All @@ -90,6 +96,7 @@ def import_dds(context, file):


def import_dds_rec(context, folder, count=0):
"""Search a folder recursively, and import found dds files."""
for file in sorted(os.listdir(folder)):
if os.path.isdir(file):
count += import_dds_rec(context, os.path.join(folder, file), count=count)
Expand All @@ -100,11 +107,13 @@ def import_dds_rec(context, folder, count=0):


class DDS_OT_import_base(Operator):
"""Base class for imoprt operators."""

def draw(self, context):
"""Draw options for file picker."""
layout = self.layout
layout.use_property_split = False
layout.use_property_decorate = False # No animation.
layout.use_property_decorate = False
dds_options = context.scene.dds_options
layout.prop(dds_options, 'invert_normals')
layout.prop(dds_options, 'cubemap_layout')
Expand All @@ -116,11 +125,14 @@ def execute_base(self, context, files=None, directory=None, is_dir=False):
try:
start_time = time.time()
if is_dir:
# For DDS_OT_import_dir
count = import_dds_rec(context, directory)
else:
# For DDS_OT_import_dds
count = 0
for _, file in enumerate(files):
import_dds(context, os.path.join(directory, file.name))
flush_stdout()
count += 1
elapsed_s = f'{(time.time() - start_time):.2f}s'
if count == 0:
Expand Down

0 comments on commit 58d5458

Please sign in to comment.