Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormeta-androcto <meta.androcto1@gmail.com>2019-05-24 04:28:03 +0300
committermeta-androcto <meta.androcto1@gmail.com>2019-05-24 04:28:03 +0300
commit0704ebc48b4762a4f3c6b4a1252abb514d7f21db (patch)
tree9a547c234aafa2a69d42808bb52fdfa6eb6d81ca /io_export_unreal_psk_psa.py
parentd3446b1bed05b18655d769127ad65c5751f7806c (diff)
io_export/import_unreal_psk_psa: move to contrib: T63750
Diffstat (limited to 'io_export_unreal_psk_psa.py')
-rw-r--r--io_export_unreal_psk_psa.py3010
1 files changed, 0 insertions, 3010 deletions
diff --git a/io_export_unreal_psk_psa.py b/io_export_unreal_psk_psa.py
deleted file mode 100644
index 3008d6c8..00000000
--- a/io_export_unreal_psk_psa.py
+++ /dev/null
@@ -1,3010 +0,0 @@
-# ##### BEGIN GPL LICENSE BLOCK #####
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-# All rights reserved.
-#
-# ##### END GPL LICENSE BLOCK #####
-
-bl_info = {
- "name": "Export Unreal Engine Format(.psk/.psa)",
- "author": "Darknet/Optimus_P-Fat/Active_Trash/Sinsoft/VendorX/Spoof",
- "version": (2, 7, 1),
- "blender": (2, 65, 4),
- "location": "File > Export > Skeletal Mesh/Animation Data (.psk/.psa)",
- "description": "Export Skeleletal Mesh/Animation Data",
- "warning": "",
- "wiki_url": "https://wiki.blender.org/index.php/Extensions:2.6/Py/"
- "Scripts/Import-Export/Unreal_psk_psa",
- "category": "Import-Export",
-}
-
-"""
--- Unreal Skeletal Mesh and Animation Export (.psk and .psa) export script v0.0.1 --<br>
-
-- NOTES:
-- This script Exports To Unreal's PSK and PSA file formats for Skeletal Meshes and Animations. <br>
-- This script DOES NOT support vertex animation! These require completely different file formats. <br>
-
-- v0.0.1
-- Initial version
-
-- v0.0.2
-- This version adds support for more than one material index!
-
-[ - Edit by: Darknet
-- v0.0.3 - v0.0.12
-- This will work on UT3 and it is a stable version that work with vehicle for testing.
-- Main Bone fix no dummy needed to be there.
-- Just bone issues position, rotation, and offset for psk.
-- The armature bone position, rotation, and the offset of the bone is fix.
- It was to deal with skeleton mesh export for psk.
-- Animation is fix for position, offset, rotation bone support one rotation direction when armature build.
-- It will convert your mesh into triangular when exporting to psk file.
-- Did not work with psa export yet.
-
-- v0.0.13
-- The animatoin will support different bone rotations when export the animation.
-
-- v0.0.14
-- Fixed Action set keys frames when there is no pose keys and it will ignore it.
-
-- v0.0.15
-- Fixed multiple objects when exporting to psk. Select one mesh to export to psk.
-- ]
-
-- v0.1.1
-- Blender 2.50 svn (Support)
-
-Credit to:
-- export_cal3d.py (Position of the Bones Format)
-- blender2md5.py (Animation Translation Format)
-- export_obj.py (Blender 2.5/Pyhton 3.x Format)
-
-- freenode #blendercoder -> user -> ideasman42
-
-- Give Credit to those who work on this script.
-
-- http://sinsoft.com
-"""
-
-
-# ===========================================================================
-"""
-NOTES for Jan 2012 refactor (Spoof)
-
- * THIS IS A WORK IN PROGRESS. These modifications were originally
- intended for internal use and are incomplete. Use at your own risk! *
-
-TODO
-
-- (Blender 2.62) changes to Matrix math
-- (Blender 2.62) check for long names
-- option to manually set the root bone for export
-
-CHANGES
-
-- new bone parsing to allow advanced rigging
-- identification of armature and mesh
-- removed the need to apply an action to the armature
-- fixed anim rate to work correctly in UDK (no more FPS fudging)
-- progress reporting while processing smooth groups
-- more informative logging
-- code refactor for clarity and modularity
- - naming conventions unified to use lowercase_with_underscore
- - C++ datatypes and PSK/PSA classes remain CamelCaseStyle for clarity
- - names such as 'ut' and 'unreal' unified to 'udk'
- - simplification of code structure
- - removed legacy code paths
-
-USAGE
-
-This version of the exporter is more selective over which bones are considered
-part of the UDK skeletal mesh, and allows greater flexibility for adding
-control bones to aid in animation.
-
-Taking advantage of this script requires the following methodology:
-
- * Place all exportable bones into a bone hierarchy extending from a single
- root. This root bone must have use_deform enabled. All other root bones
- in the armature must disable use_deform. *
-
-The script searches for a root bone with use_deform set true and considers all
-bones parented to it as part of the UDK skeletal mesh. Thus only these bones
-are exported and all other bones are ignored.
-
-This removes many restrictions on the rigger/animator, who can add control
-bone hierarchies to the rig, and keyframe any element into actions. With this
-approach you can build complex animation rigs in a similar vein to the Rigify
-add-on, by Nathan Vegdahl. However...
-
- * Rigify is incompatible with this script *
-
-Rigify interlaces deformer bones within a single hierarchy making it difficult
-to deconstruct for export. It also splits some meta-rig bones into multiple
-deformer bones (bad for optimising a game character). I had partial success
-writing a parser for the structure, but it was taking too much time and,
-considering the other issues with Rigify, it was abandoned.
-"""
-# ===========================================================================
-
-import bmesh
-import os
-import time
-import bpy
-import mathutils
-import math
-import operator
-import sys
-from bpy.props import (
- BoolProperty,
- CollectionProperty,
- EnumProperty,
- FloatProperty,
- IntProperty,
- StringProperty,
- )
-from bpy.types import (
- Operator,
- Panel,
- UIList,
- PropertyGroup,
- AddonPreferences,
- )
-from struct import pack
-
-
-# REFERENCE MATERIAL JUST IN CASE:
-#
-# U = x / sqrt(x^2 + y^2 + z^2)
-# V = y / sqrt(x^2 + y^2 + z^2)
-#
-# Triangles specified counter clockwise for front face
-#
-# defines for sizeofs
-SIZE_FQUAT = 16
-SIZE_FVECTOR = 12
-SIZE_VJOINTPOS = 44
-SIZE_ANIMINFOBINARY = 168
-SIZE_VCHUNKHEADER = 32
-SIZE_VMATERIAL = 88
-SIZE_VBONE = 120
-SIZE_FNAMEDBONEBINARY = 120
-SIZE_VRAWBONEINFLUENCE = 12
-SIZE_VQUATANIMKEY = 32
-SIZE_VVERTEX = 16
-SIZE_VPOINT = 12
-SIZE_VTRIANGLE = 12
-
-MaterialName = []
-
-
-# Python 3 doesn't have cmp anymore
-def cmp(a, b):
- return (a > b) - (a < b)
-
-
-# ===========================================================================
-# Custom exception class
-# ===========================================================================
-class Error(Exception):
-
- def __init__(self, message):
- self.message = message
-
-
-# ===========================================================================
-# Verbose logging with loop truncation
-# ===========================================================================
-def verbose(msg, iteration=-1, max_iterations=4, msg_truncated="..."):
-
- if bpy.context.scene.udk_option_verbose is True:
- # limit the number of times a loop can output messages
- if iteration > max_iterations:
- return
- elif iteration == max_iterations:
- print(msg_truncated)
- return
-
- print(msg)
-
-
-# ===========================================================================
-# Log header/separator
-# ===========================================================================
-def header(msg, justify='LEFT', spacer='_', cols=78):
-
- if justify == 'LEFT':
- s = '{:{spacer}<{cols}}'.format(msg + " ", spacer=spacer, cols=cols)
-
- elif justify == 'RIGHT':
- s = '{:{spacer}>{cols}}'.format(" " + msg, spacer=spacer, cols=cols)
-
- else:
- s = '{:{spacer}^{cols}}'.format(" " + msg + " ", spacer=spacer, cols=cols)
-
- return "\n" + s + "\n"
-
-
-# ===========================================================================
-# Generic Object->Integer mapping
-# the object must be usable as a dictionary key
-# ===========================================================================
-class ObjMap:
-
- def __init__(self):
- self.dict = {}
- self.next = 0
-
- def get(self, obj):
- if obj in self.dict:
- return self.dict[obj]
- else:
- id = self.next
- self.next = self.next + 1
- self.dict[obj] = id
- return id
-
- def items(self):
- getval = operator.itemgetter(0)
- getkey = operator.itemgetter(1)
- return map(getval, sorted(self.dict.items(), key=getkey))
-
-
-# ===========================================================================
-# RG - UNREAL DATA STRUCTS - CONVERTED FROM C STRUCTS GIVEN ON UDN SITE
-# provided here: http://udn.epicgames.com/Two/BinaryFormatSpecifications.html
-# updated UDK (Unreal Engine 3): http://udn.epicgames.com/Three/BinaryFormatSpecifications.html
-# ===========================================================================
-class FQuat:
-
- def __init__(self):
- self.X = 0.0
- self.Y = 0.0
- self.Z = 0.0
- self.W = 1.0
-
- def dump(self):
- return pack('ffff', self.X, self.Y, self.Z, self.W)
-
- def __cmp__(self, other):
- return cmp(self.X, other.X) or \
- cmp(self.Y, other.Y) or \
- cmp(self.Z, other.Z) or \
- cmp(self.W, other.W)
-
- def __hash__(self):
- return hash(self.X) ^ hash(self.Y) ^ hash(self.Z) ^ hash(self.W)
-
- def __str__(self):
- return "[%f,%f,%f,%f](FQuat)" % (self.X, self.Y, self.Z, self.W)
-
-
-class FVector(object):
-
- def __init__(self, X=0.0, Y=0.0, Z=0.0):
- self.X = X
- self.Y = Y
- self.Z = Z
-
- def dump(self):
- return pack('fff', self.X, self.Y, self.Z)
-
- def __cmp__(self, other):
- return cmp(self.X, other.X) or \
- cmp(self.Y, other.Y) or \
- cmp(self.Z, other.Z)
-
- def _key(self):
- return (type(self).__name__, self.X, self.Y, self.Z)
-
- def __hash__(self):
- return hash(self._key())
-
- def __eq__(self, other):
- if not hasattr(other, '_key'):
- return False
- return self._key() == other._key()
-
- def dot(self, other):
- return self.X * other.X + self.Y * other.Y + self.Z * other.Z
-
- def cross(self, other):
- return FVector(self.Y * other.Z - self.Z * other.Y,
- self.Z * other.X - self.X * other.Z,
- self.X * other.Y - self.Y * other.X)
-
- def sub(self, other):
- return FVector(self.X - other.X,
- self.Y - other.Y,
- self.Z - other.Z)
-
-
-class VJointPos:
-
- def __init__(self):
- self.Orientation = FQuat()
- self.Position = FVector()
- self.Length = 0.0
- self.XSize = 0.0
- self.YSize = 0.0
- self.ZSize = 0.0
-
- def dump(self):
- return self.Orientation.dump() + self.Position.dump() + \
- pack('4f', self.Length, self.XSize, self.YSize, self.ZSize)
-
-
-class AnimInfoBinary:
-
- def __init__(self):
- self.Name = "" # length=64
- self.Group = "" # length=64
- self.TotalBones = 0
- self.RootInclude = 0
- self.KeyCompressionStyle = 0
- self.KeyQuotum = 0
- self.KeyPrediction = 0.0
- self.TrackTime = 0.0
- self.AnimRate = 0.0
- self.StartBone = 0
- self.FirstRawFrame = 0
- self.NumRawFrames = 0
-
- def dump(self):
- return pack('64s64siiiifffiii', str.encode(self.Name), str.encode(self.Group),
- self.TotalBones, self.RootInclude, self.KeyCompressionStyle, self.KeyQuotum,
- self.KeyPrediction, self.TrackTime, self.AnimRate, self.StartBone,
- self.FirstRawFrame, self.NumRawFrames)
-
-
-class VChunkHeader:
-
- def __init__(self, name, type_size):
- self.ChunkID = str.encode(name) # length=20
- self.TypeFlag = 1999801 # special value
- self.DataSize = type_size
- self.DataCount = 0
-
- def dump(self):
- return pack('20siii', self.ChunkID, self.TypeFlag, self.DataSize, self.DataCount)
-
-
-class VMaterial:
-
- def __init__(self):
- self.MaterialName = "" # length=64
- self.TextureIndex = 0
- self.PolyFlags = 0 # DWORD
- self.AuxMaterial = 0
- self.AuxFlags = 0 # DWORD
- self.LodBias = 0
- self.LodStyle = 0
-
- def dump(self):
- # print("DATA MATERIAL:",self.MaterialName)
- return pack('64siLiLii', str.encode(self.MaterialName), self.TextureIndex,
- self.PolyFlags, self.AuxMaterial, self.AuxFlags, self.LodBias, self.LodStyle)
-
-
-class VBone:
-
- def __init__(self):
- self.Name = "" # length = 64
- self.Flags = 0 # DWORD
- self.NumChildren = 0
- self.ParentIndex = 0
- self.BonePos = VJointPos()
-
- def dump(self):
- return pack('64sLii', str.encode(self.Name), self.Flags,
- self.NumChildren, self.ParentIndex) + self.BonePos.dump()
-
-
-# same as above - whatever - this is how Epic does it...
-class FNamedBoneBinary:
-
- def __init__(self):
- self.Name = "" # length = 64
- self.Flags = 0 # DWORD
- self.NumChildren = 0
- self.ParentIndex = 0
- self.BonePos = VJointPos()
- self.IsRealBone = 0 # this is set to 1 when the bone is actually a bone in the mesh and not a dummy
-
- def dump(self):
- return pack('64sLii', str.encode(self.Name), self.Flags,
- self.NumChildren, self.ParentIndex) + self.BonePos.dump()
-
-
-class VRawBoneInfluence:
-
- def __init__(self):
- self.Weight = 0.0
- self.PointIndex = 0
- self.BoneIndex = 0
-
- def dump(self):
- return pack('fii', self.Weight, self.PointIndex, self.BoneIndex)
-
-
-class VQuatAnimKey:
-
- def __init__(self):
- self.Position = FVector()
- self.Orientation = FQuat()
- self.Time = 0.0
-
- def dump(self):
- return self.Position.dump() + self.Orientation.dump() + pack('f', self.Time)
-
-
-class VVertex(object):
-
- def __init__(self):
- self.PointIndex = 0 # WORD
- self.U = 0.0
- self.V = 0.0
- self.MatIndex = 0 # BYTE
- self.Reserved = 0 # BYTE
- self.SmoothGroup = 0
-
- def dump(self):
- return pack('HHffBBH', self.PointIndex, 0, self.U, self.V, self.MatIndex, self.Reserved, 0)
-
- def __cmp__(self, other):
- return cmp(self.PointIndex, other.PointIndex) or\
- cmp(self.U, other.U) or \
- cmp(self.V, other.V) or \
- cmp(self.MatIndex, other.MatIndex) or \
- cmp(self.Reserved, other.Reserved) or \
- cmp(self.SmoothGroup, other.SmoothGroup)
-
- def _key(self):
- return (type(self).__name__, self.PointIndex, self.U, self.V, self.MatIndex, self.Reserved)
-
- def __hash__(self):
- return hash(self._key())
-
- def __eq__(self, other):
- if not hasattr(other, '_key'):
- return False
- return self._key() == other._key()
-
-
-class VPointSimple:
-
- def __init__(self):
- self.Point = FVector()
-
- def __cmp__(self, other):
- return cmp(self.Point, other.Point)
-
- def __hash__(self):
- return hash(self._key())
-
- def _key(self):
- return (type(self).__name__, self.Point)
-
- def __eq__(self, other):
- if not hasattr(other, '_key'):
- return False
- return self._key() == other._key()
-
-
-class VPoint(object):
-
- def __init__(self):
- self.Point = FVector()
- self.SmoothGroup = 0
-
- def dump(self):
- return self.Point.dump()
-
- def __cmp__(self, other):
- return cmp(self.Point, other.Point) \
- or cmp(self.SmoothGroup, other.SmoothGroup)
-
- def _key(self):
- return (type(self).__name__, self.Point, self.SmoothGroup)
-
- def __hash__(self):
- return hash(self._key()) \
- ^ hash(self.SmoothGroup)
-
- def __eq__(self, other):
- if not hasattr(other, '_key'):
- return False
- return self._key() == other._key()
-
-
-class VTriangle:
-
- def __init__(self):
- self.WedgeIndex0 = 0 # WORD
- self.WedgeIndex1 = 0 # WORD
- self.WedgeIndex2 = 0 # WORD
- self.MatIndex = 0 # BYTE
- self.AuxMatIndex = 0 # BYTE
- self.SmoothingGroups = 0 # DWORD
-
- def dump(self):
- return pack('HHHBBL', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2,
- self.MatIndex, self.AuxMatIndex, self.SmoothingGroups)
- """
- print("smooth",self.SmoothingGroups)
- return pack('HHHBBI', self.WedgeIndex0, self.WedgeIndex1, self.WedgeIndex2,
- self.MatIndex, self.AuxMatIndex, self.SmoothingGroups)
- """
-# END UNREAL DATA STRUCTS
-# ===========================================================================
-
-
-# ===========================================================================
-# RG - helper class to handle the normal way the UT files are stored
-# as sections consisting of a header and then a list of data structures
-# ===========================================================================
-class FileSection:
-
- def __init__(self, name, type_size):
- self.Header = VChunkHeader(name, type_size)
- self.Data = [] # list of datatypes
-
- def dump(self):
- data = self.Header.dump()
- for i in range(len(self.Data)):
- data = data + self.Data[i].dump()
- return data
-
- def UpdateHeader(self):
- self.Header.DataCount = len(self.Data)
-
-
-# ===========================================================================
-# PSK
-# ===========================================================================
-class PSKFile:
-
- def __init__(self):
- self.GeneralHeader = VChunkHeader("ACTRHEAD", 0)
- self.Points = FileSection("PNTS0000", SIZE_VPOINT) # VPoint
- self.Wedges = FileSection("VTXW0000", SIZE_VVERTEX) # VVertex
- self.Faces = FileSection("FACE0000", SIZE_VTRIANGLE) # VTriangle
- self.Materials = FileSection("MATT0000", SIZE_VMATERIAL) # VMaterial
- self.Bones = FileSection("REFSKELT", SIZE_VBONE) # VBone
- self.Influences = FileSection("RAWWEIGHTS", SIZE_VRAWBONEINFLUENCE) # VRawBoneInfluence
-
- # RG - this mapping is not dumped, but is used internally to store the new point indices
- # for vertex groups calculated during the mesh dump, so they can be used again
- # to dump bone influences during the armature dump
- #
- # the key in this dictionary is the VertexGroup/Bone Name, and the value
- # is a list of tuples containing the new point index and the weight, in that order
- #
- # Layout:
- # { groupname : [ (index, weight), ... ], ... }
- #
- # example:
- # {'MyVertexGroup' : [ (0, 1.0), (5, 1.0), (3, 0.5) ] , 'OtherGroup' : [(2, 1.0)]}
-
- self.VertexGroups = {}
-
- def AddPoint(self, p):
- self.Points.Data.append(p)
-
- def AddWedge(self, w):
- self.Wedges.Data.append(w)
-
- def AddFace(self, f):
- self.Faces.Data.append(f)
-
- def AddMaterial(self, m):
- self.Materials.Data.append(m)
-
- def AddBone(self, b):
- self.Bones.Data.append(b)
-
- def AddInfluence(self, i):
- self.Influences.Data.append(i)
-
- def UpdateHeaders(self):
- self.Points.UpdateHeader()
- self.Wedges.UpdateHeader()
- self.Faces.UpdateHeader()
- self.Materials.UpdateHeader()
- self.Bones.UpdateHeader()
- self.Influences.UpdateHeader()
-
- def dump(self):
- self.UpdateHeaders()
- data = self.GeneralHeader.dump() + self.Points.dump() + self.Wedges.dump() + \
- self.Faces.dump() + self.Materials.dump() + self.Bones.dump() + self.Influences.dump()
- return data
-
- def GetMatByIndex(self, mat_index):
- if mat_index >= 0 and len(self.Materials.Data) > mat_index:
- return self.Materials.Data[mat_index]
- else:
- m = VMaterial()
- # modified by VendorX
- m.MaterialName = MaterialName[mat_index]
- self.AddMaterial(m)
- return m
-
- def PrintOut(self):
- print("{:>16} {:}".format("Points", len(self.Points.Data)))
- print("{:>16} {:}".format("Wedges", len(self.Wedges.Data)))
- print("{:>16} {:}".format("Faces", len(self.Faces.Data)))
- print("{:>16} {:}".format("Materials", len(self.Materials.Data)))
- print("{:>16} {:}".format("Bones", len(self.Bones.Data)))
- print("{:>16} {:}".format("Influences", len(self.Influences.Data)))
-
-
-# ===========================================================================
-# PSA
-#
-# Notes from UDN:
-# The raw key array holds all the keys for all the bones in all the specified sequences,
-# organized as follows:
-# For each AnimInfoBinary's sequence there are [Number of bones] times [Number of frames keys]
-# in the VQuatAnimKeys, laid out as tracks of [numframes] keys for each bone in the order of
-# the bones as defined in the array of FnamedBoneBinary in the PSA.
-#
-# Once the data from the PSK (now digested into native skeletal mesh) and PSA (digested into
-# a native animation object containing one or more sequences) are associated together at runtime,
-# bones are linked up by name. Any bone in a skeleton (from the PSK) that finds no partner in
-# the animation sequence (from the PSA) will assume its reference pose stance ( as defined in
-# the offsets & rotations that are in the VBones making up the reference skeleton from the PSK)
-# ===========================================================================
-class PSAFile:
-
- def __init__(self):
- self.GeneralHeader = VChunkHeader("ANIMHEAD", 0)
- self.Bones = FileSection("BONENAMES", SIZE_FNAMEDBONEBINARY) # FNamedBoneBinary
- self.Animations = FileSection("ANIMINFO", SIZE_ANIMINFOBINARY) # AnimInfoBinary
- self.RawKeys = FileSection("ANIMKEYS", SIZE_VQUATANIMKEY) # VQuatAnimKey
- # this will take the format of key=Bone Name, value = (BoneIndex, Bone Object)
- # THIS IS NOT DUMPED
- self.BoneLookup = {}
-
- def AddBone(self, b):
- self.Bones.Data.append(b)
-
- def AddAnimation(self, a):
- self.Animations.Data.append(a)
-
- def AddRawKey(self, k):
- self.RawKeys.Data.append(k)
-
- def UpdateHeaders(self):
- self.Bones.UpdateHeader()
- self.Animations.UpdateHeader()
- self.RawKeys.UpdateHeader()
-
- def GetBoneByIndex(self, bone_index):
- if bone_index >= 0 and len(self.Bones.Data) > bone_index:
- return self.Bones.Data[bone_index]
-
- def IsEmpty(self):
- return (len(self.Bones.Data) == 0 or len(self.Animations.Data) == 0)
-
- def StoreBone(self, b):
- self.BoneLookup[b.Name] = [-1, b]
-
- def UseBone(self, bone_name):
- if bone_name in self.BoneLookup:
- bone_data = self.BoneLookup[bone_name]
-
- if bone_data[0] == -1:
- bone_data[0] = len(self.Bones.Data)
- self.AddBone(bone_data[1])
- # self.Bones.Data.append(bone_data[1])
-
- return bone_data[0]
-
- def GetBoneByName(self, bone_name):
- if bone_name in self.BoneLookup:
- bone_data = self.BoneLookup[bone_name]
- return bone_data[1]
-
- def GetBoneIndex(self, bone_name):
- if bone_name in self.BoneLookup:
- bone_data = self.BoneLookup[bone_name]
- return bone_data[0]
-
- def dump(self):
- self.UpdateHeaders()
- return self.GeneralHeader.dump() + self.Bones.dump() + self.Animations.dump() + self.RawKeys.dump()
-
- def PrintOut(self):
- print("{:>16} {:}".format("Bones", len(self.Bones.Data)))
- print("{:>16} {:}".format("Animations", len(self.Animations.Data)))
- print("{:>16} {:}".format("Raw keys", len(self.RawKeys.Data)))
-
-
-# ===========================================================================
-# Helpers to create bone structs
-# ===========================================================================
-def make_vbone(name, parent_index, child_count, orientation_quat, position_vect):
- bone = VBone()
- bone.Name = name
- bone.ParentIndex = parent_index
- bone.NumChildren = child_count
- bone.BonePos.Orientation = orientation_quat
- bone.BonePos.Position.X = position_vect.x
- bone.BonePos.Position.Y = position_vect.y
- bone.BonePos.Position.Z = position_vect.z
- # these values seem to be ignored?
- # bone.BonePos.Length = tail.length
- # bone.BonePos.XSize = tail.x
- # bone.BonePos.YSize = tail.y
- # bone.BonePos.ZSize = tail.z
- return bone
-
-
-def make_namedbonebinary(name, parent_index, child_count, orientation_quat, position_vect, is_real):
- bone = FNamedBoneBinary()
- bone.Name = name
- bone.ParentIndex = parent_index
- bone.NumChildren = child_count
- bone.BonePos.Orientation = orientation_quat
- bone.BonePos.Position.X = position_vect.x
- bone.BonePos.Position.Y = position_vect.y
- bone.BonePos.Position.Z = position_vect.z
- bone.IsRealBone = is_real
- return bone
-
-
-def make_fquat(bquat):
- quat = FQuat()
- # flip handedness for UT = set x,y,z to negative (rotate in other direction)
- quat.X = -bquat.x
- quat.Y = -bquat.y
- quat.Z = -bquat.z
- quat.W = bquat.w
-
- return quat
-
-
-def make_fquat_default(bquat):
- quat = FQuat()
- # print(dir(bquat))
- quat.X = bquat.x
- quat.Y = bquat.y
- quat.Z = bquat.z
- quat.W = bquat.w
-
- return quat
-
-
-# ===========================================================================
-# RG - check to make sure face isnt a line
-# ===========================================================================
-def is_1d_face(face, mesh):
- # ID Vertex of id point
- v0 = face.vertices[0]
- v1 = face.vertices[1]
- v2 = face.vertices[2]
-
- return (mesh.vertices[v0].co == mesh.vertices[v1].co or
- mesh.vertices[v1].co == mesh.vertices[v2].co or
- mesh.vertices[v2].co == mesh.vertices[v0].co)
- return False
-
-
-# ===========================================================================
-# Smoothing group
-# (renamed to separate it from VVertex.SmoothGroup)
-# ===========================================================================
-class SmoothingGroup:
-
- static_id = 1
-
- def __init__(self):
- self.faces = []
- self.neighboring_faces = []
- self.neighboring_groups = []
- self.id = -1
- self.local_id = SmoothingGroup.static_id
- SmoothingGroup.static_id += 1
-
- def __cmp__(self, other):
- if isinstance(other, SmoothingGroup):
- return cmp(self.local_id, other.local_id)
- return -1
-
- def __hash__(self):
- return hash(self.local_id)
-
- # searches neighboring faces to determine which smoothing group ID can be used
- def get_valid_smoothgroup_id(self):
- temp_id = 1
- for group in self.neighboring_groups:
- if group is not None and group.id == temp_id:
- if temp_id < 0x80000000:
- temp_id = temp_id << 1
- else:
- raise Error("Smoothing Group ID Overflowed, "
- "Smoothing Group evidently has more than 31 neighboring groups")
-
- self.id = temp_id
- return self.id
-
- def make_neighbor(self, new_neighbor):
- if new_neighbor not in self.neighboring_groups:
- self.neighboring_groups.append(new_neighbor)
-
- def contains_face(self, face):
- return (face in self.faces)
-
- def add_neighbor_face(self, face):
- if face not in self.neighboring_faces:
- self.neighboring_faces.append(face)
-
- def add_face(self, face):
- if face not in self.faces:
- self.faces.append(face)
-
-
-def determine_edge_sharing(mesh):
-
- edge_sharing_list = dict()
-
- for edge in mesh.edges:
- edge_sharing_list[edge.key] = []
-
- for face in mesh.tessfaces:
- for key in face.edge_keys:
- if face not in edge_sharing_list[key]:
- edge_sharing_list[key].append(face) # mark this face as sharing this edge
-
- return edge_sharing_list
-
-
-def find_edges(mesh, key):
- """ Temp replacement for mesh.findEdges().
- This is painfully slow.
- """
- for edge in mesh.edges:
- v = edge.vertices
- if key[0] == v[0] and key[1] == v[1]:
- return edge.index
-
-
-def add_face_to_smoothgroup(mesh, face, edge_sharing_list, smoothgroup):
-
- if face in smoothgroup.faces:
- return
-
- smoothgroup.add_face(face)
-
- for key in face.edge_keys:
-
- edge_id = find_edges(mesh, key)
-
- if edge_id is not None:
-
- # not sharp
- if not (mesh.edges[edge_id].use_edge_sharp):
-
- for shared_face in edge_sharing_list[key]:
- if shared_face != face:
- # recursive
- add_face_to_smoothgroup(mesh, shared_face, edge_sharing_list, smoothgroup)
- # sharp
- else:
- for shared_face in edge_sharing_list[key]:
- if shared_face != face:
- smoothgroup.add_neighbor_face(shared_face)
-
-
-def determine_smoothgroup_for_face(mesh, face, edge_sharing_list, smoothgroup_list):
-
- for group in smoothgroup_list:
- if (face in group.faces):
- return
-
- smoothgroup = SmoothingGroup()
- add_face_to_smoothgroup(mesh, face, edge_sharing_list, smoothgroup)
-
- if smoothgroup not in smoothgroup_list:
- smoothgroup_list.append(smoothgroup)
-
-
-def build_neighbors_tree(smoothgroup_list):
-
- for group in smoothgroup_list:
- for face in group.neighboring_faces:
- for neighbor_group in smoothgroup_list:
- if neighbor_group.contains_face(face) and neighbor_group not in group.neighboring_groups:
- group.make_neighbor(neighbor_group)
- neighbor_group.make_neighbor(group)
-
-
-# ===========================================================================
-# parse_smooth_groups
-# ===========================================================================
-def parse_smooth_groups(mesh):
-
- print("Parsing smooth groups...")
-
- t = time.clock()
- smoothgroup_list = []
- edge_sharing_list = determine_edge_sharing(mesh)
-
- # print("faces:",len(mesh.tessfaces))
- interval = math.floor(len(mesh.tessfaces) / 100)
-
- if interval == 0: # if the faces are few do this
- interval = math.floor(len(mesh.tessfaces) / 10)
- # print("FACES:",len(mesh.tessfaces),"//100 =" "interval:",interval)
-
- for face in mesh.tessfaces:
- # print(dir(face))
- determine_smoothgroup_for_face(mesh, face, edge_sharing_list, smoothgroup_list)
- # progress indicator, writes to console without scrolling
- if face.index > 0 and (face.index % interval) == 0:
- print("Processing... {}%\r".format(int(face.index / len(mesh.tessfaces) * 100)), end='')
- sys.stdout.flush()
- print("Completed", ' ' * 20)
-
- verbose("len(smoothgroup_list)={}".format(len(smoothgroup_list)))
-
- build_neighbors_tree(smoothgroup_list)
-
- for group in smoothgroup_list:
- group.get_valid_smoothgroup_id()
-
- print("Smooth group parsing completed in {:.2f}s".format(time.clock() - t))
- return smoothgroup_list
-
-
-# ===========================================================================
-# http://en.wikibooks.org/wiki/Blender_3D:_Blending_Into_Python/Cookbook#Triangulate_NMesh
-# blender 2.50 format using the Operators/command convert the mesh to tri mesh
-# ===========================================================================
-def triangulate_mesh(object):
-
- verbose(header("triangulateNMesh"))
- # print(type(object))
- scene = bpy.context.scene
- view_layer = bpy.context.view_layer
-
- me_ob = object.copy()
- depsgraph = bpy.context.evaluated_depsgraph_get()
- me_ob.data = bpy.data.meshes.new_from_object(object.evaluated_get(depsgraph)) # write data object
- bpy.context.collection.objects.link(me_ob)
- view_layer.update()
- bpy.ops.object.mode_set(mode='OBJECT')
-
- for i in scene.objects:
- i.select_set(False) # deselect all objects
-
- me_ob.select_set(True)
- view_layer.objects.active = me_ob
-
- print("Copy and Convert mesh just incase any way...")
-
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.select_all(action='SELECT') # select all the face/vertex/edge
- bpy.ops.object.mode_set(mode='EDIT')
- bpy.ops.mesh.quads_convert_to_tris()
- view_layer.update()
-
- bpy.ops.object.mode_set(mode='OBJECT')
-
- bpy.context.scene.udk_option_triangulate = True
-
- verbose("Triangulated mesh")
-
- me_ob.data = bpy.data.meshes.new_from_object(me_ob.evaluated_get(depsgraph)) # write data object
- view_layer.update()
- return me_ob
-
-
-# copy mesh data and then merge them into one object
-def meshmerge(selectedobjects):
- bpy.ops.object.mode_set(mode='OBJECT') # object mode and not edit mode
- cloneobjects = [] # object holder for copying object data
-
- if len(selectedobjects) > 1:
- print("selectedobjects:", len(selectedobjects)) # print select object
- count = 0 # reset count
-
- for count in range(len(selectedobjects)):
- # print("Index:",count)
- if selectedobjects[count] is not None:
- me_da = selectedobjects[count].data.copy() # copy data
- me_ob = selectedobjects[count].copy() # copy object
- # note two copy two types else it will use the current data or mesh
- me_ob.data = me_da # assign the data
- bpy.context.collection.objects.link(me_ob) # link the object to the collection (current obj location)
- print("Index:", count, "clone object", me_ob.name) # print clone object
- cloneobjects.append(me_ob) # add object to the array
-
- for i in bpy.data.objects:
- i.select_set(False) # deselect all objects
- count = 0 # reset count
- # begin merging the mesh together as one
- for count in range(len(cloneobjects)):
- if count == 0:
- bpy.context.view_layer.objects.active = cloneobjects[count]
- print("Set Active Object:", cloneobjects[count].name)
- cloneobjects[count].select_set(True)
- bpy.ops.object.join() # join object together
- if len(cloneobjects) > 1:
- bpy.types.Scene.udk_copy_merge = True
- return cloneobjects[0]
-
-
-# sort the mesh center top list and not center at the last array.
-# Base on order while select to merge mesh to make them center.
-def sortmesh(selectmesh):
- print("MESH SORTING...")
- centermesh = []
- notcentermesh = []
- for countm in range(len(selectmesh)):
- # if object are center add here
- if selectmesh[countm].location.x == 0 and \
- selectmesh[countm].location.y == 0 and \
- selectmesh[countm].location.z == 0:
- centermesh.append(selectmesh[countm])
- else: # if not add here for not center
- notcentermesh.append(selectmesh[countm])
- selectmesh = []
- # add mesh object in order for merge object
- for countm in range(len(centermesh)):
- selectmesh.append(centermesh[countm])
- for countm in range(len(notcentermesh)):
- selectmesh.append(notcentermesh[countm])
- if len(selectmesh) == 1: # if there one mesh just do some here
- return selectmesh[0] # return object mesh
- else:
- return meshmerge(selectmesh) # return merge object mesh
-
-
-import binascii
-
-
-# ===========================================================================
-# parse_mesh
-# ===========================================================================
-def parse_mesh(mesh, psk):
- # bpy.ops.object.mode_set(mode='OBJECT')
- # error ? on commands for select object?
- print(header("MESH", 'RIGHT'))
- print("Mesh object:", mesh.name)
- scene = bpy.context.scene
- view_layer = bpy.context.view_layer
-
- for i in scene.objects:
- i.select_set(False) # deselect all objects
-
- view_layer.objects.active = mesh
- setmesh = mesh
- mesh = triangulate_mesh(mesh)
-
- if bpy.types.Scene.udk_copy_merge is True:
- bpy.context.collection.objects.unlink(setmesh)
-
- # print("FACES----:",len(mesh.data.tessfaces))
- verbose("Working mesh object: {}".format(mesh.name))
-
- # collect a list of the material names
- print("Materials...")
-
- mat_slot_index = 0
-
- for slot in mesh.material_slots:
-
- print(" Material {} '{}'".format(mat_slot_index, slot.name))
- MaterialName.append(slot.name)
-
- """
- if slot.material.texture_slots[0] is not None:
- if slot.material.texture_slots[0].texture.image.filepath is not None:
- print(" Texture path {}".format(slot.material.texture_slots[0].texture.image.filepath))
- """
-
- # create the current material
- v_material = psk.GetMatByIndex(mat_slot_index)
- v_material.MaterialName = slot.name
- v_material.TextureIndex = mat_slot_index
- v_material.AuxMaterial = mat_slot_index
- mat_slot_index += 1
- verbose(" PSK index {}".format(v_material.TextureIndex))
-
- # END slot in mesh.material_slots
-
- # object_mat = mesh.materials[0]
- # object_material_index = mesh.active_material_index
- # FIXME ^ this is redundant due to "= face.material_index" in face loop
-
- wedges = ObjMap()
- points = ObjMap() # vertex
- points_linked = {}
-
- discarded_face_count = 0
- sys.setrecursionlimit(1000000)
- smoothgroup_list = parse_smooth_groups(mesh.data)
-
- print("{} faces".format(len(mesh.data.tessfaces)))
-
- print("Smooth groups active:", bpy.context.scene.udk_option_smoothing_groups)
-
- for face in mesh.data.tessfaces:
-
- smoothgroup_id = 0x80000000
-
- for smooth_group in smoothgroup_list:
- if smooth_group.contains_face(face):
- smoothgroup_id = smooth_group.id
- break
-
- # modified by VendorX
- object_material_index = face.material_index
-
- if len(face.vertices) != 3:
- raise Error("Non-triangular face (%i)" % len(face.vertices))
-
- # RG - apparently blender sometimes has problems when you do quad to triangle
- # conversion, and ends up creating faces that have only TWO points -
- # one of the points is simply in the vertex list for the face twice.
- # This is bad, since we can't get a real face normal for a LINE, we need
- # a plane for this. So, before we add the face to the list of real faces,
- # ensure that the face is actually a plane, and not a line. If it is not
- # planar, just discard it and notify the user in the console after we're
- # done dumping the rest of the faces
-
- if not is_1d_face(face, mesh.data):
-
- wedge_list = []
- vect_list = []
-
- # get or create the current material
- psk.GetMatByIndex(object_material_index)
-
- face_index = face.index
- has_uv = False
- face_uv = None
-
- if len(mesh.data.uv_textures) > 0:
- has_uv = True
- uv_layer = mesh.data.tessface_uv_textures.active
- face_uv = uv_layer.data[face_index]
- # size(data) is number of texture faces. Each face has UVs
- # print("DATA face uv: ",len(faceUV.uv), " >> ",(faceUV.uv[0][0]))
-
- for i in range(3):
- vert_index = face.vertices[i]
- vert = mesh.data.vertices[vert_index]
- uv = []
- # assumes 3 UVs Per face (for now)
- if (has_uv):
- if len(face_uv.uv) != 3:
- print("WARNING: face has more or less than 3 UV coordinates - writing 0,0...")
- uv = [0.0, 0.0]
- else:
- uv = [face_uv.uv[i][0], face_uv.uv[i][1]] # OR bottom works better # 24 for cube
- else:
- # print ("No UVs?")
- uv = [0.0, 0.0]
-
- # flip V coordinate because UEd requires it and DOESN'T flip it on its own like it
- # does with the mesh Y coordinates. this is otherwise known as MAGIC-2
- uv[1] = 1.0 - uv[1]
-
- # clamp UV coords if udk_option_clight_uv is True
- if bpy.context.scene.udk_option_clight_uv:
- if (uv[0] > 1):
- uv[0] = 1
- if (uv[0] < 0):
- uv[0] = 0
- if (uv[1] > 1):
- uv[1] = 1
- if (uv[1] < 0):
- uv[1] = 0
-
- # RE - Append untransformed vector (for normal calc below)
- # TODO: convert to Blender.Mathutils
- vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z))
-
- # Transform position for export
- # vpos = vert.co * object_material_index
-
- # should fixed this!!
- vpos = mesh.matrix_local * vert.co
- if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
- # print("OK!")
- vpos.x = vpos.x * bpy.context.scene.udk_option_scale
- vpos.y = vpos.y * bpy.context.scene.udk_option_scale
- vpos.z = vpos.z * bpy.context.scene.udk_option_scale
-
- # print("scale pos:", vpos)
- # Create the point
- p = VPoint()
- p.Point.X = vpos.x
- p.Point.Y = vpos.y
- p.Point.Z = vpos.z
- if bpy.context.scene.udk_option_smoothing_groups: # is this necessary?
- p.SmoothGroup = smoothgroup_id
-
- lPoint = VPointSimple()
- lPoint.Point.X = vpos.x
- lPoint.Point.Y = vpos.y
- lPoint.Point.Z = vpos.z
-
- if lPoint in points_linked:
- if not(p in points_linked[lPoint]):
- points_linked[lPoint].append(p)
- else:
- points_linked[lPoint] = [p]
-
- # Create the wedge
- w = VVertex()
- w.MatIndex = object_material_index
- w.PointIndex = points.get(p) # store keys
- w.U = uv[0]
- w.V = uv[1]
- if bpy.context.scene.udk_option_smoothing_groups: # is this necessary?
- w.SmoothGroup = smoothgroup_id
- index_wedge = wedges.get(w)
- wedge_list.append(index_wedge)
-
- # print results
- # print("result PointIndex={}, U={:.6f}, V={:.6f}, wedge_index={}".format(
- # w.PointIndex,
- # w.U,
- # w.V,
- # index_wedge))
-
- # END for i in range(3)
-
- # Determine face vertex order
-
- # TODO: convert to Blender.Mathutils
- # get normal from blender
- no = face.normal
- # convert to FVector
- norm = FVector(no[0], no[1], no[2])
- # Calculate the normal of the face in blender order
- tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
- # RE - dot the normal from blender order against the blender normal
- # this gives the product of the two vectors' lengths along the blender normal axis
- # all that matters is the sign
- dot = norm.dot(tnorm)
-
- tri = VTriangle()
- # RE - magic: if the dot product above > 0, order the vertices 2, 1, 0
- # if the dot product above < 0, order the vertices 0, 1, 2
- # if the dot product is 0, then blender's normal is coplanar with the face
- # and we cannot deduce which side of the face is the outside of the mesh
- if dot > 0:
- (tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
- elif dot < 0:
- (tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list
- else:
- dindex0 = face.vertices[0]
- dindex1 = face.vertices[1]
- dindex2 = face.vertices[2]
-
- mesh.data.vertices[dindex0].select = True
- mesh.data.vertices[dindex1].select = True
- mesh.data.vertices[dindex2].select = True
-
- raise Error("Normal coplanar with face! points: %s, %s, %s" % (str(mesh.data.vertices[dindex0].co),
- str(mesh.data.vertices[dindex1].co),
- str(mesh.data.vertices[dindex2].co)))
-
- face.select = True
- if face.use_smooth is True:
- tri.SmoothingGroups = 1
- else:
- tri.SmoothingGroups = 0
- tri.MatIndex = object_material_index
-
- if bpy.context.scene.udk_option_smoothing_groups:
- tri.SmoothingGroups = smoothgroup_id
- print("Bool Smooth")
-
- psk.AddFace(tri)
-
- # END if not is_1d_face(current_face, mesh.data)
-
- else:
- discarded_face_count += 1
-
- # END face in mesh.data.faces
-
- print("{} points".format(len(points.dict)))
-
- for point in points.items():
- psk.AddPoint(point)
-
- if len(points.dict) > 32767:
- raise Error("Mesh vertex limit exceeded! {} > 32767".format(len(points.dict)))
-
- print("{} wedges".format(len(wedges.dict)))
-
- for wedge in wedges.items():
- psk.AddWedge(wedge)
-
- # alert the user to degenerate face issues
- if discarded_face_count > 0:
- print("WARNING: Mesh contained degenerate faces (non-planar)")
- print(" Discarded {} faces".format(discarded_face_count))
-
- # RG - walk through the vertex groups and find the indexes into the PSK points array
- # for them, then store that index and the weight as a tuple in a new list of
- # verts for the group that we can look up later by bone name, since Blender matches
- # verts to bones for influences by having the VertexGroup named the same thing as
- # the bone
-
- # [print(x, len(points_linked[x])) for x in points_linked]
- # print("pointsindex length ",len(points_linked))
- # vertex group
-
- # all vertex groups of the mesh (obj)...
- for obj_vertex_group in mesh.vertex_groups:
-
- # print(" bone group build:",obj_vertex_group.name)#print bone name
- # print(dir(obj_vertex_group))
- verbose("obj_vertex_group.name={}".format(obj_vertex_group.name))
-
- vertex_list = []
-
- # all vertices in the mesh...
- for vertex in mesh.data.vertices:
- # print(dir(vertex))
- # all groups this vertex is a member of...
- for vgroup in vertex.groups:
- if vgroup.group == obj_vertex_group.index:
- vertex_weight = vgroup.weight
- p = VPointSimple()
- vpos = mesh.matrix_local * vertex.co
-
- if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
- vpos.x = vpos.x * bpy.context.scene.udk_option_scale
- vpos.y = vpos.y * bpy.context.scene.udk_option_scale
- vpos.z = vpos.z * bpy.context.scene.udk_option_scale
- p.Point.X = vpos.x
- p.Point.Y = vpos.y
- p.Point.Z = vpos.z
- # print(p)
- # print(len(points_linked[p]))
- try: # check if point doesn't give error
- for point in points_linked[p]:
- point_index = points.get(point) # point index
- v_item = (point_index, vertex_weight)
- vertex_list.append(v_item)
- except Exception: # if get error ignore them # not safe I think
- print("Error link points!")
- pass
-
- # bone name, [point id and wieght]
- # print("Add Vertex Group:",obj_vertex_group.name, " No. Points:",len(vertex_list))
- psk.VertexGroups[obj_vertex_group.name] = vertex_list
-
- # remove the temporary triangulated mesh
- if bpy.context.scene.udk_option_triangulate is True:
- verbose("Removing temporary triangle mesh: {}".format(mesh.name))
- bpy.ops.object.mode_set(mode='OBJECT') # OBJECT mode
- mesh.parent = None # unparent to avoid phantom links
- bpy.context.collection.objects.unlink(mesh) # unlink
-
-
-# ===========================================================================
-# Collate bones that belong to the UDK skeletal mesh
-# ===========================================================================
-def parse_armature(armature, psk, psa):
-
- print(header("ARMATURE", 'RIGHT'))
- verbose("Armature object: {} Armature data: {}".format(armature.name, armature.data.name))
-
- # generate a list of root bone candidates
- root_candidates = [b for b in armature.data.bones if b.parent is None and b.use_deform is True]
-
- # should be a single, unambiguous result
- if len(root_candidates) == 0:
- raise Error("Cannot find root for UDK bones. The root bone must use deform.")
-
- if len(root_candidates) > 1:
- raise Error("Ambiguous root for UDK. More than one root bone is using deform.")
-
- # prep for bone collection
- udk_root_bone = root_candidates[0]
- udk_bones = []
- BoneUtil.static_bone_id = 0 # replaces global
-
- # traverse bone chain
- print("{: <3} {: <48} {: <20}".format("ID", "Bone", "Status"))
- print()
- recurse_bone(udk_root_bone, udk_bones, psk, psa, 0, armature.matrix_local)
-
- # final validation
- if len(udk_bones) < 3:
- raise Error("Less than three bones may crash UDK (legacy issue?)")
-
- # return a list of bones making up the entire udk skel
- # this is passed to parse_animation instead of working from keyed bones in the action
- return udk_bones
-
-
-# ===========================================================================
-# bone current bone
-# bones bone list
-# psk the PSK file object
-# psa the PSA file object
-# parent_id
-# parent_matrix
-# indent text indent for recursive log
-# ===========================================================================
-def recurse_bone(bone, bones, psk, psa, parent_id, parent_matrix, indent=""):
-
- status = "Ok"
- bones.append(bone)
-
- if not bone.use_deform:
- status = "No effect"
-
- # calc parented bone transform
- if bone.parent is not None:
- quat = make_fquat(bone.matrix.to_quaternion())
- quat_parent = bone.parent.matrix.to_quaternion().inverted()
- parent_head = quat_parent * bone.parent.head
- parent_tail = quat_parent * bone.parent.tail
- translation = (parent_tail - parent_head) + bone.head
-
- # calc root bone transform
- else:
- translation = parent_matrix * bone.head # ARMATURE OBJECT Location
- rot_matrix = bone.matrix * parent_matrix.to_3x3() # ARMATURE OBJECT Rotation
- quat = make_fquat_default(rot_matrix.to_quaternion())
-
- # udk_option_scale bones here?
- if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
- translation.x = translation.x * bpy.context.scene.udk_option_scale
- translation.y = translation.y * bpy.context.scene.udk_option_scale
- translation.z = translation.z * bpy.context.scene.udk_option_scale
- bone_id = BoneUtil.static_bone_id # ALT VERS
- BoneUtil.static_bone_id += 1 # ALT VERS
-
- child_count = len(bone.children)
-
- psk.AddBone(make_vbone(bone.name, parent_id, child_count, quat, translation))
- psa.StoreBone(make_namedbonebinary(bone.name, parent_id, child_count, quat, translation, 1))
-
- # RG - dump influences for this bone - use the data we collected
- # in the mesh dump phase to map our bones to vertex groups
- if bone.name in psk.VertexGroups:
- vertex_list = psk.VertexGroups[bone.name]
- # print("vertex list:", len(vertex_list), " of >" ,bone.name)
-
- for vertex_data in vertex_list:
- point_index = vertex_data[0]
- vertex_weight = vertex_data[1]
- influence = VRawBoneInfluence()
- influence.Weight = vertex_weight
- influence.BoneIndex = bone_id
- influence.PointIndex = point_index
- # print (" AddInfluence to vertex {}, weight={},".format(point_index, vertex_weight))
- psk.AddInfluence(influence)
- else:
- status = "No vertex group"
- # FIXME overwriting previous status error?
-
- print("{:<3} {:<48} {:<20}".format(bone_id, indent + bone.name, status))
-
- # bone.matrix_local
- # recursively dump child bones
-
- for child_bone in bone.children:
- recurse_bone(child_bone, bones, psk, psa, bone_id, parent_matrix, " " + indent)
-
-
-# FIXME rename? remove?
-class BoneUtil:
- static_bone_id = 0 # static property to replace global
-
-
-# ===========================================================================
-# armature the armature
-# udk_bones list of bones to be exported
-# actions_to_export list of actions to process for export
-# psa the PSA file object
-# ===========================================================================
-def parse_animation(armature, udk_bones, actions_to_export, psa):
-
- print(header("ANIMATION", 'RIGHT'))
-
- context = bpy.context
- anim_rate = context.scene.render.fps
-
- verbose("Armature object: {}".format(armature.name))
- print("Scene: {} FPS: {} Frames: {} to {}".format(context.scene.name, anim_rate,
- context.scene.frame_start, context.scene.frame_end)
- )
- print("Processing {} action(s)\n".format(len(actions_to_export)))
-
- # if animation data was not create for the armature it will skip the exporting action set(s)
- if armature.animation_data is None:
- print("None Actions Set! skipping...")
- return
- restoreAction = armature.animation_data.action # Q: is animation_data always valid?
- # we already do this in export_proxy, but we'll do it here too for now
- restoreFrame = context.scene.frame_current
- raw_frame_index = 0 # used to set FirstRawFrame, separating actions in the raw keyframe array
-
- # action loop...
- for action in actions_to_export:
-
- # removed: check for armature with no animation; all it did was force you to add one
-
- if not len(action.fcurves):
- print("{} has no keys, skipping".format(action.name))
- continue
-
- # apply action to armature and update scene
- # note if loop all actions that is not armature it will override and will break armature animation
- armature.animation_data.action = action
- context.view_layer.update()
-
- # min/max frames define range
- framemin, framemax = action.frame_range
- start_frame = int(framemin)
- end_frame = int(framemax)
- scene_range = range(start_frame, end_frame + 1)
- frame_count = len(scene_range)
-
- # create the AnimInfoBinary
- anim = AnimInfoBinary()
- anim.Name = action.name
- anim.Group = "" # unused?
- anim.NumRawFrames = frame_count
- anim.AnimRate = anim_rate
- anim.FirstRawFrame = raw_frame_index
-
- print("{}, frames {} to {} ({} frames)".format(action.name, start_frame, end_frame, frame_count))
-
- # removed: bone lookup table
-
- # build a list of pose bones relevant to the collated udk_bones
- # fixme: could be done once, prior to loop?
- udk_pose_bones = []
- for b in udk_bones:
- for pb in armature.pose.bones:
- if b.name == pb.name:
- udk_pose_bones.append(pb)
- break
-
- # sort in the order the bones appear in the PSA file
- ordered_bones = {}
- ordered_bones = sorted([(psa.UseBone(b.name), b) for b in udk_pose_bones], key=operator.itemgetter(0))
-
- # NOTE: posebone.bone references the obj/edit bone
- # REMOVED: unique_bone_indexes is redundant?
-
- # frame loop...
- for i in range(frame_count):
-
- frame = scene_range[i]
-
- # verbose("FRAME {}".format(i), i) # test loop sampling
-
- # advance to frame (automatically updates the pose)
- context.scene.frame_set(frame)
-
- # compute the key for each bone
- for bone_data in ordered_bones:
-
- bone_index = bone_data[0]
- pose_bone = bone_data[1]
- pose_bone_matrix = mathutils.Matrix(pose_bone.matrix)
-
- if pose_bone.parent is not None:
- pose_bone_parent_matrix = mathutils.Matrix(pose_bone.parent.matrix)
- pose_bone_matrix = pose_bone_parent_matrix.inverted() * pose_bone_matrix
-
- head = pose_bone_matrix.to_translation()
- quat = pose_bone_matrix.to_quaternion().normalized()
-
- if pose_bone.parent is not None:
- quat = make_fquat(quat)
- else:
- quat = make_fquat_default(quat)
-
- # scale animation position here?
- if bpy.context.scene.udk_option_scale < 0 or bpy.context.scene.udk_option_scale > 1:
- head.x = head.x * bpy.context.scene.udk_option_scale
- head.y = head.y * bpy.context.scene.udk_option_scale
- head.z = head.z * bpy.context.scene.udk_option_scale
-
- vkey = VQuatAnimKey()
- vkey.Position.X = head.x
- vkey.Position.Y = head.y
- vkey.Position.Z = head.z
- vkey.Orientation = quat
-
- # frame delta = 1.0 / fps
- vkey.Time = 1.0 / anim_rate # according to C++ header this is "disregarded"
-
- psa.AddRawKey(vkey)
-
- # END for bone_data in ordered_bones
-
- raw_frame_index += 1
-
- # END for i in range(frame_count)
-
- # REMOVED len(unique_bone_indexes)
- anim.TotalBones = len(ordered_bones)
- # frame_count/anim.AnimRate makes more sense, but this is what actually works in UDK
- anim.TrackTime = float(frame_count)
-
- verbose("anim.TotalBones={}, anim.TrackTime={}".format(anim.TotalBones, anim.TrackTime))
-
- psa.AddAnimation(anim)
-
- # END for action in actions
-
- # restore
- armature.animation_data.action = restoreAction
- context.scene.frame_set(restoreFrame)
-
-
-# ===========================================================================
-# Collate actions to be exported
-# Modify this to filter for one, some or all actions. For now use all.
-# RETURNS list of actions
-# ===========================================================================
-def collate_actions():
- verbose(header("collate_actions"))
- actions_to_export = []
-
- for action in bpy.data.actions:
- if bpy.context.scene.udk_option_selectanimations: # check if needed to select actions set for exporting it
- print("Action Set is selected!")
- bready = False
- for actionlist in bpy.context.scene.udkas_list: # list the action set from the list
- if actionlist.name == action.name and actionlist.bmatch is True and actionlist.bexport is True:
- bready = True
- print("Added Action Set:", action.name)
- break
- if bready is False: # don't export it
- print("Skipping Action Set:", action.name)
- continue
- verbose(" + {}".format(action.name)) # action set name
- actions_to_export.append(action) # add to the action array
-
- return actions_to_export
-
-
-# ===========================================================================
-# Locate the target armature and mesh for export
-# RETURNS armature, mesh
-# ===========================================================================
-def find_armature_and_mesh():
- verbose(header("find_armature_and_mesh", 'LEFT', '<', 60))
-
- context = bpy.context
- active_object = context.active_object
- armature = None
- mesh = None
-
- # TODO:
- # this could be more intuitive
- # bpy.ops.object.mode_set(mode='OBJECT')
-
- if bpy.context.scene.udk_option_selectobjects: # if checked select object true do list object on export
- print("select mode:")
- if len(bpy.context.scene.udkArm_list) > 0:
- print("Armature Name:", bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name)
- for obj in bpy.context.scene.objects:
- if obj.name == bpy.context.scene.udkArm_list[bpy.context.scene.udkArm_list_idx].name:
- armature = obj
- break
- else:
- raise Error("There is no Armature in the list!")
- meshselected = []
- # parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
- meshes = [obj for obj in bpy.context.scene.objects if obj.type == 'MESH']
- for obj in meshes:
- # print(dir(obj))
- if obj.type == 'MESH':
- bexportmesh = False
- # print("PARENT MESH:",obj.name)
- for udkmeshlist in bpy.context.scene.udkmesh_list:
- if obj.name == udkmeshlist.name and udkmeshlist.bexport is True:
- bexportmesh = True
- break
- if bexportmesh is True:
- print("Mesh Name:", obj.name, " < SELECT TO EXPORT!")
- meshselected.append(obj)
-
- print("MESH COUNT:", len(meshselected))
- # try the active object
- if active_object and active_object.type == 'MESH' and len(meshselected) == 0:
- if active_object.parent == armature:
- mesh = active_object
- else:
- raise Error("The selected mesh is not parented to the armature")
-
- # otherwise, expect a single mesh parented to the armature (other object types are ignored)
- else:
- print("Number of meshes:", len(meshes))
- print("Number of meshes (selected):", len(meshes))
- if len(meshes) == 1:
- mesh = meshes[0]
-
- elif len(meshes) > 1:
- if len(meshselected) >= 1:
- mesh = sortmesh(meshselected)
- else:
- raise Error("More than one mesh(s) parented to armature. Select object(s)!")
- else:
- raise Error("No mesh parented to armature")
- else: # if not check for select function from the list work the code here
- print("normal mode:")
- # try the active object
- if active_object and active_object.type == 'ARMATURE':
- armature = active_object
- bpy.ops.object.mode_set(mode='OBJECT')
- # otherwise, try for a single armature in the scene
- else:
- # bpy.ops.object.mode_set(mode='OBJECT')
- all_armatures = [obj for obj in bpy.context.scene.objects if obj.type == 'ARMATURE']
-
- if len(all_armatures) == 1: # if armature has one scene just assign it
- armature = all_armatures[0]
- elif len(all_armatures) > 1: # if there more armature then find the select armature
- barmselect = False
- for _armobj in all_armatures:
- if _armobj.select_get():
- armature = _armobj
- barmselect = True
- break
- if barmselect is False:
- raise Error("Please select an armatures in the scene")
- else:
- raise Error("No armatures in scene")
-
- verbose("Found armature: {}".format(armature.name))
-
- meshselected = []
- parented_meshes = [obj for obj in armature.children if obj.type == 'MESH']
-
- if len(armature.children) == 0:
- raise Error("The selected Armature has no mesh parented to the Armature Object!")
-
- for obj in armature.children:
- # print(dir(obj))
- if obj.type == 'MESH' and obj.select_get() is True:
- meshselected.append(obj)
- # try the active object
- if active_object and active_object.type == 'MESH' and len(meshselected) == 0:
- if active_object.parent == armature:
- mesh = active_object
- else:
- raise Error("The selected mesh is not parented to the armature")
-
- # otherwise, expect a single mesh parented to the armature (other object types are ignored)
- else:
- print("Number of meshes:", len(parented_meshes))
- print("Number of meshes (selected):", len(meshselected))
- if len(parented_meshes) == 1:
- mesh = parented_meshes[0]
-
- elif len(parented_meshes) > 1:
- if len(meshselected) >= 1:
- mesh = sortmesh(meshselected)
- else:
- raise Error("More than one mesh(s) parented to armature. Select object(s)!")
- else:
- raise Error("No mesh parented to armature")
-
- verbose("Found mesh: {}".format(mesh.name))
- if mesh is None or armature is None:
- raise Error("Check Mesh and Armature are list!")
-
- """
- if len(armature.pose.bones) == len(mesh.vertex_groups):
- print("Armature and Mesh Vertex Groups matches Ok!")
- else:
- raise Error("Armature bones:" + str(len(armature.pose.bones)) +
- " Mesh Vertex Groups:" + str(len(mesh.vertex_groups)) +" doesn't match!")
- """
- # this will check if object need to be rebuild
- if bpy.context.scene.udk_option_rebuildobjects:
- # print("INIT... REBUILDING...")
- print("REBUILDING ARMATURE...")
- # if deform mesh
- # rebuild the armature to raw. If there IK constraint it will ignore it
- armature = rebuildarmature(armature)
- print("REBUILDING MESH...")
- mesh = rebuildmesh(mesh) # rebuild the mesh to raw data format.
-
- return armature, mesh
-
-
-# ===========================================================================
-# Returns a list of vertex groups in the mesh. Can be modified to filter
-# groups as necessary.
-# UNUSED
-# ===========================================================================
-def collate_vertex_groups(mesh):
- verbose("collate_vertex_groups")
- groups = []
-
- for group in mesh.vertex_groups:
-
- groups.append(group)
- verbose(" " + group.name)
-
- return groups
-
-
-# ===========================================================================
-# Main
-# ===========================================================================
-def export(filepath):
- print(header("Export", 'RIGHT'))
- bpy.types.Scene.udk_copy_merge = False # in case fail to export set this to default
- t = time.clock()
- context = bpy.context
-
- print("Blender Version {}.{}.{}".format(bpy.app.version[0], bpy.app.version[1], bpy.app.version[2]))
- print("Filepath: {}".format(filepath))
-
- verbose("PSK={}, PSA={}".format(context.scene.udk_option_export_psk, context.scene.udk_option_export_psa))
-
- # find armature and mesh
- # [change this to implement alternative methods; raise Error() if not found]
- udk_armature, udk_mesh = find_armature_and_mesh()
-
- # check misc conditions
- if not (udk_armature.scale.x == udk_armature.scale.y == udk_armature.scale.z == 1):
- raise Error("bad armature scale: armature object should have uniform scale of 1 (ALT-S)")
-
- if not (udk_mesh.scale.x == udk_mesh.scale.y == udk_mesh.scale.z == 1):
- raise Error("bad mesh scale: mesh object should have uniform scale of 1 (ALT-S)")
-
- if not (udk_armature.location.x == udk_armature.location.y == udk_armature.location.z == 0):
- raise Error("bad armature location: armature should be located at origin (ALT-G)")
-
- if not (udk_mesh.location.x == udk_mesh.location.y == udk_mesh.location.z == 0):
- raise Error("bad mesh location: mesh should be located at origin (ALT-G)")
-
- # prep
- psk = PSKFile()
- psa = PSAFile()
-
- # step 1
- parse_mesh(udk_mesh, psk)
-
- # step 2
- udk_bones = parse_armature(udk_armature, psk, psa)
-
- # step 3
- if context.scene.udk_option_export_psa is True:
- actions = collate_actions()
- parse_animation(udk_armature, udk_bones, actions, psa)
-
- # write files
- print(header("Exporting", 'CENTER'))
-
- psk_filename = filepath + '.psk'
- psa_filename = filepath + '.psa'
-
- if context.scene.udk_option_export_psk is True:
- print("Skeletal mesh data...")
- psk.PrintOut()
- file = open(psk_filename, "wb")
- file.write(psk.dump())
- file.close()
- print("Exported: " + psk_filename)
- print()
-
- if context.scene.udk_option_export_psa is True:
- print("Animation data...")
- if not psa.IsEmpty():
- psa.PrintOut()
- file = open(psa_filename, "wb")
- file.write(psa.dump())
- file.close()
- print("Exported: " + psa_filename)
- else:
- print("No Animation (.psa file) to export")
-
- print()
-
- # if objects are rebuild do the unlink
- if bpy.context.scene.udk_option_rebuildobjects:
- print("Unlinking Objects")
- print("Armature Object Name:", udk_armature.name) # display object name
- bpy.context.collection.objects.unlink(udk_armature) # remove armature from the collection
- print("Mesh Object Name:", udk_mesh.name) # display object name
- bpy.context.collection.objects.unlink(udk_mesh) # remove mesh from the collection
-
- print("Export completed in {:.2f} seconds".format((time.clock() - t)))
-
-
-# ===========================================================================
-# Operator
-# ===========================================================================
-class Operator_UDKExport(Operator):
- """Export to UDK"""
- bl_idname = "object.udk_export"
- bl_label = "Export now"
-
- def execute(self, context):
- print("\n" * 8)
- scene = bpy.context.scene
-
- scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2')
- scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
-
- filepath = get_dst_path()
-
- # cache settings
- restore_frame = scene.frame_current
- message = "Object(s) exported to: {}".format(filepath)
- try:
- export(filepath)
-
- except Error as err:
- print(err.message)
- message = err.message
-
- # restore settings
- scene.frame_set(restore_frame)
-
- def draw(self, context):
- self.layout.label(text="Export Finished")
- try:
- context.window_manager.popup_menu(draw, title=message, icon="INFO")
- except:
- pass
-
- self.report({'INFO'}, message)
-
- # restore settings
- scene.frame_set(restore_frame)
-
- return {'FINISHED'}
-
-
-# ===========================================================================
-# Operator
-# ===========================================================================
-class Operator_ToggleConsole(Operator):
- """Show or hide the console"""
- bl_idname = "object.toggle_console"
- bl_label = "Toggle console"
-
- def execute(self, context):
- bpy.ops.wm.console_toggle()
- return {'FINISHED'}
-
-
-# ===========================================================================
-# Get filepath for export
-# ===========================================================================
-def get_dst_path():
- if bpy.context.scene.udk_option_filename_src == '0':
- if bpy.context.active_object:
- path = os.path.split(bpy.data.filepath)[0] + "\\" + bpy.context.active_object.name # + ".psk"
- else:
- # path = os.path.split(bpy.data.filepath)[0] + "\\" + "Unknown";
- path = os.path.splitext(bpy.data.filepath)[0] # + ".psk"
- else:
- path = os.path.splitext(bpy.data.filepath)[0] # + ".psk"
- return path
-
-
-# ===========================================================================
-# User interface
-# ===========================================================================
-class OBJECT_OT_UTSelectedFaceSmooth(Operator):
- """It will only select smooth faces that is select mesh"""
- bl_idname = "object.utselectfacesmooth" # XXX, name???
- bl_label = "Select Smooth Faces" # "Select Smooth faces"
-
- def invoke(self, context, event):
- print("----------------------------------------")
- print("Init Select Face(s):")
- bselected = False
- for obj in bpy.data.objects:
- if obj.type == 'MESH' and obj.select_get() is True:
- smoothcount = 0
- flatcount = 0
- bpy.ops.object.mode_set(mode='OBJECT') # it need to go into object mode to able to select the faces
-
- for i in bpy.context.scene.objects:
- i.select_set(False) # deselect all objects
-
- obj.select_set(True) # set current object select
- bpy.context.view_layer.objects.active = obj # set active object
- mesh = bmesh.new()
- mesh.from_mesh(obj.data)
-
- for face in mesh.faces:
- face.select = False
-
- for face in mesh.faces:
- if face.smooth is True:
- face.select = True
- smoothcount += 1
- else:
- flatcount += 1
- face.select = False
- mesh.to_mesh(obj.data)
- bpy.context.view_layer.update()
- bpy.ops.object.mode_set(mode='EDIT')
- print("Select Smooth Count(s):", smoothcount, " Flat Count(s):", flatcount)
- bselected = True
- break
- if bselected:
- self.report({'INFO'}, "Selected Face(s) Executed")
- else:
- self.report({'INFO'}, "Mesh Object is not selected")
- print("----------------------------------------")
-
- return{'FINISHED'}
-
-
-class OBJECT_OT_MeshClearWeights(Operator):
- """Remove all mesh vertex groups weights for the bones"""
- bl_idname = "object.meshclearweights" # XXX, name???
- bl_label = "Remove Vertex Weights" # "Remove Mesh vertex weights"
-
- def invoke(self, context, event):
- for obj in bpy.data.objects:
- if obj.type == 'MESH' and obj.select_get() is True:
- for vg in obj.vertex_groups:
- obj.vertex_groups.remove(vg)
- self.report({'INFO'}, "Mesh Vertex Groups Removed")
- break
- return{'FINISHED'}
-
-
-def unpack_list(list_of_tuples):
- l = []
- for t in list_of_tuples:
- l.extend(t)
- return l
-
-
-def rebuildmesh(obj):
- # make sure it in object mode
- print("Mesh Object Name:", obj.name)
- bpy.ops.object.mode_set(mode='OBJECT')
-
- for i in bpy.context.scene.objects:
- i.select_set(False) # deselect all objects
- obj.select_set(True)
- bpy.context.view_layer.objects.active = obj
-
- me_ob = bpy.data.meshes.new(("Re_" + obj.name))
- mesh = obj.data
- faces = []
- verts = []
- smoothings = []
- uvfaces = []
- # print("creating array build mesh...")
- depsgraph = bpy.context.evaluated_depsgraph_get()
- obj_eval = obj.evaluated_get(depsgraph)
- mmesh = obj_eval.to_mesh()
- uv_layer = mmesh.tessface_uv_textures.active
-
- for face in mmesh.tessfaces:
- smoothings.append(face.use_smooth) # smooth or flat in boolean
- if uv_layer is not None: # check if there texture data exist
- faceUV = uv_layer.data[face.index]
- uvs = []
- for uv in faceUV.uv:
- uvs.append((uv[0], uv[1]))
- uvfaces.append(uvs)
- # print((face.vertices[:]))
- if len(face.vertices) == 3:
- faces.extend([(face.vertices[0], face.vertices[1], face.vertices[2], 0)])
- else:
- faces.extend([(face.vertices[0], face.vertices[1], face.vertices[2], face.vertices[3])])
-
- obj_eval.to_mesh_clear()
-
- # vertex positions
- for vertex in mesh.vertices:
- verts.append(vertex.co.to_tuple())
- # vertices weight groups into array
- vertGroups = {} # array in strings
-
- for vgroup in obj.vertex_groups:
- vlist = []
- for v in mesh.vertices:
- for vg in v.groups:
- if vg.group == vgroup.index:
- vlist.append((v.index, vg.weight))
- # print((v.index,vg.weight))
- vertGroups[vgroup.name] = vlist
-
- # print("creating mesh object...")
- # me_ob.from_pydata(verts, [], faces)
- me_ob.vertices.add(len(verts))
- me_ob.tessfaces.add(len(faces))
- me_ob.vertices.foreach_set("co", unpack_list(verts))
- me_ob.tessfaces.foreach_set("vertices_raw", unpack_list(faces))
- me_ob.tessfaces.foreach_set("use_smooth", smoothings) # smooth array from face
-
- # check if there is uv faces
- if len(uvfaces) > 0:
- uvtex = me_ob.tessface_uv_textures.new(name="retex")
- for i, face in enumerate(me_ob.tessfaces):
- blender_tface = uvtex.data[i] # face
- mfaceuv = uvfaces[i]
- if len(mfaceuv) == 3:
- blender_tface.uv1 = mfaceuv[0]
- blender_tface.uv2 = mfaceuv[1]
- blender_tface.uv3 = mfaceuv[2]
- if len(mfaceuv) == 4:
- blender_tface.uv1 = mfaceuv[0]
- blender_tface.uv2 = mfaceuv[1]
- blender_tface.uv3 = mfaceuv[2]
- blender_tface.uv4 = mfaceuv[3]
-
- me_ob.update() # need to update the information to able to see into the secne
- obmesh = bpy.data.objects.new(("Re_" + obj.name), me_ob)
- bpy.context.view_layer.update()
-
- # Build tmp materials
- materialname = "ReMaterial"
- for matcount in mesh.materials:
- matdata = bpy.data.materials.new(materialname)
- me_ob.materials.append(matdata)
-
- # assign face to material id
- for face in mesh.tessfaces:
- me_ob.faces[face.index].material_index = face.material_index
-
- # vertices weight groups
- for vgroup in vertGroups:
- group = obmesh.vertex_groups.new(name=vgroup)
- for v in vertGroups[vgroup]:
- group.add([v[0]], v[1], 'ADD') # group.add(array[vertex id],weight,add)
- bpy.context.collection.objects.link(obmesh)
- # print("Mesh Material Count:",len(me_ob.materials))
- matcount = 0
- # print("MATERIAL ID OREDER:")
- for mat in me_ob.materials:
- # print("-Material:",mat.name,"INDEX:",matcount)
- matcount += 1
-
- print("Mesh Object Name:", obmesh.name)
- bpy.context.view_layer.update()
-
- return obmesh
-
-
-class OBJECT_OT_UTRebuildMesh(Operator):
- """It rebuild the mesh from scrape from the selected mesh object. """ \
- """Note the scale will be 1:1 for object mode. To keep from deforming"""
- bl_idname = "object.utrebuildmesh" # XXX, name???
- bl_label = "Rebuild Mesh" # "Rebuild Mesh"
-
- def invoke(self, context, event):
- print("----------------------------------------")
- print("Init Mesh Bebuild...")
- bselected = False
- bpy.ops.object.mode_set(mode='OBJECT')
-
- for obj in bpy.data.objects:
- if obj.type == 'MESH' and obj.select_get() is True:
- rebuildmesh(obj)
-
- self.report({'INFO'}, "Rebuild Mesh Finished!")
- print("Finish Mesh Build...")
- print("----------------------------------------")
- return{'FINISHED'}
-
-
-def rebuildarmature(obj):
- currentbone = [] # select armature for roll copy
- print("Armature Name:", obj.name)
- objectname = "ArmatureDataPSK"
- meshname = "ArmatureObjectPSK"
- armdata = bpy.data.armatures.new(objectname)
- ob_new = bpy.data.objects.new(meshname, armdata)
- bpy.context.collection.objects.link(ob_new)
- # bpy.ops.object.mode_set(mode='OBJECT')
-
- for i in bpy.context.scene.objects:
- i.select_set(False) # deselect all objects
-
- ob_new.select_set(True)
- bpy.context.view_layer.objects.active = obj
-
- bpy.ops.object.mode_set(mode='EDIT')
- for bone in obj.data.edit_bones:
- if bone.parent is not None:
- currentbone.append([bone.name, bone.roll])
- else:
- currentbone.append([bone.name, bone.roll])
- bpy.ops.object.mode_set(mode='OBJECT')
-
- for i in bpy.context.scene.objects:
- i.select_set(False) # deselect all objects
-
- bpy.context.view_layer.objects.active = ob_new
- bpy.ops.object.mode_set(mode='EDIT')
-
- for bone in obj.data.bones:
- bpy.ops.object.mode_set(mode='EDIT')
- newbone = ob_new.data.edit_bones.new(bone.name)
- newbone.head = bone.head_local
- newbone.tail = bone.tail_local
- for bonelist in currentbone:
- if bone.name == bonelist[0]:
- newbone.roll = bonelist[1]
- break
- if bone.parent is not None:
- parentbone = ob_new.data.edit_bones[bone.parent.name]
- newbone.parent = parentbone
-
- ob_new.animation_data_create() # create animation data
- if obj.animation_data is not None: # check for animation
- # just make sure it here to do the animations if exist
- ob_new.animation_data.action = obj.animation_data.action
-
- print("Armature Object Name:", ob_new.name)
- return ob_new
-
-
-class OBJECT_OT_UTRebuildArmature(Operator):
- """If mesh is deform when importing to unreal engine try this. """ \
- """It rebuild the bones one at the time by select one armature object scrape to raw setup build. """ \
- """Note the scale will be 1:1 for object mode. To keep from deforming"""
- bl_idname = "object.utrebuildarmature" # XXX, name???
- bl_label = "Rebuild Armature" # Rebuild Armature
-
- def invoke(self, context, event):
- print("----------------------------------------")
- print("Init Rebuild Armature...")
- bselected = False
- for obj in bpy.data.objects:
- if obj.type == 'ARMATURE' and obj.select_get() is True:
- rebuildarmature(obj)
- self.report({'INFO'}, "Rebuild Armature Finish!")
- print("End of Rebuild Armature.")
- print("----------------------------------------")
- return{'FINISHED'}
-
-
-class UDKActionSetListPG(PropertyGroup):
- bool: BoolProperty(default=False)
- string: StringProperty()
- actionname: StringProperty()
- bmatch: BoolProperty(
- default=False,
- name="Match",
- options={"HIDDEN"},
- description="This check against bone names and action group "
- "names matches and override boolean if true"
- )
- bexport: BoolProperty(
- default=False,
- name="Export",
- description="Check this to export the animation"
- )
-
-
-class UL_UDKActionSetList(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
- layout.label(item.name)
- layout.prop(item, "bmatch", text="Match")
- layout.prop(item, "bexport", text="Export")
-
-
-class UDKObjListPG(PropertyGroup):
- bool: BoolProperty(default=False)
- string: StringProperty()
- bexport: BoolProperty(
- default=False,
- name="Export",
- options={"HIDDEN"},
- description="This will be ignore when exported"
- )
- bselect: BoolProperty(
- default=False,
- name="Select",
- options={"HIDDEN"},
- description="This will be ignore when exported"
- )
- otype: StringProperty(
- name="Type",
- description="This will be ignore when exported"
- )
-
-
-class UL_UDKObjList(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
- layout.label(item.name)
- layout.prop(item, "otype", text="")
- layout.prop(item, "bselect", text="")
-
-
-class UDKMeshListPG(PropertyGroup):
- bool: BoolProperty(
- default=False
- )
- string: StringProperty()
- bexport: BoolProperty(
- default=False,
- name="Export",
- options={"HIDDEN"},
- description="This object will be export when true"
- )
- bselect: BoolProperty(
- default=False,
- name="Select",
- options={"HIDDEN"},
- description="Make sure you have Mesh is parent to Armature"
- )
- otype: StringProperty(
- name="Type",
- description="This will be ignore when exported"
- )
-
-
-class UL_UDKMeshList(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
- layout.label(item.name)
- # layout.prop(item, "bselect", text="Select")
- layout.prop(item, "bexport", text="Export")
-
-
-class UDKArmListPG(PropertyGroup):
- bool: BoolProperty(default=False)
- string: StringProperty()
- bexport: BoolProperty(
- default=False,
- name="Export",
- options={"HIDDEN"},
- description="This will be ignore when exported"
- )
- bselect: BoolProperty(
- default=False,
- name="Select",
- options={"HIDDEN"},
- description="This will be ignore when exported"
- )
- otype: StringProperty(
- name="Type",
- description="This will be ignore when exported"
- )
-
-
-class UL_UDKArmList(UIList):
- def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
- layout.label(item.name)
-
-
-class Panel_UDKExport(Panel):
- bl_label = "UDK Export"
- bl_idname = "OBJECT_PT_udk_tools"
- bl_category = "File I/O"
- bl_space_type = "VIEW_3D"
- bl_region_type = "TOOLS"
- bl_context = "objectmode"
-
- """
- def draw_header(self, context):
- layout = self.layout
- obj = context.object
- layout.prop(obj, "select", text="")
-
- @classmethod
- def poll(cls, context):
- return context.active_object
- """
-
- def draw(self, context):
- layout = self.layout
- path = get_dst_path()
-
- object_name = ""
- """
- if context.object:
- object_name = context.object.name
- """
- if context.active_object:
- object_name = context.active_object.name
- row10 = layout.row()
- row10.prop(context.scene, "udk_option_smoothing_groups")
- row10.prop(context.scene, "udk_option_clight_uv")
- row10.prop(context.scene, "udk_option_verbose")
-
- row = layout.row()
- row.label(text="Active object: " + object_name)
- layout.prop(context.scene, "udk_option_filename_src")
- row = layout.row()
- row.label(text=path)
-
- layout.prop(context.scene, "udk_option_export")
- layout.prop(context.scene, "udk_option_selectobjects")
-
- if context.scene.udk_option_selectobjects:
- layout.operator("object.selobjectpdate")
- layout.label(text="ARMATURE - Index")
- layout.template_list("UL_UDKArmList", "udk_armatures", context.scene, "udkArm_list",
- context.scene, "udkArm_list_idx", rows=3)
- layout.label(text="MESH - Export")
- layout.template_list("UL_UDKMeshList", "", context.scene, "udkmesh_list",
- context.scene, "udkmesh_list_idx", rows=5)
- layout.prop(context.scene, "udk_option_selectanimations")
-
- if context.scene.udk_option_selectanimations:
- layout.operator("action.setanimupdate")
- layout.label(text="Action Set(s) - Match / Export")
- layout.template_list("UL_UDKActionSetList", "", context.scene, "udkas_list",
- context.scene, "udkas_list_idx", rows=5)
- layout.separator()
- layout.prop(context.scene, "udk_option_scale")
- layout.prop(context.scene, "udk_option_rebuildobjects")
- # layout.prop(context.scene, "udk_option_ignoreactiongroupnames")
-
- row11 = layout.row()
- row11.operator("object.udk_export")
- row11.operator("object.toggle_console")
- layout.operator(OBJECT_OT_UTRebuildArmature.bl_idname)
- layout.label(text="Mesh")
- layout.operator(OBJECT_OT_MeshClearWeights.bl_idname)
- layout.operator(OBJECT_OT_UTSelectedFaceSmooth.bl_idname)
- layout.operator(OBJECT_OT_UTRebuildMesh.bl_idname)
- layout.operator(OBJECT_OT_UDKCheckMeshLines.bl_idname)
-
-
-def udkupdateobjects():
- my_objlist = bpy.context.scene.udkArm_list
- objectl = []
- for objarm in bpy.context.scene.objects: # list and filter only mesh and armature
- if objarm.type == 'ARMATURE':
- objectl.append(objarm)
-
- for _objd in objectl: # check if list has in udk list
- bfound_obj = False
- for _obj in my_objlist:
- if _obj.name == _objd.name and _obj.otype == _objd.type:
- _obj.bselect = _objd.select
- bfound_obj = True
- break
-
- if bfound_obj is False:
- # print("ADD ARMATURE...")
- my_item = my_objlist.add()
- my_item.name = _objd.name
- my_item.bselect = _objd.select
- my_item.otype = _objd.type
- removeobject = []
- for _udkobj in my_objlist:
- bfound_objv = False
-
- for _objd in bpy.context.scene.objects: # check if there no existing object from sense to remove it
- if _udkobj.name == _objd.name and _udkobj.otype == _objd.type:
- bfound_objv = True
- break
-
- if bfound_objv is False:
- removeobject.append(_udkobj)
- # print("remove check...")
- for _item in removeobject: # loop remove object from udk list object
- count = 0
- for _obj in my_objlist:
- if _obj.name == _item.name and _obj.otype == _item.otype:
- my_objlist.remove(count)
- break
- count += 1
-
- my_objlist = bpy.context.scene.udkmesh_list
- objectl = []
- for objarm in bpy.context.scene.objects: # list and filter only mesh and armature
- if objarm.type == 'MESH':
- objectl.append(objarm)
- for _objd in objectl: # check if list has in udk list
- bfound_obj = False
- for _obj in my_objlist:
- if _obj.name == _objd.name and _obj.otype == _objd.type:
- _obj.bselect = _objd.select
- bfound_obj = True
- break
- if bfound_obj is False:
- my_item = my_objlist.add()
- my_item.name = _objd.name
- my_item.bselect = _objd.select
- my_item.otype = _objd.type
- removeobject = []
- for _udkobj in my_objlist:
- bfound_objv = False
- for _objd in bpy.context.scene.objects: # check if there no existing object from sense to remove it
- if _udkobj.name == _objd.name and _udkobj.otype == _objd.type:
- bfound_objv = True
- break
- if bfound_objv is False:
- removeobject.append(_udkobj)
- # print("remove check...")
- for _item in removeobject: # loop remove object from udk list object
- count = 0
- for _obj in my_objlist:
- if _obj.name == _item.name and _obj.otype == _item.otype:
- my_objlist.remove(count)
- break
- count += 1
-
-
-class OBJECT_OT_UDKObjUpdate(Operator):
- """This will update the filter of the mesh and armature"""
- bl_idname = "object.selobjectpdate"
- bl_label = "Update Object(s)"
-
- actionname: bpy.props.StringProperty()
-
- def execute(self, context):
- udkupdateobjects()
- return{'FINISHED'}
-
-
-def udkcheckmeshline():
- objmesh = None
- for obj in bpy.context.scene.objects:
- if obj.type == 'MESH' and obj.select_get() is True:
- objmesh = obj
-
- objmesh = triangulate_mesh(objmesh) # create a copy of the mesh
- bpy.ops.object.mode_set(mode='OBJECT')
-
- for i in bpy.context.scene.objects:
- i.select_set(False) # deselect all objects
-
- objmesh.select_set(True)
- bpy.context.view_layer.objects.active = objmesh # set active mesh
- wedges = ObjMap()
- points = ObjMap()
- bpy.ops.object.mode_set(mode='EDIT') # set in edit mode
- bpy.ops.mesh.select_all(action='DESELECT')
- bpy.context.tool_settings.mesh_select_mode = (True, False, False) # select vertices
-
- if objmesh is not None:
- print("found mesh")
- print(objmesh)
- print(objmesh.data.tessfaces)
- vertex_list = []
- for face in objmesh.data.tessfaces:
- wedge_list = []
- vect_list = []
- for i in range(3):
- vert_index = face.vertices[i]
- vert = objmesh.data.vertices[vert_index]
- vect_list.append(FVector(vert.co.x, vert.co.y, vert.co.z))
- vpos = objmesh.matrix_local * vert.co
- p = VPoint()
- p.Point.X = vpos.x
- p.Point.Y = vpos.y
- p.Point.Z = vpos.z
- w = VVertex()
- w.PointIndex = points.get(p) # store keys
- index_wedge = wedges.get(w)
- wedge_list.append(index_wedge)
- no = face.normal
- norm = FVector(no[0], no[1], no[2])
- tnorm = vect_list[1].sub(vect_list[0]).cross(vect_list[2].sub(vect_list[1]))
- dot = norm.dot(tnorm)
-
- tri = VTriangle()
- if dot > 0:
- (tri.WedgeIndex2, tri.WedgeIndex1, tri.WedgeIndex0) = wedge_list
- elif dot < 0:
- (tri.WedgeIndex0, tri.WedgeIndex1, tri.WedgeIndex2) = wedge_list
- else:
- dindex0 = face.vertices[0]
- dindex1 = face.vertices[1]
- dindex2 = face.vertices[2]
- vertex_list.append(dindex0)
- vertex_list.append(dindex1)
- vertex_list.append(dindex2)
-
- bpy.ops.object.mode_set(mode='OBJECT')
- for vertex in objmesh.data.vertices: # loop all vertex in the mesh list
- for vl in vertex_list: # loop for error vertex
- if vertex.index == vl: # if match set to select
- vertex.select = True
- break
- bpy.ops.object.mode_set(mode='EDIT') # set in edit mode to see the select vertex
- objmesh.data.update() # update object
- bpy.context.view_layer.update() # update scene
- message = "MESH PASS"
- if len(vertex_list) > 0:
- message = "MESH FAIL"
- return message
-
-
-class OBJECT_OT_UDKCheckMeshLines(Operator):
- """Select the mesh for export test. This will create dummy mesh to see which area are broken. """ \
- """If the vertices share the same position it will cause a bug"""
- bl_idname = "object.udkcheckmeshline"
- bl_label = "Check Mesh Vertices"
-
- def execute(self, context):
- message = udkcheckmeshline()
- self.report({'ERROR'}, message)
- return{'FINISHED'}
-
-
-class OBJECT_OT_ActionSetAnimUpdate(Operator):
- """Select Armture to match the action set groups. """ \
- """All bones keys must be set to match with number of bones"""
- bl_idname = "action.setanimupdate"
- bl_label = "Update Action Set(s)"
-
- actionname: bpy.props.StringProperty()
-
- def execute(self, context):
- my_sett = bpy.context.scene.udkas_list
-
- bones = []
- armature = None
- armatures = []
- armatureselected = []
- for objarm in bpy.context.scene.objects:
- if objarm.type == 'ARMATURE':
- # print("ADDED ARMATURE...")
- armatures.append(objarm)
- if objarm.select_get() is True:
- armatureselected.append(objarm)
-
- if len(armatureselected) == len(armatures) == 1:
- armature = armatures[0]
- if len(armatures) == 1:
- armature = armatures[0]
- if len(armatureselected) == 1:
- armature = armatureselected[0]
-
- if armature is not None:
- for bone in armature.pose.bones:
- bones.append(bone.name)
-
- for action in bpy.data.actions: # action list
- bfound = False
- count = 0
- for actionbone in action.groups:
- # print("Pose bone name: ",actionbone.name)
- for b in bones:
- if b == actionbone.name:
- count += 1
- # print(b," : ",actionbone.name)
- break
- for actionlist in my_sett:
- if action.name == actionlist.name:
- bactionfound = True
- if len(bones) == len(action.groups) == count:
- actionlist.bmatch = True
- else:
- actionlist.bmatch = False
- bfound = True
- break
- if bfound is not True:
- my_item = my_sett.add()
- # print(dir(my_item.bmatch))
- my_item.name = action.name
- # my_item.template_list_controls = "bmatch:bexport"
- if len(bones) == len(action.groups) == count:
- my_item.bmatch = True
- else:
- my_item.bmatch = False
- removeactions = []
- # check action list and data actions
- for actionlist in bpy.context.scene.udkas_list:
- bfind = False
- notfound = 0
- for act in bpy.data.actions:
- if actionlist.name == act.name:
- bfind = True
- else:
- notfound += 1
- # print("ACT NAME:",actionlist.name," COUNT",notfound)
- if notfound == len(bpy.data.actions):
- # print("remove :",actionlist.name)
- removeactions.append(actionlist.name)
- # print("Not in the action data list:",len(removeactions))
- # remove list or changes in the name the template list
- for actname in removeactions:
- actioncount = 0
- for actionlist in my_sett:
- # print("action name:",actionlist.name)
- if actionlist.name == actname:
- my_sett.remove(actioncount)
- break
- actioncount += 1
- return{'FINISHED'}
-
-
-class ExportUDKAnimData(Operator):
- """Export Skeleton Mesh / Animation Data file(s). """ \
- """One mesh and one armature else select one mesh or armature to be exported"""
- bl_idname = "export_anim.udk" # this is important since its how bpy.ops.export.udk_anim_data is constructed
- bl_label = "Export PSK/PSA"
-
- # List of operator properties, the attributes will be assigned
- # to the class instance from the operator settings before calling.
-
- filepath: StringProperty(
- subtype='FILE_PATH',
- )
- filter_glob: StringProperty(
- default="*.psk;*.psa",
- options={'HIDDEN'},
- )
- udk_option_scale: FloatProperty(
- name="UDK Scale",
- description="In case you don't want to scale objects manually - "
- "This will just scale position when on export for the skeleton mesh and animation data",
- default=1
- )
- udk_option_rebuildobjects: BoolProperty(
- name="Rebuild Objects",
- description="In case of deform skeleton mesh and animations data - "
- "This will rebuild objects from raw format on export when checked",
- default=False
- )
-
- @classmethod
- def poll(cls, context):
- return context.active_object is not None
-
- def execute(self, context):
- scene = bpy.context.scene
- scene.udk_option_export_psk = (scene.udk_option_export == '0' or scene.udk_option_export == '2')
- scene.udk_option_export_psa = (scene.udk_option_export == '1' or scene.udk_option_export == '2')
- bpy.context.scene.udk_option_scale = self.udk_option_scale
- bpy.context.scene.udk_option_rebuildobjects = self.udk_option_rebuildobjects
-
- filepath = get_dst_path()
-
- # cache settings
- restore_frame = scene.frame_current
-
- message = "Finish Export!"
- try:
- export(filepath)
-
- except Error as err:
- print(err.message)
- message = err.message
-
- # restore settings
- scene.frame_set(restore_frame)
-
- self.report({'WARNING', 'INFO'}, message)
- return {'FINISHED'}
-
- def draw(self, context):
- layout = self.layout
- scene = context.scene
-
- layout.prop(scene, "udk_option_smoothing_groups")
- layout.prop(scene, "udk_option_clight_uv")
- layout.prop(scene, "udk_option_verbose")
- layout.prop(scene, "udk_option_filename_src")
- layout.prop(scene, "udk_option_export")
- layout.prop(self, "udk_option_scale")
- layout.prop(self, "udk_option_rebuildobjects")
-
- def invoke(self, context, event):
- self.udk_option_scale = bpy.context.scene.udk_option_scale
- self.udk_option_rebuildobjects = bpy.context.scene.udk_option_rebuildobjects
-
- wm = context.window_manager
- wm.fileselect_add(self)
- return {'RUNNING_MODAL'}
-
-
-def menu_func(self, context):
- default_path = os.path.splitext(bpy.data.filepath)[0] + ".psk"
- self.layout.operator(ExportUDKAnimData.bl_idname,
- text="Skeleton Mesh / Animation Data (.psk/.psa)").filepath = default_path
-
-
-# Add-ons Preferences Update Panel
-
-# Define Panel classes for updating
-panels = (
- Panel_UDKExport,
- )
-
-
-def update_panel(self, context):
- message = "Export Unreal Engine Format(.psk/.psa): Updating Panel locations has failed"
- try:
- for panel in panels:
- if "bl_rna" in panel.__dict__:
- bpy.utils.unregister_class(panel)
-
- for panel in panels:
- panel.bl_category = context.preferences.addons[__name__].preferences.category
- bpy.utils.register_class(panel)
-
- except Exception as e:
- print("\n[{}]\n{}\n\nError:\n{}".format(__name__, message, e))
- pass
-
-
-class PskAddonPreferences(AddonPreferences):
- # this must match the addon name, use '__package__'
- # when defining this in a submodule of a python package.
- bl_idname = __name__
-
- category: StringProperty(
- name="Tab Category",
- description="Choose a name for the category of the panel",
- default="File I/O",
- update=update_panel
- )
-
- def draw(self, context):
- layout = self.layout
-
- row = layout.row()
- col = row.column()
- col.label(text="Tab Category:")
- col.prop(self, "category", text="")
-
-
-# ===========================================================================
-# Entry
-# ===========================================================================
-def register():
-
- bpy.utils.register_module(__name__)
- bpy.types.TOPBAR_MT_file_export.append(menu_func)
- update_panel(None, bpy.context)
-
- # Added by [MGVS]
- bpy.types.Scene.udk_option_filename_src = EnumProperty(
- name="Filename",
- description="Sets the name for the files",
- items=[
- ('0', "From object", "Name will be taken from object name"),
- ('1', "From Blend", "Name will be taken from .blend file name")
- ],
- default='0'
- )
- bpy.types.Scene.udk_option_export_psk = BoolProperty(
- name="bool export psa",
- description="Boolean for exporting psk format (Skeleton Mesh)",
- default=True
- )
- bpy.types.Scene.udk_option_export_psa = BoolProperty(
- name="bool export psa",
- description="Boolean for exporting psa format (Animation Data)",
- default=True
- )
- bpy.types.Scene.udk_option_clight_uv = BoolProperty(
- name="Clamp UV",
- description="True is to limit Clamp UV co-ordinates to [0-1]. False is unrestricted (x,y)",
- default=False
- )
- bpy.types.Scene.udk_copy_merge = BoolProperty(
- name="Merge Mesh",
- description="This will copy the mesh(s) and merge the object together "
- "and unlink the mesh to be remove while exporting the object",
- default=False
- )
- bpy.types.Scene.udk_option_export = EnumProperty(
- name="Export",
- description="What to export",
- items=[
- ('0', "Mesh only", "Exports the PSK file for the Skeletal Mesh"),
- ('1', "Animation only", "Export the PSA file for Action Set(s)(Animations Data)"),
- ('2', "Mesh & Animation", "Export both PSK and PSA files(Skeletal Mesh/Animation(s) Data)")
- ],
- default='2'
- )
- bpy.types.Scene.udk_option_verbose = BoolProperty(
- name="Verbose",
- description="Verbose console output",
- default=False
- )
- bpy.types.Scene.udk_option_smoothing_groups = BoolProperty(
- name="Smooth Groups",
- description="Activate hard edges as smooth groups",
- default=True
- )
- bpy.types.Scene.udk_option_triangulate = BoolProperty(
- name="Triangulate Mesh",
- description="Convert Quads to Triangles",
- default=False
- )
- bpy.types.Scene.udk_option_selectanimations = BoolProperty(
- name="Select Animation(s)",
- description="Select animation(s) for export to psa file",
- default=False
- )
- bpy.types.Scene.udk_option_selectobjects = BoolProperty(
- name="Select Object(s)",
- description="Select Armature and Mesh(s). Just make sure mesh(s) is parent to armature",
- default=False
- )
- bpy.types.Scene.udk_option_rebuildobjects = BoolProperty(
- name="Rebuild Objects",
- description="In case of deform skeleton mesh and animations data - "
- "This will rebuild objects from raw format on export when checked",
- default=False
- )
- bpy.types.Scene.udk_option_ignoreactiongroupnames = BoolProperty(
- name="Ignore Action Group Names",
- description="This will Ignore Action Set Group Names Check With Armature Bones. "
- "It will override armature to set action set",
- default=False
- )
- bpy.types.Scene.udk_option_scale = FloatProperty(
- name="UDK Scale",
- description="In case you don't want to scale objects manually - "
- "This will just scale position when on export for the skeleton mesh and animation data",
- default=1
- )
- bpy.types.Scene.udkas_list = CollectionProperty(
- type=UDKActionSetListPG
- )
- bpy.types.Scene.udkas_list_idx = IntProperty()
- bpy.types.Scene.udkobj_list = CollectionProperty(
- type=UDKObjListPG
- )
- bpy.types.Scene.udkobj_list_idx = IntProperty()
- bpy.types.Scene.udkmesh_list = CollectionProperty(
- type=UDKMeshListPG
- )
- bpy.types.Scene.udkmesh_list_idx = IntProperty()
- bpy.types.Scene.udkArm_list = CollectionProperty(
- type=UDKArmListPG
- )
- bpy.types.Scene.udkArm_list_idx = IntProperty()
-
-
-def unregister():
- bpy.utils.unregister_module(__name__)
- bpy.types.TOPBAR_MT_file_export.remove(menu_func)
-
- del bpy.types.Scene.udk_option_filename_src
- del bpy.types.Scene.udk_option_export_psk
- del bpy.types.Scene.udk_option_export_psa
- del bpy.types.Scene.udk_option_clight_uv
- del bpy.types.Scene.udk_copy_merge
- del bpy.types.Scene.udk_option_export
- del bpy.types.Scene.udk_option_verbose
- del bpy.types.Scene.udk_option_smoothing_groups
- del bpy.types.Scene.udk_option_triangulate
- del bpy.types.Scene.udk_option_selectanimations
- del bpy.types.Scene.udk_option_selectobjects
- del bpy.types.Scene.udk_option_rebuildobjects
- del bpy.types.Scene.udk_option_ignoreactiongroupnames
- del bpy.types.Scene.udk_option_scale
- del bpy.types.Scene.udkas_list
- del bpy.types.Scene.udkas_list_idx
- del bpy.types.Scene.udkobj_list
- del bpy.types.Scene.udkobj_list_idx
- del bpy.types.Scene.udkmesh_list
- del bpy.types.Scene.udkmesh_list_idx
- del bpy.types.Scene.udkArm_list
- del bpy.types.Scene.udkArm_list_idx
-
-
-if __name__ == "__main__":
- print(header("UDK Export PSK/PSA 2.6", 'CENTER'))
- register()