новая структура проекта
This commit is contained in:
+440
@@ -0,0 +1,440 @@
|
||||
import io
|
||||
import os
|
||||
import traceback
|
||||
|
||||
import bpy
|
||||
|
||||
from ...utils.misc import load_game_data
|
||||
from ..utils.materials import load_texture
|
||||
from ...utils.node_builder import NodeTreeBuilder
|
||||
from ...pywowlib.io_utils.types import *
|
||||
from ...ui.preferences import get_project_preferences
|
||||
|
||||
|
||||
# This file is implementing basic M2 geometry parsing in prodedural style for the sake of performance.
|
||||
class Submesh:
|
||||
__slots__ = (
|
||||
"start_vertex",
|
||||
"n_vertices",
|
||||
"start_triangle",
|
||||
"n_triangles",
|
||||
"texture_id",
|
||||
"blend_mode"
|
||||
)
|
||||
|
||||
def __init__(self, f):
|
||||
skip(f, 4)
|
||||
self.start_vertex = uint16.read(f)
|
||||
self.n_vertices = uint16.read(f)
|
||||
self.start_triangle = uint16.read(f)
|
||||
self.n_triangles = uint16.read(f)
|
||||
self.texture_id = None
|
||||
self.blend_mode = 0
|
||||
|
||||
skip(f, 36)
|
||||
|
||||
|
||||
def skip(f, n_bytes):
|
||||
f.seek(f.tell() + n_bytes)
|
||||
|
||||
|
||||
def import_doodad_model(asset_dir: str, filepath: str, placeholder: bool) -> bpy.types.Object:
|
||||
"""Import World of Warcraft M2 model to scene."""
|
||||
|
||||
game_data = load_game_data()
|
||||
|
||||
if placeholder:
|
||||
m2_path = os.path.splitext('Spells\\Errorcube.m2')[0] + ".m2"
|
||||
skin_path = os.path.splitext('Spells\\Errorcube.m2')[0] + "00.skin"
|
||||
real_m2_path = os.path.splitext(filepath)[0] + ".m2"
|
||||
m2_name = os.path.basename(os.path.splitext(real_m2_path)[0])
|
||||
else:
|
||||
m2_path = os.path.splitext(filepath)[0] + ".m2"
|
||||
skin_path = os.path.splitext(filepath)[0] + "00.skin"
|
||||
m2_name = os.path.basename(os.path.splitext(m2_path)[0])
|
||||
|
||||
try:
|
||||
file, _ = game_data.read_file(m2_path)
|
||||
m2_file = io.BytesIO(file)
|
||||
except KeyError:
|
||||
raise FileNotFoundError("\nModel <<{}>> not found in WoW file system.".format(filepath))
|
||||
|
||||
try:
|
||||
file, _ = game_data.read_file(skin_path)
|
||||
skin_file = io.BytesIO(file)
|
||||
except KeyError:
|
||||
raise FileNotFoundError("\nSkin file for model <<{}>> not found in WoW file system.".format(filepath))
|
||||
|
||||
###### M2 file parsing ######
|
||||
|
||||
f = m2_file
|
||||
magic = f.read(4).decode("utf-8")
|
||||
while magic not in ('MD20', 'MD21'):
|
||||
skip(f, uint32.read(f))
|
||||
magic = f.read(4).decode("utf-8")
|
||||
|
||||
# read flags
|
||||
f.seek(16)
|
||||
global_flags = uint32.read(f)
|
||||
|
||||
# read vertex positions
|
||||
f.seek(60)
|
||||
n_vertices = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
|
||||
vertices = [(0.0, 0.0, 0.0)] * n_vertices
|
||||
normals = [(0.0, 0.0, 0.0)] * n_vertices
|
||||
uv_coords = [(0.0, 0.0)] * n_vertices
|
||||
|
||||
# if n_vertices == 0:
|
||||
# addon_dir = os.path.dirname(__file__)
|
||||
# placeholder_blend_path = os.path.join(addon_dir, "utils", "placeholders", "placeholder.blend")
|
||||
# directory = placeholder_blend_path + "\\Object\\"
|
||||
# bpy.ops.wm.append(filename='placeholder', directory=directory)
|
||||
# return import_doodad_model(asset_dir, filepath, True)
|
||||
|
||||
for i in range(n_vertices):
|
||||
vertices[i] = float32.read(f, 3)
|
||||
skip(f, 8)
|
||||
normals[i] = float32.read(f, 3)
|
||||
uv_coords[i] = float32.read(f, 2)
|
||||
skip(f, 8)
|
||||
|
||||
# read render flags and blending modes
|
||||
f.seek(112)
|
||||
n_render_flags = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
|
||||
blend_modes = [] # skip render flags for now
|
||||
for _ in range(n_render_flags):
|
||||
skip(f, 2)
|
||||
blend_modes.append(uint16.read(f))
|
||||
|
||||
# read texture lookup
|
||||
f.seek(128)
|
||||
n_tex_lookups = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
texture_lookup_table = [uint16.read(f) for _ in range(n_tex_lookups)]
|
||||
|
||||
# read texture names
|
||||
f.seek(80)
|
||||
n_textures = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
|
||||
texture_paths = []
|
||||
for _ in range(n_textures):
|
||||
skip(f, 8)
|
||||
len_tex = uint32.read(f)
|
||||
ofs_tex = uint32.read(f)
|
||||
|
||||
if not len_tex:
|
||||
texture_paths.append(None)
|
||||
continue
|
||||
|
||||
pos = f.tell()
|
||||
f.seek(ofs_tex)
|
||||
texture_paths.append(f.read(len_tex).decode('utf-8').rstrip('\0'))
|
||||
f.seek(pos)
|
||||
|
||||
# read blend mode overrides
|
||||
if global_flags & 0x08:
|
||||
f.seek(304)
|
||||
n_blendmode_overrides = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
|
||||
blend_mode_overrides = []
|
||||
for _ in range(n_blendmode_overrides):
|
||||
blend_mode_overrides.append(uint16.read(f))
|
||||
|
||||
###### Skin ######
|
||||
|
||||
f = skin_file
|
||||
padding = 0
|
||||
|
||||
# indices
|
||||
try:
|
||||
magic = f.read(4).decode("utf-8")
|
||||
except UnicodeDecodeError:
|
||||
padding = 4
|
||||
f.seek(0)
|
||||
|
||||
n_indices = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
indices = unpack('{}H'.format(n_indices), f.read(2 * n_indices))
|
||||
|
||||
# triangles
|
||||
f.seek(12 - padding)
|
||||
n_triangles = uint32.read(f) // 3
|
||||
f.seek(uint32.read(f))
|
||||
triangles = [(0, 0, 0)] * n_triangles
|
||||
for i in range(n_triangles):
|
||||
triangles[i] = unpack('HHH', f.read(6))
|
||||
|
||||
# submeshes
|
||||
f.seek(28 - padding)
|
||||
n_submeshes = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
submeshes = [Submesh(f) for _ in range(n_submeshes)]
|
||||
|
||||
# texture units
|
||||
f.seek(36 - padding)
|
||||
n_tex_units = uint32.read(f)
|
||||
f.seek(uint32.read(f))
|
||||
for _ in range(n_tex_units):
|
||||
skip(f, 2)
|
||||
shader_id = uint16.read(f)
|
||||
submesh_id = uint16.read(f)
|
||||
|
||||
if submeshes[submesh_id].texture_id is not None:
|
||||
skip(f, 18)
|
||||
continue
|
||||
|
||||
skip(f, 4)
|
||||
render_flag_index = uint16.read(f)
|
||||
skip(f, 4)
|
||||
submeshes[submesh_id].texture_id = uint16.read(f)
|
||||
skip(f, 6)
|
||||
|
||||
submeshes[submesh_id].blend_mode = blend_modes[render_flag_index]
|
||||
|
||||
'''
|
||||
# determine blending mode
|
||||
if global_flags & 0x08 and shader_id < len(blend_mode_overrides):
|
||||
submeshes[submesh_id].blend_mode = blend_mode_overrides[shader_id]
|
||||
else:
|
||||
submeshes[submesh_id].blend_mode = blend_modes[render_flag_index]
|
||||
'''
|
||||
|
||||
###### Build blender object ######
|
||||
faces = [[0, 0, 0]] * n_triangles
|
||||
|
||||
for i, tri in enumerate(triangles):
|
||||
face = []
|
||||
for index in tri:
|
||||
face.append(indices[index])
|
||||
|
||||
faces[i] = face
|
||||
|
||||
# create mesh
|
||||
mesh = bpy.data.meshes.new(m2_name)
|
||||
mesh.from_pydata(vertices, [], faces)
|
||||
|
||||
for poly in mesh.polygons:
|
||||
poly.use_smooth = True
|
||||
|
||||
# set normals
|
||||
custom_normals = [(0.0, 0.0, 0.0)] * len(mesh.loops)
|
||||
mesh.use_auto_smooth = True
|
||||
for i, loop in enumerate(mesh.loops):
|
||||
custom_normals[i] = normals[loop.vertex_index]
|
||||
|
||||
mesh.normals_split_custom_set(custom_normals)
|
||||
|
||||
# set uv
|
||||
uv_layer1 = mesh.uv_layers.new(name="UVMap")
|
||||
for i in range(len(uv_layer1.data)):
|
||||
uv = uv_coords[mesh.loops[i].vertex_index]
|
||||
uv_layer1.data[i].uv = (uv[0], 1 - uv[1])
|
||||
|
||||
# unpack and convert textures
|
||||
game_data.extract_textures_as_png(asset_dir, texture_paths)
|
||||
|
||||
def import_placeholder(addon_relative_path, placeholder_object_name):
|
||||
current_dir = os.path.dirname(__file__)
|
||||
base_dir = os.path.dirname(os.path.dirname(current_dir))
|
||||
blend_path = os.path.join(base_dir, addon_relative_path)
|
||||
|
||||
directory = os.path.join(blend_path, "Object\\")
|
||||
|
||||
try:
|
||||
bpy.ops.wm.append(filename=placeholder_object_name, directory=directory)
|
||||
|
||||
#obj = bpy.ops.wm.append(filename=placeholder_object_name, directory=directory)
|
||||
if placeholder_object_name in bpy.data.objects:
|
||||
placeholder = bpy.data.objects.get(placeholder_object_name)
|
||||
nplaceholder = bpy.data.objects.new(m2_name, placeholder.data.copy())
|
||||
#placeholder_object_name.delete()
|
||||
#placeholder_duplicate = placeholder.copy()
|
||||
#placeholder_duplicate.data = placeholder.data.copy()
|
||||
bpy.data.objects.remove(placeholder, do_unlink=True)
|
||||
|
||||
return nplaceholder
|
||||
|
||||
except Exception as e:
|
||||
print(f"Failed to import placeholder: {e}")
|
||||
return None
|
||||
|
||||
# create object
|
||||
if len(mesh.vertices) == 0 or placeholder:
|
||||
placeholder_object_name = 'Placeholder'
|
||||
addon_relative_path = 'utils\\placeholder\\placeholder.blend'
|
||||
nobj = import_placeholder(addon_relative_path, placeholder_object_name)
|
||||
nobj.wow_wmo_doodad.path = filepath
|
||||
nobj.wow_wmo_doodad.enabled = True
|
||||
image_name = "Placeholder"
|
||||
|
||||
img = bpy.data.images.get('Placeholder_Texture')
|
||||
if not img:
|
||||
img = bpy.data.images.new(name=image_name, width=512, height=512, alpha=True, float_buffer=False)
|
||||
img.generated_type = 'UV_GRID'
|
||||
#img = bpy.data.images['Placeholder_Texture']
|
||||
|
||||
mat = bpy.data.materials.get('Placeholder')
|
||||
if mat:
|
||||
nobj.data.materials.append(mat)
|
||||
|
||||
for block in bpy.data.meshes:
|
||||
if block.users == 0:
|
||||
bpy.data.meshes.remove(block)
|
||||
|
||||
for block in bpy.data.materials:
|
||||
if block.users == 0:
|
||||
bpy.data.materials.remove(block)
|
||||
|
||||
for block in bpy.data.textures:
|
||||
if block.users == 0:
|
||||
bpy.data.textures.remove(block)
|
||||
|
||||
for block in bpy.data.images:
|
||||
if block.users == 0:
|
||||
bpy.data.images.remove(block)
|
||||
|
||||
return nobj
|
||||
else:
|
||||
mat = bpy.data.materials.new(name="Placeholder")
|
||||
|
||||
mat.use_nodes = True
|
||||
|
||||
node_tree = mat.node_tree
|
||||
tree_builder = NodeTreeBuilder(node_tree)
|
||||
|
||||
tex_image = tree_builder.add_node('ShaderNodeTexImage', "Texture", 0, 0)
|
||||
tex_image.image = img
|
||||
|
||||
doodad_color= tree_builder.add_node('ShaderNodeAttribute', "DoodadColor", 0, 2)
|
||||
|
||||
mix_rgb = tree_builder.add_node('ShaderNodeMixRGB', "ApplyColor", 1, 1)
|
||||
mix_rgb.inputs['Fac'].default_value = 1.0
|
||||
mix_rgb.blend_type = 'MULTIPLY'
|
||||
|
||||
transparent_bsdf = tree_builder.add_node('ShaderNodeBsdfTransparent', "Transparent", 2, 0)
|
||||
bsdf = tree_builder.add_node('ShaderNodeBsdfDiffuse', "Diffuse", 2, 2)
|
||||
|
||||
mix_shader = tree_builder.add_node('ShaderNodeMixShader', "MixShader", 3, 0)
|
||||
mix_shader.inputs['Fac'].default_value = 1.0
|
||||
|
||||
output = tree_builder.add_node('ShaderNodeOutputMaterial', "Output", 4, 1)
|
||||
|
||||
mat.node_tree.links.new(tex_image.outputs['Color'], mix_rgb.inputs['Color1'])
|
||||
|
||||
mat.node_tree.links.new(doodad_color.outputs['Color'], mix_rgb.inputs['Color2'])
|
||||
mat.node_tree.links.new(mix_rgb.outputs['Color'], bsdf.inputs['Color'])
|
||||
mat.node_tree.links.new(bsdf.outputs['BSDF'], mix_shader.inputs[2])
|
||||
mat.node_tree.links.new(transparent_bsdf.outputs['BSDF'], mix_shader.inputs[1])
|
||||
mat.node_tree.links.new(mix_shader.outputs['Shader'], output.inputs['Surface'])
|
||||
mat.blend_method = 'OPAQUE'
|
||||
|
||||
nobj.data.materials.append(mat)
|
||||
|
||||
return nobj
|
||||
|
||||
else:
|
||||
nobj = bpy.data.objects.new(m2_name, mesh)
|
||||
|
||||
nobj.wow_wmo_doodad.path = filepath
|
||||
nobj.wow_wmo_doodad.enabled = True
|
||||
|
||||
# set textures
|
||||
texture_dir = get_project_preferences().cache_dir_path
|
||||
textures = {}
|
||||
for i, submesh in enumerate(submeshes):
|
||||
# tex_path = os.path.splitext(texture_paths[texture_lookup_table[submesh.texture_id]])[0] + '.png'
|
||||
tex_path = texture_paths[texture_lookup_table[submesh.texture_id]]
|
||||
|
||||
# add support for unix filesystems
|
||||
if os.name != 'nt':
|
||||
tex_path = tex_path.replace('\\', '/')
|
||||
|
||||
img = None
|
||||
|
||||
# try:
|
||||
# img = bpy.data.images.load(os.path.join(asset_dir, tex_path), check_existing=True)
|
||||
# except RuntimeError:
|
||||
# traceback.print_exc()
|
||||
# print("\nFailed to load texture: <<{}>>. File is missing or corrupted.".format(tex_path))
|
||||
|
||||
try:
|
||||
img = load_texture(textures, tex_path, texture_dir)
|
||||
except:
|
||||
print("\nFailed to load texture: <<{}>>. File is missing or corrupted.".format(tex_path))
|
||||
pass
|
||||
|
||||
if img:
|
||||
for j in range(submesh.start_triangle // 3, (submesh.start_triangle + submesh.n_triangles) // 3):
|
||||
mesh.polygons[j].material_index = i
|
||||
|
||||
mat = bpy.data.materials.new(name="{}_{}".format(m2_name, i))
|
||||
|
||||
mat.use_nodes = True
|
||||
|
||||
node_tree = mat.node_tree
|
||||
tree_builder = NodeTreeBuilder(node_tree)
|
||||
|
||||
tex_image = tree_builder.add_node('ShaderNodeTexImage', "Texture", 0, 0)
|
||||
tex_image.image = img
|
||||
|
||||
# doodad_color = tree_builder.add_node('ShaderNodeRGB', "DoodadColor", 0, 2)
|
||||
|
||||
doodad_color= tree_builder.add_node('ShaderNodeAttribute', "DoodadColor", 0, 2)
|
||||
|
||||
mix_rgb = tree_builder.add_node('ShaderNodeMixRGB', "ApplyColor", 1, 1)
|
||||
mix_rgb.inputs['Fac'].default_value = 1.0
|
||||
mix_rgb.blend_type = 'MULTIPLY'
|
||||
|
||||
transparent_bsdf = tree_builder.add_node('ShaderNodeBsdfTransparent', "Transparent", 2, 0)
|
||||
bsdf = tree_builder.add_node('ShaderNodeBsdfDiffuse', "Diffuse", 2, 2)
|
||||
|
||||
mix_shader = tree_builder.add_node('ShaderNodeMixShader', "MixShader", 3, 0)
|
||||
mix_shader.inputs['Fac'].default_value = 1.0
|
||||
|
||||
output = tree_builder.add_node('ShaderNodeOutputMaterial', "Output", 4, 1)
|
||||
|
||||
mat.node_tree.links.new(tex_image.outputs['Color'], mix_rgb.inputs['Color1'])
|
||||
|
||||
if submesh.blend_mode not in (0, 3):
|
||||
mat.node_tree.links.new(tex_image.outputs['Alpha'], mix_shader.inputs['Fac'])
|
||||
|
||||
mat.node_tree.links.new(doodad_color.outputs['Color'], mix_rgb.inputs['Color2'])
|
||||
mat.node_tree.links.new(mix_rgb.outputs['Color'], bsdf.inputs['Color'])
|
||||
mat.node_tree.links.new(bsdf.outputs['BSDF'], mix_shader.inputs[2])
|
||||
mat.node_tree.links.new(transparent_bsdf.outputs['BSDF'], mix_shader.inputs[1])
|
||||
mat.node_tree.links.new(mix_shader.outputs['Shader'], output.inputs['Surface'])
|
||||
|
||||
|
||||
# configure blending
|
||||
if submesh.blend_mode == 0:
|
||||
mat.blend_method = 'OPAQUE'
|
||||
elif submesh.blend_mode == 1:
|
||||
mat.blend_method = 'CLIP'
|
||||
mat.alpha_threshold = 0.9
|
||||
else:
|
||||
mat.blend_method = 'BLEND'
|
||||
|
||||
mesh.materials.append(mat)
|
||||
|
||||
return nobj
|
||||
|
||||
|
||||
def import_doodad(m2_path: str, cache_path: str) -> bpy.types.Object:
|
||||
|
||||
try:
|
||||
obj = import_doodad_model(cache_path, m2_path, False)
|
||||
except:
|
||||
obj = import_doodad_model(cache_path, m2_path, True)
|
||||
traceback.print_exc()
|
||||
print("\nFailed to import model: <<{}>>. Placeholder is imported instead.".format(m2_path))
|
||||
|
||||
obj.name = os.path.splitext(m2_path.split('\\')[-1])[0]
|
||||
|
||||
return obj
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import bpy
|
||||
|
||||
|
||||
def create_fog_object(name='WoW Fog', radius=1.0, location=None, color=(1.0, 1.0, 1.0, 1.0)):
|
||||
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(radius=radius
|
||||
, location=bpy.context.scene.cursor.location if location is None else location
|
||||
)
|
||||
|
||||
fog = bpy.context.view_layer.objects.active
|
||||
fog.name = name
|
||||
|
||||
# applying real object transformation
|
||||
bpy.ops.object.shade_smooth()
|
||||
|
||||
mesh = fog.data
|
||||
material = bpy.data.materials.new(name=name)
|
||||
mesh.materials.append(material)
|
||||
|
||||
material.use_nodes = True
|
||||
node_tree = material.node_tree
|
||||
|
||||
for node in node_tree.nodes:
|
||||
node_tree.nodes.remove(node)
|
||||
|
||||
output = material.node_tree.nodes.new('ShaderNodeOutputMaterial')
|
||||
diffuse = material.node_tree.nodes.new('ShaderNodeBsdfDiffuse')
|
||||
transparent = material.node_tree.nodes.new('ShaderNodeBsdfTransparent')
|
||||
mix = material.node_tree.nodes.new('ShaderNodeMixShader')
|
||||
color_node = material.node_tree.nodes.new('ShaderNodeAttribute')
|
||||
|
||||
node_tree.links.new(output.inputs['Surface'], mix.outputs['Shader'])
|
||||
node_tree.links.new(mix.inputs[1], transparent.outputs['BSDF'])
|
||||
node_tree.links.new(mix.inputs[2], diffuse.outputs['BSDF'])
|
||||
node_tree.links.new(diffuse.inputs['Color'], color_node.outputs['Color'])
|
||||
|
||||
mix.inputs['Fac'].default_value = 0.3
|
||||
# diffuse.inputs['Color'].default_value = color
|
||||
# color_node.inputs('Type').
|
||||
color_node.attribute_name = 'wow_wmo_fog.color1'
|
||||
color_node.attribute_type = 'OBJECT'
|
||||
|
||||
fog.hide_set(False if "3" in bpy.context.scene.wow_visibility else True)
|
||||
|
||||
bpy.context.view_layer.active_layer_collection.collection.objects.unlink(fog)
|
||||
|
||||
return fog
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
import bpy
|
||||
import os
|
||||
from ...pywowlib.blp import BLP2PNG
|
||||
from typing import Dict
|
||||
|
||||
from ...utils.misc import load_game_data
|
||||
|
||||
# old: read from extracted cache images
|
||||
def load_texture_file(textures : dict, filepath : str, texture_dir : str) -> bpy.types.Image:
|
||||
new_filename = os.path.splitext(filepath)[0] + '.png'
|
||||
|
||||
if os.name != 'nt':
|
||||
new_filename = new_filename.replace('\\', '/')
|
||||
|
||||
texture = textures.get(filepath)
|
||||
|
||||
# if image is not loaded, do it
|
||||
if not texture:
|
||||
tex_img = bpy.data.images.load(os.path.join(texture_dir, new_filename))
|
||||
tex_img.wow_wmo_texture.path = filepath
|
||||
tex_img.name = os.path.basename(new_filename)
|
||||
texture = tex_img
|
||||
|
||||
textures[filepath] = texture
|
||||
|
||||
return texture
|
||||
|
||||
|
||||
def load_texture(textures : dict, filepath : str, texture_dir : str) -> bpy.types.Image:
|
||||
""" Load texture image from game data and add it to the blender scene """
|
||||
new_filename = os.path.splitext(filepath)[0] + '.png'
|
||||
|
||||
if os.name != 'nt':
|
||||
new_filename = new_filename.replace('\\', '/')
|
||||
|
||||
texture = textures.get(filepath)
|
||||
|
||||
if not texture:
|
||||
# try getting it from cache first ?
|
||||
save_path = os.path.join(texture_dir, new_filename)
|
||||
|
||||
# check file exists in the directory
|
||||
if os.path.isfile(save_path):
|
||||
# pretty much load_texture_file()
|
||||
tex_img = bpy.data.images.load(os.path.join(texture_dir, new_filename))
|
||||
texture = tex_img
|
||||
|
||||
else:
|
||||
# if not, load from game data and save it to directory.
|
||||
game_data = load_game_data()
|
||||
|
||||
result = game_data.read_file(filepath, "", 'blp', True)
|
||||
|
||||
if result is None:
|
||||
print("\nFailed to load texture: <<{}>> from gamedata.".format(filepath))
|
||||
return None
|
||||
|
||||
tex_img: bpy.types.Image = BLP2PNG().create_image(result[0])
|
||||
|
||||
# save it to the cache folder
|
||||
tex_img.save(filepath= save_path)
|
||||
tex_img.filepath = save_path
|
||||
|
||||
texture = tex_img
|
||||
|
||||
filepath = filepath.replace('/', '\\')
|
||||
texture.wow_wmo_texture.path = filepath
|
||||
texture.wow_m2_texture.path = filepath
|
||||
texture.name = os.path.basename(new_filename)
|
||||
|
||||
textures[filepath] = texture
|
||||
|
||||
return texture
|
||||
|
||||
|
||||
def add_ghost_material() -> bpy.types.Material:
|
||||
""" Add ghost material """
|
||||
|
||||
mat = bpy.data.materials.get("WowMaterial_ghost")
|
||||
if not mat:
|
||||
mat = bpy.data.materials.new("WowMaterial_ghost")
|
||||
mat.blend_method = 'BLEND'
|
||||
mat.use_nodes = True
|
||||
mat.node_tree.nodes.remove(mat.node_tree.nodes.get('Principled BSDF'))
|
||||
material_output = mat.node_tree.nodes.get('Material Output')
|
||||
transparent = mat.node_tree.nodes.new('ShaderNodeBsdfTransparent')
|
||||
mat.node_tree.links.new(material_output.inputs[0], transparent.outputs[0])
|
||||
mat.node_tree.nodes["Transparent BSDF"].inputs[0].default_value = (0.38, 0.89, 0.37, 1)
|
||||
|
||||
return mat
|
||||
@@ -0,0 +1,104 @@
|
||||
from typing import Union
|
||||
|
||||
from ...ui.preferences import get_project_preferences
|
||||
|
||||
|
||||
def wmv_get_last_wmo() -> Union[None, str]:
|
||||
"""Get the path of last WMO model from WoWModelViewer or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wmv_path:
|
||||
|
||||
lines = open(project_preferences.wmv_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Loading WMO' in line:
|
||||
return line[22:].rstrip("\n")
|
||||
|
||||
|
||||
def wmv_get_last_m2() -> Union[None, str]:
|
||||
"""Get the path of last M2 model from WoWModelViewer or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wmv_path:
|
||||
|
||||
lines = open(project_preferences.wmv_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Loading model:' in line:
|
||||
return line[25:].split(",", 1)[0].rstrip("\n")
|
||||
|
||||
|
||||
def wmv_get_last_texture() -> Union[None, str]:
|
||||
"""Get the path of last texture from WoWModelViewer or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wmv_path:
|
||||
|
||||
lines = open(project_preferences.wmv_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Loading texture' in line:
|
||||
return line[27:].rstrip("\n")
|
||||
|
||||
def wow_export_get_last_texture() -> Union[None,str]:
|
||||
"""Get the path of last texture from WoWExport or similar log."""
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wow_export_path:
|
||||
lines = open(project_preferences.wow_export_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Previewing texture file' in line:
|
||||
return line[35:].split(",", 1)[0].rstrip("\n")
|
||||
|
||||
def wow_export_get_last_m2() -> Union[None, str]:
|
||||
"""Get the path of last M2 model from WoWExport or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wow_export_path:
|
||||
|
||||
lines = open(project_preferences.wow_export_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Previewing model' in line:
|
||||
return line[28:].split(",", 1)[0].rstrip("\n")
|
||||
|
||||
def noggit_red_get_last_m2() -> Union[None, str]:
|
||||
"""Get the path of last M2 model from Noggit Red or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.noggit_red_path:
|
||||
|
||||
lines = open(project_preferences.noggit_red_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Loaded file' in line and 'm2' in line:
|
||||
start = line.find("'") + 1
|
||||
end = line.find(".m2") + 3
|
||||
return line[start:end]
|
||||
|
||||
def noggit_red_get_last_wmo() -> Union[None, str]:
|
||||
"""Get the path of last WMO model from Noggit Red or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.noggit_red_path:
|
||||
|
||||
lines = open(project_preferences.noggit_red_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Loaded file' in line and 'wmo' in line:
|
||||
start = line.find("'") + 1
|
||||
end = line.find(".wmo") + 4
|
||||
return line[start:end]
|
||||
|
||||
def wow_export_get_last_wmo() -> Union[None, str]:
|
||||
"""Get the path of last WMO model from WoWExport or similar log."""
|
||||
|
||||
project_preferences = get_project_preferences()
|
||||
if project_preferences.wow_export_path:
|
||||
|
||||
lines = open(project_preferences.wow_export_path).readlines()
|
||||
|
||||
for line in reversed(lines):
|
||||
if 'Previewing model' in line:
|
||||
return line[28:].split(",", 1)[0].rstrip("\n")
|
||||
Reference in New Issue
Block a user