mirror of
https://git.code.sf.net/p/quake/quakeforge
synced 2024-11-10 15:22:04 +00:00
Initial Blender 2.8 changes - it works, now it's time for improvements
This commit is contained in:
parent
8112c4e8b6
commit
ae86790e6c
3 changed files with 119 additions and 68 deletions
|
@ -24,14 +24,14 @@
|
|||
bl_info = {
|
||||
"name": "Quake MDL format",
|
||||
"author": "Bill Currie",
|
||||
"blender": (2, 6, 3),
|
||||
"blender": (2, 80, 0),
|
||||
"api": 35622,
|
||||
"location": "File > Import-Export",
|
||||
"description": "Import-Export Quake MDL (version 6) files. (.mdl)",
|
||||
"warning": "not even alpha",
|
||||
"warning": "still work in progress",
|
||||
"wiki_url": "",
|
||||
"tracker_url": "",
|
||||
# "support": 'OFFICIAL',
|
||||
# "support": 'OFFICIAL',
|
||||
"category": "Import-Export"}
|
||||
|
||||
# To support reload properly, try to access a package var, if it's there,
|
||||
|
@ -64,28 +64,35 @@ EFFECTS=(
|
|||
('EF_TRACER2', "Tracer 2", "Orange split trail + rotate"),
|
||||
('EF_TRACER3', "Tracer 3", "Purple split trail"),
|
||||
)
|
||||
'''
|
||||
class QFMDLSettings(bpy.types.PropertyGroup):
|
||||
# eyeposition = FloatVectorProperty(
|
||||
# name="Eye Position",
|
||||
# description="View possion relative to object origin")
|
||||
# synctype = EnumProperty(
|
||||
# items=SYNCTYPE,
|
||||
# name="Sync Type",
|
||||
# description="Add random time offset for automatic animations")
|
||||
# rotate = BoolProperty(
|
||||
# name="Rotate",
|
||||
# description="Rotate automatically (for pickup items)")
|
||||
# effects = EnumProperty(
|
||||
# items=EFFECTS,
|
||||
# name="Effects",
|
||||
# description="Particle trail effects")
|
||||
# #doesn't work :(
|
||||
# #script = PointerProperty(
|
||||
# # type=bpy.types.Object,
|
||||
# # name="Script",
|
||||
# # description="Script for animating frames and skins")
|
||||
|
||||
class QFMDLSettings(bpy.types.PropertyGroup):
|
||||
'''
|
||||
eyeposition = FloatVectorProperty(
|
||||
name="Eye Position",
|
||||
description="View possion relative to object origin")
|
||||
synctype = EnumProperty(
|
||||
items=SYNCTYPE,
|
||||
name="Sync Type",
|
||||
description="Add random time offset for automatic animations")
|
||||
rotate = BoolProperty(
|
||||
name="Rotate",
|
||||
description="Rotate automatically (for pickup items)")
|
||||
effects = EnumProperty(
|
||||
items=EFFECTS,
|
||||
name="Effects",
|
||||
description="Particle trail effects")
|
||||
#doesn't work :(
|
||||
#script = PointerProperty(
|
||||
# type=bpy.types.Object,
|
||||
# name="Script",
|
||||
# description="Script for animating frames and skins")
|
||||
# xform = BoolProperty(
|
||||
# name="Auto transform",
|
||||
# description="Auto-apply location/rotation/scale when exporting",
|
||||
# default=True)
|
||||
# md16 = BoolProperty(
|
||||
# name="16-bit",
|
||||
# description="16 bit vertex coordinates: QuakeForge only")
|
||||
|
||||
xform = BoolProperty(
|
||||
name="Auto transform",
|
||||
|
@ -94,10 +101,10 @@ class QFMDLSettings(bpy.types.PropertyGroup):
|
|||
md16 = BoolProperty(
|
||||
name="16-bit",
|
||||
description="16 bit vertex coordinates: QuakeForge only")
|
||||
'''
|
||||
script = StringProperty(
|
||||
name="Script",
|
||||
description="Script for animating frames and skins")
|
||||
description="Script for animating frames and skins")
|
||||
'''
|
||||
|
||||
class ImportMDL6(bpy.types.Operator, ImportHelper):
|
||||
'''Load a Quake MDL (v6) File'''
|
||||
|
@ -155,6 +162,7 @@ class ExportMDL6(bpy.types.Operator, ExportHelper):
|
|||
keywords = self.as_keywords (ignore=("check_existing", "filter_glob"))
|
||||
return export_mdl.export_mdl(self, context, **keywords)
|
||||
|
||||
'''
|
||||
class OBJECT_PT_MDLPanel(bpy.types.Panel):
|
||||
bl_space_type = 'PROPERTIES'
|
||||
bl_region_type = 'WINDOW'
|
||||
|
@ -176,6 +184,7 @@ class OBJECT_PT_MDLPanel(bpy.types.Panel):
|
|||
layout.prop(obj.qfmdl, "script")
|
||||
layout.prop(obj.qfmdl, "xform")
|
||||
layout.prop(obj.qfmdl, "md16")
|
||||
'''
|
||||
|
||||
def menu_func_import(self, context):
|
||||
self.layout.operator(ImportMDL6.bl_idname, text="Quake MDL (.mdl)")
|
||||
|
@ -184,21 +193,25 @@ def menu_func_import(self, context):
|
|||
def menu_func_export(self, context):
|
||||
self.layout.operator(ExportMDL6.bl_idname, text="Quake MDL (.mdl)")
|
||||
|
||||
classes = (
|
||||
#OBJECT_PT_MDLPanel,
|
||||
ImportMDL6,
|
||||
ExportMDL6
|
||||
)
|
||||
|
||||
def register():
|
||||
bpy.utils.register_module(__name__)
|
||||
|
||||
bpy.types.Object.qfmdl = PointerProperty(type=QFMDLSettings)
|
||||
|
||||
bpy.types.INFO_MT_file_import.append(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.append(menu_func_export)
|
||||
for cls in classes:
|
||||
bpy.utils.register_class(cls)
|
||||
|
||||
bpy.types.TOPBAR_MT_file_import.append(menu_func_import)
|
||||
bpy.types.TOPBAR_MT_file_export.append(menu_func_export)
|
||||
|
||||
def unregister():
|
||||
bpy.utils.unregister_module(__name__)
|
||||
for cls in classes:
|
||||
bpy.utils.unregister_class(cls)
|
||||
|
||||
bpy.types.INFO_MT_file_import.remove(menu_func_import)
|
||||
bpy.types.INFO_MT_file_export.remove(menu_func_export)
|
||||
bpy.types.TOPBAR_MT_file_import.remove(menu_func_import)
|
||||
bpy.types.TOPBAR_MT_file_export.remove(menu_func_export)
|
||||
|
||||
if __name__ == "__main__":
|
||||
register()
|
||||
|
|
|
@ -86,7 +86,7 @@ def null_skin(size):
|
|||
return skin
|
||||
|
||||
def active_uv(mesh):
|
||||
for uvt in mesh.uv_textures:
|
||||
for uvt in mesh.uv_layers:
|
||||
if uvt.active:
|
||||
return uvt
|
||||
return None
|
||||
|
@ -95,6 +95,18 @@ def make_skin(operator, mdl, mesh):
|
|||
uvt = active_uv(mesh)
|
||||
mdl.skinwidth, mdl.skinheight = (4, 4)
|
||||
skin = null_skin((mdl.skinwidth, mdl.skinheight))
|
||||
|
||||
mat = bpy.context.object.data.materials[0]
|
||||
allNodes = mat.node_tree.nodes
|
||||
|
||||
for node in allNodes:
|
||||
if node.type == "TEX_IMAGE":
|
||||
image = node.image
|
||||
mdl.skinwidth, mdl.skinheight = image.size
|
||||
skin = convert_image(image)
|
||||
mdl.skins.append(skin)
|
||||
|
||||
'''
|
||||
if (uvt and uvt.data and uvt.data[0].image):
|
||||
image = uvt.data[0].image
|
||||
if (uvt.data[0].image.size[0] and uvt.data[0].image.size[1]):
|
||||
|
@ -103,7 +115,9 @@ def make_skin(operator, mdl, mesh):
|
|||
else:
|
||||
operator.report({'WARNING'},
|
||||
"Texture '%s' invalid (missing?)." % image.name)
|
||||
|
||||
mdl.skins.append(skin)
|
||||
'''
|
||||
|
||||
def build_tris(mesh):
|
||||
# mdl files have a 1:1 relationship between stverts and 3d verts.
|
||||
|
@ -183,7 +197,7 @@ def calc_average_area(mdl):
|
|||
a = Vector(verts[0].r) - Vector(verts[1].r)
|
||||
b = Vector(verts[2].r) - Vector(verts[1].r)
|
||||
c = a.cross(b)
|
||||
totalarea += (c * c) ** 0.5 / 2.0
|
||||
totalarea += (c @ c) ** 0.5 / 2.0
|
||||
return totalarea / len(mdl.tris)
|
||||
|
||||
def get_properties(
|
||||
|
@ -301,7 +315,8 @@ def export_mdl(
|
|||
):
|
||||
|
||||
obj = context.active_object
|
||||
mesh = obj.to_mesh(context.scene, True, 'PREVIEW') #wysiwyg?
|
||||
#mesh = obj.to_mesh(context.scene, True, 'PREVIEW') #wysiwyg?
|
||||
mesh = obj.to_mesh(context.depsgraph, True, calc_undeformed=False)
|
||||
#if not check_faces(mesh):
|
||||
# operator.report({'ERROR'},
|
||||
# "Mesh has faces with more than 3 vertices.")
|
||||
|
@ -334,7 +349,7 @@ def export_mdl(
|
|||
curframe = context.scene.frame_current
|
||||
for fno in range(context.scene.frame_start, context.scene.frame_end + 1):
|
||||
context.scene.frame_set(fno)
|
||||
mesh = obj.to_mesh(context.scene, True, 'PREVIEW') #wysiwyg?
|
||||
mesh = obj.to_mesh(context.depsgraph, True, calc_undeformed=False) #wysiwyg?
|
||||
if xform:
|
||||
mesh.transform(mdl.obj.matrix_world)
|
||||
mdl.frames.append(make_frame(mesh, vertmap))
|
||||
|
|
|
@ -39,7 +39,7 @@ def make_verts(mdl, framenum, subframenum=0):
|
|||
( 0, 0,s.z,o.z),
|
||||
( 0, 0, 0, 1)))
|
||||
for v in frame.verts:
|
||||
verts.append(m * Vector(v.r))
|
||||
verts.append(m @ Vector(v.r))
|
||||
return verts
|
||||
|
||||
def make_faces(mdl):
|
||||
|
@ -87,7 +87,7 @@ def load_skins(mdl):
|
|||
p[l + 2] = c[2] / 255.0
|
||||
p[l + 3] = 1.0
|
||||
img.pixels[:] = p[:]
|
||||
img.pack(True)
|
||||
img.pack(as_png=True)
|
||||
img.use_fake_user = True
|
||||
|
||||
mdl.images=[]
|
||||
|
@ -100,27 +100,47 @@ def load_skins(mdl):
|
|||
|
||||
def setup_skins(mdl, uvs):
|
||||
load_skins(mdl)
|
||||
img = mdl.images[0] # use the first skin for now
|
||||
uvlay = mdl.mesh.uv_textures.new(mdl.name)
|
||||
uvloop = mdl.mesh.uv_layers[0]
|
||||
for i, texpoly in enumerate(uvlay.data):
|
||||
# img = mdl.images[0] # use the first skin for now
|
||||
# uvlay = mdl.mesh.uv_textures.new(mdl.name)
|
||||
# uvloop = mdl.mesh.uv_layers[0]
|
||||
# for i, texpoly in enumerate(uvlay.data):
|
||||
uvloop = mdl.mesh.uv_layers.new(name = mdl.name)
|
||||
for i in range(len(mdl.mesh.polygons)):
|
||||
poly = mdl.mesh.polygons[i]
|
||||
mdl_uv = uvs[i]
|
||||
texpoly.image = img
|
||||
# texpoly.image = img # TODO: commented out by jazz
|
||||
for j,k in enumerate(poly.loop_indices):
|
||||
uvloop.data[k].uv = mdl_uv[j]
|
||||
|
||||
# Create main material
|
||||
mat = bpy.data.materials.new(mdl.name)
|
||||
mat.blend_method = 'OPAQUE'
|
||||
mat.diffuse_color = (1,1,1)
|
||||
mat.use_raytrace = False
|
||||
tex = bpy.data.textures.new(mdl.name, 'IMAGE')
|
||||
tex.extension = 'CLIP'
|
||||
tex.use_preview_alpha = True
|
||||
tex.image = img
|
||||
mat.texture_slots.add()
|
||||
ts = mat.texture_slots[0]
|
||||
ts.texture = tex
|
||||
ts.use_map_alpha = True
|
||||
ts.texture_coords = 'UV'
|
||||
mat.metallic = 1
|
||||
mat.roughness = 1
|
||||
mat.specular_intensity = 0
|
||||
mat.use_nodes = True
|
||||
|
||||
# TODO: turn transform to True and position it properly in editor
|
||||
emissionNode = mat.node_tree.nodes.new("ShaderNodeEmission")
|
||||
shaderOut = mat.node_tree.nodes["Material Output"]
|
||||
bdsf_node = mat.node_tree.nodes["Principled BSDF"]
|
||||
mat.node_tree.nodes.remove(bdsf_node)
|
||||
|
||||
#Add skingroup
|
||||
#bpy.ops.object.material_slot_add()
|
||||
#bpy.ops.material.new()
|
||||
|
||||
#Add all existing textures to shader node
|
||||
for i in range(len(mdl.images)):
|
||||
tex_node = mat.node_tree.nodes.new("ShaderNodeTexImage")
|
||||
tex_node.image = mdl.images[i]
|
||||
tex_node.interpolation = "Closest"
|
||||
if i == 0:
|
||||
# connect only first texture (we'll need something smarter in the future)
|
||||
mat.node_tree.links.new(tex_node.outputs[0], emissionNode.inputs[0])
|
||||
|
||||
mat.node_tree.links.new(emissionNode.outputs[0], shaderOut.inputs[0])
|
||||
mdl.mesh.materials.append(mat)
|
||||
|
||||
def make_shape_key(mdl, framenum, subframenum=0):
|
||||
|
@ -133,7 +153,7 @@ def make_shape_key(mdl, framenum, subframenum=0):
|
|||
name = frame.name
|
||||
else:
|
||||
frame.name = name
|
||||
frame.key = mdl.obj.shape_key_add(name)
|
||||
frame.key = mdl.obj.shape_key_add(name=name)
|
||||
frame.key.value = 0.0
|
||||
mdl.keys.append(frame.key)
|
||||
s = Vector(mdl.scale)
|
||||
|
@ -143,11 +163,11 @@ def make_shape_key(mdl, framenum, subframenum=0):
|
|||
( 0, 0,s.z,o.z),
|
||||
( 0, 0, 0, 1)))
|
||||
for i, v in enumerate(frame.verts):
|
||||
frame.key.data[i].co = m * Vector(v.r)
|
||||
frame.key.data[i].co = m @ Vector(v.r)
|
||||
|
||||
def build_shape_keys(mdl):
|
||||
mdl.keys = []
|
||||
mdl.obj.shape_key_add("Basis")
|
||||
mdl.obj.shape_key_add(name="Basis",from_mix=False)
|
||||
mdl.mesh.shape_keys.name = mdl.name
|
||||
mdl.obj.active_shape_key_index = 0
|
||||
for i, frame in enumerate(mdl.frames):
|
||||
|
@ -327,6 +347,7 @@ def parse_flags(flags):
|
|||
else:
|
||||
return 'EF_NONE'
|
||||
|
||||
'''
|
||||
def set_properties(mdl):
|
||||
mdl.obj.qfmdl.eyeposition = mdl.eyeposition
|
||||
try:
|
||||
|
@ -337,12 +358,13 @@ def set_properties(mdl):
|
|||
mdl.obj.qfmdl.effects = parse_flags(mdl.flags)
|
||||
mdl.obj.qfmdl.script = mdl.text.name #FIXME really want the text object
|
||||
mdl.obj.qfmdl.md16 = (mdl.ident == "MD16")
|
||||
'''
|
||||
|
||||
def import_mdl(operator, context, filepath):
|
||||
bpy.context.user_preferences.edit.use_global_undo = False
|
||||
def import_mdl(operator, context, filepath, **opts):
|
||||
bpy.context.preferences.edit.use_global_undo = False
|
||||
|
||||
for obj in bpy.context.scene.objects:
|
||||
obj.select = False
|
||||
for obj in bpy.context.scene.collection.objects:
|
||||
obj.select_set(False)
|
||||
|
||||
mdl = MDL()
|
||||
if not mdl.read(filepath):
|
||||
|
@ -354,18 +376,19 @@ def import_mdl(operator, context, filepath):
|
|||
mdl.mesh = bpy.data.meshes.new(mdl.name)
|
||||
mdl.mesh.from_pydata(verts, [], faces)
|
||||
mdl.obj = bpy.data.objects.new(mdl.name, mdl.mesh)
|
||||
bpy.context.scene.objects.link(mdl.obj)
|
||||
bpy.context.scene.objects.active = mdl.obj
|
||||
mdl.obj.select = True
|
||||
|
||||
bpy.context.scene.collection.objects.link(mdl.obj)
|
||||
mdl.obj.select_set(True)
|
||||
bpy.context.view_layer.objects.active = mdl.obj
|
||||
setup_skins(mdl, uvs)
|
||||
if len(mdl.frames) > 1 or mdl.frames[0].type:
|
||||
build_shape_keys(mdl)
|
||||
merge_frames(mdl)
|
||||
build_actions(mdl)
|
||||
write_text(mdl)
|
||||
set_properties(mdl)
|
||||
#set_properties(mdl) #TODO: bring it back
|
||||
|
||||
mdl.mesh.update()
|
||||
|
||||
bpy.context.user_preferences.edit.use_global_undo = True
|
||||
bpy.context.preferences.edit.use_global_undo = True
|
||||
return {'FINISHED'}
|
||||
|
|
Loading…
Reference in a new issue