#!BPY
"""
Name: 'OGRE for Kenshi (*.MESH)'
Blender: 2.80
Group: 'Import/Export'
Tooltip: 'Import/Export Kenhi OGRE mesh files'
Author: Someone
Based on the Torchlight Impost/Export script by 'Dusho'
Thanks goes to 'goatman' for his port of Ogre export script from 2.49b to 2.5x,
and 'CCCenturion' for trying to refactor the code to be nicer (to be included)
"""
__author__ = "someone"
__version__ = "0.9.1 13-Sep-2019"
__bpydoc__ = """\
This script imports/exports Kenshi Ogre models into/from Blender.
Supported:<br>
* import/export of basic meshes
* import/export of skeleton
* import/export of animations
* import/export of vertex weights (ability to import characters and adjust rigs)
* import/export of vertex colour (RGB)
* import/export of vertex alpha (Uses second vertex colour layer called Alpha)
* import/export of shape keys
* Calculation of tangents and binormals for export
Known issues:<br>
* imported materials will lose certain informations not applicable to Blender when exported
History:<br>
* v0.9.1 (13-Sep-2019) - Fixed importing skeletons
* v0.9.0 (07-May-2019) - Switched to Blender 2.80 API
* v0.8.15 (17-Jul-2019) - Added option to import normals
* v0.8.14 (14-May-2019) - Fixed blender deleting zero length bones
* v0.8.13 (19-Mar-2019) - Exporting material files is optional
* v0.8.12 (14-Mar-2019) - Fixed error exporting animation scale keyframes
* v0.8.11 (26-Feb-2019) - Fixed tangents and binormals for mirrorred uvs
* v0.8.10 (32-Jan-2019) - Fixed export when mesh has multiple uv sets
* v0.8.9 (08-Mar-2018) - Added import option to match weight maps and link with a previously imported skeleton
* v0.8.8 (26-Feb-2018) - Fixed export triangulation and custom normals
* v0.8.7 (01-Feb-2018) - Scene frame rate adjusted on import, Fixed quatenion normalisation
* v0.8.6 (31-Jan-2018) - Fixed crash exporting animations in blender 2.79
* v0.8.5 (02-Jan-2018) - Optimisation: Use hashmap for duplicate vertex detection
* v0.8.4 (20-Nov-2017) - Fixed animation quaternion interpolation
* v0.8.3 (06-Nov-2017) - Warning when linked skeleton file not found
* v0.8.2 (25-Sep-2017) - Fixed bone translations in animations
* v0.8.1 (28-Jul-2017) - Added alpha component to vertex colour
* v0.8.0 (30-Jun-2017) - Added animation and shape key support. Rewritten skeleton export
* v0.7.2 (08-Dec-2016) - fixed divide by 0 error calculating tangents
* v0.7.1 (07-Sep-2016) - bug fixes
* v0.7.0 (02-Sep-2016) - Implemented changes needed for Kenshi: Persistant Ogre bone IDs, Export vertex colours. Generates tangents and binormals.
* v0.6.2 (09-Mar-2013) - bug fixes (working with materials+textures), added 'Apply modifiers' and 'Copy textures'
* v0.6.1 (27-Sep-2012) - updated to work with Blender 2.63a
* v0.6 (01-Sep-2012) - added skeleton import + vertex weights import/export
* v0.5 (06-Mar-2012) - added material import/export
* v0.4.1 (29-Feb-2012) - flag for applying transformation, default=true
* v0.4 (28-Feb-2012) - fixing export when no UV data are present
* v0.3 (22-Feb-2012) - WIP - started cleaning + using OgreXMLConverter
* v0.2 (19-Feb-2012) - WIP - working export of geometry and faces
* v0.1 (18-Feb-2012) - initial 2.59 import code (from .xml)
* v0.0 (12-Feb-2012) - file created
"""
"""
When importing: (x)-Blender, (x')-Ogre
vectors: x=x', y=-z', z=y'
UVtex: u=u', v = -v'+1
Inner data representation:
MESHDATA:
['sharedgeometry']: {}
['positions'] - vectors with [x,y,z]
['normals'] - vectors with [x,y,z]
['vertexcolors'] - vectors with [r,g,b,a]
['texcoordsets'] - integer (number of UV sets)
['uvsets'] - vectors with [u,v] * number or UV sets for vertex [[u,v]][[u,v]]...
['boneassignments']: {[boneName]} - for every bone name:
[[vertexNumber], [weight]], [[vertexNumber], [weight]], ..
['submeshes'][idx]
[material] - string (material name)
[materialOrg] - original material name - for searching the in shared materials file
[faces] - vectors with faces [v1,v2,v3]
[geometry] - identical to 'sharedgeometry' data content
['materials']
[(matID)]: {}
['texture'] - full path to texture file
['imageNameOnly'] - only image name from material file
['skeleton']: {[boneName]} for each bone
['name'] - bone name
['id'] - bone ID
['position'] - bone position [x,y,z]
['rotation'] - bone rotation [x,y,z,angle]
['parent'] - bone name of parent bone
['children'] - list with names if children ([child1, child2, ...])
['boneIDs']: {[bone ID]:[bone Name]} - dictionary with ID to name
['skeletonName'] - name of skeleton
Note: Bones store their OGREID as a custom variable so they are consistent when a mesh is exported
"""
#from Blender import *
from xml.dom import minidom
import bpy
from mathutils import Vector, Matrix
import math
import os
import subprocess
SHOW_IMPORT_DUMPS = False
SHOW_IMPORT_TRACE = False
DEFAULT_KEEP_XML = False
# default blender version of script
blender_version = 259
# makes sure name doesn't exceeds blender naming limits
# also keeps after name (as Torchlight uses names to identify types -boots, chest, ...- with names)
# TODO: this is not needed for Blender 2.62 and above
def GetValidBlenderName(name):
global blender_version
maxChars = 20
if blender_version>262:
maxChars = 63
newname = name
if(len(name) > maxChars):
if(name.find("/") >= 0):
if(name.find("Material") >= 0):
# replace 'Material' string with only 'Mt'
newname = name.replace("Material","Mt")
# check if it's still above 20
if(len(newname) > maxChars):
suffix = newname[newname.find("/"):]
prefix = newname[0:(maxChars+1-len(suffix))]
newname = prefix + suffix
else:
newname = name[0:maxChars+1]
if(newname!=name):
print("WARNING: Name truncated (" + name + " -> " + newname + ")")
return newname
def xOpenFile(filename):
xml_file = open(filename)
try:
xml_doc = minidom.parse(xml_file)
output = xml_doc
except:
print ("File not valid!")
output = 'None'
xml_file.close()
return output
def xCollectFaceData(facedata):
faces = []
for face in facedata.childNodes:
if face.localName == 'face':
v1 = int(face.getAttributeNode('v1').value)
v2 = int(face.getAttributeNode('v2').value)
v3 = int(face.getAttributeNode('v3').value)
faces.append([v1,v2,v3])
return faces
def xCollectVertexData(data, useNormals):
vertexdata = {}
vertices = []
normals = []
vertexcolors = []
for vb in data.childNodes:
if vb.localName == 'vertexbuffer':
if vb.hasAttribute('positions'):
for vertex in vb.getElementsByTagName('vertex'):
for vp in vertex.childNodes:
if vp.localName == 'position':
x = float(vp.getAttributeNode('x').value)
y = -float(vp.getAttributeNode('z').value)
z = float(vp.getAttributeNode('y').value)
vertices.append([x,y,z])
vertexdata['positions'] = vertices
if vb.hasAttribute('normals') and useNormals:
for vertex in vb.getElementsByTagName('vertex'):
for vn in vertex.childNodes:
if vn.localName == 'normal':
x = float(vn.getAttribu