I made tweaks to the script to work with that one geo file u shared (i don't know if it'll work with anything else) alot of code had to be deleted or restructured because the file you sent does not have a h3d so it was impossible to load the file without deleting sections of the material code.
in the future please put some effort into organizing information and uploading a variety of sufficient file samples. it is frustrating working on only a few files, files that are missing side files, or files that are mixed in from different games of different versions or generations of files...
Code: Select all
""" ======================================================================
PythonScript: [Mobile] Offroad Legends
Author: mariokart64n
Date: March 20, 2021
Version: 0.1
======================================================================
ChangeLog:
2021-03-16
Script Wrote
2021-03-20
Updates Made to work with Version 6 files
H3D loading was stripped to load just geo (materials are now removed gone)
====================================================================== """
import bpy # Needed to interface with blender
import struct # Needed for Binary Reader
import math
from pathlib import Path # Needed for os stuff
from xml.dom import minidom
useOpenDialog = True
signed, unsigned = 0, 1 # Enums for read function
seek_set, seek_cur, seek_end = 0, 1, 2 # Enums for seek function
def deleteScene(include=[]):
if len(include) > 0:
# Exit and Interactions
if bpy.context.view_layer.objects.active != None:
bpy.ops.object.mode_set(mode='OBJECT')
# Select All
bpy.ops.object.select_all(action='SELECT')
# Loop Through Each Selection
for o in bpy.context.view_layer.objects.selected:
for t in include:
if o.type == t:
bpy.data.objects.remove(o, do_unlink=True)
break
# De-Select All
bpy.ops.object.select_all(action='DESELECT')
return None
def messageBox(message="", title="Message Box", icon='INFO'):
def draw(self, context): self.layout.label(text=message)
bpy.context.window_manager.popup_menu(draw, title=title, icon=icon)
return None
def doesFileExist(filename):
file = Path(filename)
if file.is_file():
return True
elif file.is_dir():
return True
else:
return False
def clearListener(len=64):
for i in range(0, len): print('')
def getFilenamePath(file): # returns: "g:\subdir1\subdir2\"
return (str(Path(file).resolve().parent) + "\\")
def getFilenameFile(file): # returns: "myImage"
return Path(file).stem
def Bitmaptexture(mat, filename="", name="ShaderNodeTexImage"):
imageTex = mat.node_tree.nodes.new('ShaderNodeTexImage')
imageTex.label = name
try:
imageTex.image = bpy.data.images.load(
filepath=filename,
check_existing=False
)
imageTex.image.name = filenameFromPath(filename)
imageTex.image.colorspace_settings.name = 'sRGB'
except:
imageTex.image = bpy.data.images.new(
name=filename,
width=8,
height=8,
alpha=False,
float_buffer=False
)
return imageTex
class StandardMaterial:
data = None
bsdf = None
def __init__(self, name="Material"):
# make material
self.data = bpy.data.materials.new(name=name)
self.data.use_nodes = True
self.data.use_backface_culling = True
self.bsdf = self.data.node_tree.nodes["Principled BSDF"]
self.bsdf.label = "Standard"
return None
def diffuse(self, colour=(0.0, 0.0, 0.0, 0.0), name="Diffuse"):
rgbaColor = self.data.node_tree.nodes.new('ShaderNodeRGB')
rgbaColor.label = name
rgbaColor.outputs[0].default_value = (colour[0], colour[1], colour[2], colour[3])
if self.bsdf != None:
self.data.node_tree.links.new(self.bsdf.inputs['Base Color'], rgbaColor.outputs['Color'])
return None
def diffuseMap(self, imageTex=None):
if imageTex != None and self.bsdf != None:
self.data.node_tree.links.new(self.bsdf.inputs['Base Color'], imageTex.outputs['Color'])
return None
def opacityMap(self, imageTex=None):
if imageTex != None and self.bsdf != None:
self.data.blend_method = 'BLEND'
self.data.shadow_method = 'HASHED'
self.data.show_transparent_back = False
self.data.node_tree.links.new(self.bsdf.inputs['Alpha'], imageTex.outputs['Alpha'])
return None
def normalMap(self, imageNode=None):
if imageTex != None and self.bsdf != None:
imageTex.image.colorspace_settings.name = 'Linear'
normMap = self.data.node_tree.nodes.new('ShaderNodeNormalMap')
normMap.label = 'ShaderNodeNormalMap'
self.data.node_tree.links.new(normMap.inputs['Color'], imageTex.outputs['Color'])
self.data.node_tree.links.new(self.bsdf.inputs['Normal'], normMap.outputs['Normal'])
return None
def specularMap(self, imageNode=None, invert=True):
if imageTex != None and self.bsdf != None:
if invert:
invertRGB = self.data.node_tree.nodes.new('ShaderNodeInvert')
invertRGB.label = 'ShaderNodeInvert'
self.data.node_tree.links.new(invertRGB.inputs['Color'], imageTex.outputs['Color'])
self.data.node_tree.links.new(self.bsdf.inputs['Roughness'], invertRGB.outputs['Color'])
else:
self.data.node_tree.links.new(self.bsdf.inputs['Roughness'], imageTex.outputs['Color'])
return None
class fopen:
little_endian = True
file = ""
mode = 'rb'
data = bytearray()
size = 0
pos = 0
isGood = False
def __init__(self, filename=None, mode='rb', isLittleEndian=True):
if mode == 'rb':
if filename != None and Path(filename).is_file():
self.data = open(filename, mode).read()
self.size = len(self.data)
self.pos = 0
self.mode = mode
self.file = filename
self.little_endian = isLittleEndian
self.isGood = True
else:
self.file = filename
self.mode = mode
self.data = bytearray()
self.pos = 0
self.size = 0
self.little_endian = isLittleEndian
self.isGood = False
return None
# def __del__(self):
# self.flush()
def resize(self, dataSize=0):
if dataSize > 0:
self.data = bytearray(dataSize)
else:
self.data = bytearray()
self.pos = 0
self.size = dataSize
self.isGood = False
return None
def flush(self):
print("flush")
print("file:\t%s" % self.file)
print("isGood:\t%s" % self.isGood)
print("size:\t%s" % len(self.data))
if self.file != "" and not self.isGood and len(self.data) > 0:
self.isGood = True
s = open(self.file, 'w+b')
s.write(self.data)
s.close()
def read_and_unpack(self, unpack, size):
'''
Charactor, Byte-order
@, native, native
=, native, standard
<, little endian
>, big endian
!, network
Format, C-type, Python-type, Size[byte]
c, char, byte, 1
b, signed char, integer, 1
B, unsigned char, integer, 1
h, short, integer, 2
H, unsigned short, integer, 2
i, int, integer, 4
I, unsigned int, integer, 4
f, float, float, 4
d, double, float, 8
'''
value = 0
if self.size > 0 and self.pos + size < self.size:
value = struct.unpack_from(unpack, self.data, self.pos)[0]
self.pos += size
return value
def pack_and_write(self, pack, size, value):
if self.pos + size > self.size:
self.data.extend(b'\x00' * ((self.pos + size) - self.size))
self.size = self.pos + size
try:
struct.pack_into(pack, self.data, self.pos, value)
except:
print('Pos:\t%i / %i (buf:%i) [val:%i:%i:%s]' % (self.pos, self.size, len(self.data), value, size, pack))
pass
self.pos += size
return None
def set_pointer(self, offset):
self.pos = offset
return None
def fseek(bitStream, offset, dir):
if dir == 0:
bitStream.set_pointer(offset)
elif dir == 1:
bitStream.set_pointer(bitStream.pos + offset)
elif dir == 2:
bitStream.set_pointer(bitStream.pos - offset)
return None
def ftell(bitStream):
return bitStream.pos
def fclose(bitStream):
bitStream.flush()
bitStream.isGood = False
def readShort(bitStream, isSigned=0):
fmt = '>' if not bitStream.little_endian else '<'
fmt += 'h' if isSigned == 0 else 'H'
return (bitStream.read_and_unpack(fmt, 2))
def readLong(bitStream, isSigned=0):
fmt = '>' if not bitStream.little_endian else '<'
fmt += 'i' if isSigned == 0 else 'I'
return (bitStream.read_and_unpack(fmt, 4))
def readFloat(bitStream):
fmt = '>f' if not bitStream.little_endian else '<f'
return (bitStream.read_and_unpack(fmt, 4))
def mesh_validate (vertices=[], faces=[]):
#
# Returns True if mesh is BAD
#
# check face index bound
face_min = 0
face_max = len(vertices) - 1
for face in faces:
for side in face:
if side < face_min or side > face_max:
print("Face Index Out of Range:\t[%i / %i]" % (side, face_max))
return True
return False
def mesh(
vertices=[],
faces=[],
materialIDs=[],
tverts=[],
normals=[],
colours=[],
materials=[],
mscale=1.0,
flipAxis=False,
obj_name="Object",
lay_name='',
position = (0.0, 0.0, 0.0)
):
#
# This function is pretty, ugly
# imports the mesh into blender
#
# Clear Any Object Selections
# for o in bpy.context.selected_objects: o.select = False
bpy.context.view_layer.objects.active = None
# Get Collection (Layers)
if lay_name != '':
# make collection
layer = bpy.data.collections.new(lay_name)
bpy.context.scene.collection.children.link(layer)
else:
layer = bpy.data.collections[bpy.context.view_layer.active_layer_collection.name]
# make mesh
msh = bpy.data.meshes.new('Mesh')
# msh.name = msh.name.replace(".", "_")
# Apply vertex scaling
# mscale *= bpy.context.scene.unit_settings.scale_length
if len(vertices) > 0:
vertArray = [[float] * 3] * len(vertices)
if flipAxis:
for v in range(0, len(vertices)):
vertArray[v] = (
vertices[v][0] * mscale,
-vertices[v][2] * mscale,
vertices[v][1] * mscale
)
else:
for v in range(0, len(vertices)):
vertArray[v] = (
vertices[v][0] * mscale,
vertices[v][1] * mscale,
vertices[v][2] * mscale
)
if mesh_validate(vertArray, faces):
# Erase Mesh
msh.user_clear()
bpy.data.meshes.remove(msh)
print("Mesh Deleted!")
return None
# assign data from arrays
msh.from_pydata(vertArray, [], faces)
# set surface to smooth
msh.polygons.foreach_set("use_smooth", [True] * len(msh.polygons))
# Set Normals
if len(faces) > 0:
if len(normals) > 0:
msh.use_auto_smooth = True
if len(normals) == (len(faces) * 3):
msh.normals_split_custom_set(normals)
else:
normArray = [[float] * 3] * (len(faces) * 3)
if flipAxis:
for i in range(0, len(faces)):
for v in range(0, 3):
normArray[(i * 3) + v] = (
[normals[faces[i][v]][0],
-normals[faces[i][v]][2],
normals[faces[i][v]][1]]
)
else:
for i in range(0, len(faces)):
for v in range(0, 3):
normArray[(i * 3) + v] = (
[normals[faces[i][v]][0],
normals[faces[i][v]][1],
normals[faces[i][v]][2]]
)
msh.normals_split_custom_set(normArray)
# create texture corrdinates
#print("tverts ", len(tverts))
# this is just a hack, i just add all the UVs into the same space <<<
if len(tverts) > 0:
uvw = msh.uv_layers.new()
# if len(tverts) == (len(faces) * 3):
# for v in range(0, len(faces) * 3):
# msh.uv_layers[uvw.name].data[v].uv = tverts[v]
# else:
uvwArray = [[float] * 2] * len(tverts[0])
for i in range(0, len(tverts[0])):
uvwArray[i] = [0.0, 0.0]
for v in range(0, len(tverts[0])):
for i in range(0, len(tverts)):
uvwArray[v][0] += tverts[i][v][0]
uvwArray[v][1] += 1.0 - tverts[i][v][1]
for i in range(0, len(faces)):
for v in range(0, 3):
msh.uv_layers[uvw.name].data[(i * 3) + v].uv = (
uvwArray[faces[i][v]][0],
uvwArray[faces[i][v]][1]
)
# Create Face Maps?
# msh.face_maps.new()
# Update Mesh
msh.update()
# Check mesh is Valid
# Without this blender may crash!!! lulz
# However the check will throw false positives so
# and additional or a replacement valatiation function
# would be required
if msh.validate():
print("Mesh Failed Validation")
# Erase Mesh
#msh.user_clear()
#bpy.data.meshes.remove(msh)
#print("Mesh Deleted!")
#return None
# Assign Mesh to Object
obj = bpy.data.objects.new(obj_name, msh)
obj.location = position
# obj.name = obj.name.replace(".", "_")
for i in range(0, len(materials)):
if len(obj.material_slots) < (i + 1):
# if there is no slot then we append to create the slot and assign
obj.data.materials.append(materials[i])
else:
# we always want the material in slot[0]
obj.material_slots[0].material = materials[i]
# obj.active_material = obj.material_slots[i].material
if len(materialIDs) == len(obj.data.polygons):
if len(materialIDs) > 0:
for i in range(0, len(materialIDs)):
obj.data.polygons[i].material_index = materialIDs[i]
elif len(materialIDs) > 0:
for i in range(0, len(obj.data.polygons)):
if i < len(materialIDs):
obj.data.polygons[i].material_index = materialIDs[i]
else:
obj.data.polygons[i].material_index = 0
print("Error:\tMaterial Index Out of Range")
# obj.data.materials.append(material)
layer.objects.link(obj)
# Generate a Material
# img_name = "Test.jpg" # dummy texture
# mat_count = len(texmaps)
# if mat_count == 0 and len(materialIDs) > 0:
# for i in range(0, len(materialIDs)):
# if (materialIDs[i] + 1) > mat_count: mat_count = materialIDs[i] + 1
# Assign Material ID's
bpy.context.view_layer.objects.active = obj
bpy.ops.object.mode_set(mode='EDIT', toggle=False)
bpy.context.tool_settings.mesh_select_mode = [False, False, True]
bpy.ops.object.mode_set(mode='OBJECT')
# materialIDs
# Redraw Entire Scene
# bpy.context.scene.update()
return obj
class h3d_uniform:
name = ""
a = 0.0
b = 0.0
c = 0.0
d = 0.0
class h3d_sampler:
name = ""
map = ""
class h3d_material:
name = ""
sampler = []
uniform = []
class h3d_geo_block:
index = 0
stride = 0
data = []
class h3d_geo_file:
fileid = 0 # 0x47443348 'H3DG'
unk01 = 0 # VERSION '5'
unk02 = 0 # Num Bones
matrix = []
unk03 = 0 # Num Vertex Components
vertex_count = 0
# Vertex Stream starts here
vertices = h3d_geo_block()
normals = h3d_geo_block()
texcoord0 = h3d_geo_block()
texcoord1 = h3d_geo_block()
face_count = 0
faces = []
def __repr__(self):
return 'VertexCount:\t%i\nFaceCount:\t%i\nUnknowns:\t%i\t%i\t%i' % (
self.vertex_count, self.face_count, self.unk01, self.unk02, self.unk03)
def read(self, f=fopen()):
self.fileid = readLong(f)
if self.fileid != 0x47443348:
print("Error:\tInvalid File")
return False
self.unk01 = readLong(f)
if self.unk01 != 5 and self.unk01 !=6:
print("Version Unsupported")
return False
self.unk02 = readLong(f)
for i in range(0, self.unk02):
self.matrix.append((
(readFloat(f), readFloat(f), readFloat(f), readFloat(f)),
(readFloat(f), readFloat(f), readFloat(f), readFloat(f)),
(readFloat(f), readFloat(f), readFloat(f), readFloat(f)),
(readFloat(f), readFloat(f), readFloat(f), readFloat(f))
))
self.unk03 = readLong(f)
self.vertex_count = readLong(f)
#print ("vertex_count:\t%i" % self.vertex_count)
for vs in range(0, self.unk03):
block_id = readLong(f)
block_stride = readLong(f)
block_start = f.pos
#print ("block_start:\t%i" % block_start)
#print ("block_stride:\t%i" % block_stride)
if block_id == 0: # Positions
print('BlockID:\tPOSITION')
comp = int(block_stride / 4)
self.vertices.data = ([[[float] for x in range(comp)] for y in range(self.vertex_count)])
for x in range(0, self.vertex_count):
for y in range(0, comp):
self.vertices.data[x][y] = readFloat(f)
elif block_id == 1: # Normals
print('BlockID:\tNORMAL')
comp = int(block_stride / 2)
self.normals.data = ([[[float] for x in range(comp)] for y in range(self.vertex_count)])
for x in range(0, self.vertex_count):
for y in range(0, comp):
self.normals.data[x][y] = float(readShort(f) / 32767.0)
#elif block_id == 2: # Tangents
#elif block_id == 3: # BiNormals
#elif block_id == 4: # Bone Ids
#elif block_id == 5: # Weights
elif block_id == 6: # Texture UV0
print('BlockID:\tTEXCOORD0')
if block_stride == 4:
comp = int(block_stride / 2)
self.texcoord0.data = ([[[float] for x in range(comp)] for y in range(self.vertex_count)])
for x in range(0, self.vertex_count):
for y in range(0, comp):
self.texcoord0.data[x][y] = float(readShort(f) / 32767.0)
elif block_stride == 6:
comp = int(block_stride / 4)
self.texcoord0.data = ([[[float] for x in range(comp)] for y in range(self.vertex_count)])
for x in range(0, self.vertex_count):
for y in range(0, comp):
self.texcoord0.data[x][y] = readFloat(f)
elif block_id == 7: # Texture UV1
print('BlockID:\tTEXCOORD1')
comp = int(block_stride / 4)
self.texcoord1.data = ([[[float] for x in range(comp)] for y in range(self.vertex_count)])
for x in range(0, self.vertex_count):
for y in range(0, comp):
self.texcoord1.data[x][y] = readFloat(f)
else:
print("Unidentified Block ID:\t%i" % block_id)
f.set_pointer(block_start + (self.vertex_count * block_stride))
# Faces
self.face_count = readLong(f)
self.faces = [[int] for x in range(self.face_count)]
face_stride = 4
if self.unk01 == 6:
face_stride = readLong(f)
if face_stride == 4:
for x in range(0, self.face_count):
self.faces[x] = readLong(f)
elif face_stride == 2:
for x in range(0, self.face_count):
self.faces[x] = readShort(f)
return True
def read_triangle_strip(faces=[], faceArray=[], faceCount=0, facePosition=0, faceOffset=0):
i = 0
face = [1, 1, 1]
while i < faceCount:
faceCW = True
face[0] = faces[i + facePosition]
face[1] = faces[i + facePosition + 1]
i += 2
while i < faceCount:
face[2] = faces[i + facePosition]
if face[2] == 0xFFFF or face[2] == -1: break
if face[0] != face[2] and face[1] != face[2] and face[2] != face[0]:
if faceCW:
faceArray.append([
face[0] + faceOffset,
face[1] + faceOffset,
face[2] + faceOffset
])
else:
faceArray.append([
face[0] + faceOffset,
face[2] + faceOffset,
face[1] + faceOffset
])
faceCW = not faceCW
face = [face[1], face[2], face[0]]
i += 1
return None
def read_triangle_list(faces=[], faceArray=[], faceCount=0, facePosition=0, faceOffset=0):
for i in range(0, int(faceCount / 3)):
faceArray.append([
faces[(i * 3) + facePosition] + faceOffset,
faces[(i * 3) + facePosition + 1] + faceOffset,
faces[(i * 3) + facePosition + 2] + faceOffset
])
return None
def read(file="", mscale=1.0):
# Check File is present
if not doesFileExist(file):
print("Error:\tFailed To Find Geo File")
return False
# Strip Paths From fullpath to find sister file
fpath = getFilenamePath(file)
fname = getFilenameFile(file)
h3d_file = fpath + fname + ".h3d"
# Check if sister file is found
if not doesFileExist(h3d_file):
print("Error:\tFailed To Find H3D File")
#return False
# open GEO file
f = fopen(file, 'rb')
# Create GEO Object to store data from Geo file
geo = h3d_geo_file()
# Attempt to read GEO file into Geo Class
read_good = False
try:
read_good = geo.read(f)
except:
print("Error:\t Failed to Read File")
return False
# Check if Geo File Was Read
if not read_good:
print("Error:\t Failed to Read File")
return False
# Print Geo Class Info
print(repr(geo))
# Close Geo File
fclose(f)
# Read Sister File, import data
h3d = None
h3d_mat = []
h3d_mat_index = 0
h3d_sp = h3d_sampler()
h3d_un = h3d_uniform()
msh = None
msh_name = ""
msh_matn = ""
msh_tx = 0.0
msh_ty = 0.0
msh_tz = 0.0
msh_batchStart = 0
msh_batchCount = 0
msh_vertRStart = 0
msh_vertREnd = 0
msh_vertRCount = 0
mat_index = 0
mat_name = ""
vertArray = []
normArray = []
uvw0Array = []
uvw1Array = []
faceArray = []
matidArray = []
uvSet = []
mats = []
try:
h3d = minidom.parse(h3d_file)
for materials in h3d.getElementsByTagName('Materials'):
for material in materials.getElementsByTagName('Material'):
h3d_mat.append(h3d_material())
h3d_mat[h3d_mat_index].name = material.attributes['name'].value
for sampler in material.getElementsByTagName('Sampler'):
h3d_sp = h3d_sampler()
try:
h3d_sp.name = sampler.attributes['name'].value
h3d_sp.map = sampler.attributes['map'].value
h3d_mat[h3d_mat_index].sampler.append(h3d_sp)
except:
pass
for uniform in material.getElementsByTagName('Uniform'):
h3d_un = h3d_uniform()
try:
h3d_un.name = sampler.attributes['name'].value
h3d_un.a = float(sampler.attributes['a'].value)
h3d_un.b = float(sampler.attributes['b'].value)
h3d_un.c = float(sampler.attributes['c'].value)
h3d_un.d = float(sampler.attributes['d'].value)
h3d_mat[h3d_mat_index].uniform.append(h3d_un)
except:
pass
h3d_mat_index += 1
except:
print("Error:\tFailed to Parse XML file")
#return False
if h3d != None:
for m in h3d_mat:
mat = StandardMaterial(m.name)
for t in m.sampler:
if t.name == 'albedoMap':
if '/' in t.map:
mat.diffuseMap(Bitmaptexture(mat.data, filename=t.map.split('/')[-1:][0]))
else:
mat.diffuseMap(Bitmaptexture(mat.data, filename=t.map))
mats.append(mat.data)
for gmesh in h3d.getElementsByTagName('Mesh'):
try:
msh_name = gmesh.attributes['name'].value
except:
pass
try:
msh_matn = gmesh.attributes['material'].value
except:
pass
try:
msh_tx = float(gmesh.attributes['tx'].value)
except:
pass
try:
msh_ty = float(gmesh.attributes['ty'].value)
except:
pass
try:
msh_tz = float(gmesh.attributes['tz'].value)
except:
pass
try:
msh_batchStart = int(gmesh.attributes['batchStart'].value)
except:
pass
try:
msh_batchCount = int(gmesh.attributes['batchCount'].value)
except:
pass
try:
msh_vertRStart = int(gmesh.attributes['vertRStart'].value)
except:
pass
try:
msh_vertREnd = int(gmesh.attributes['vertREnd'].value)
except:
pass
mat_index = 0
if '#' in msh_matn:
mat_name = msh_matn.split('#')[-1:][0]
mat_index = 0
for i in range(0, len(h3d_mat)):
if h3d_mat[i].name == mat_name:
mat_index = i
break
mat_index = mat_index % len(h3d_mat)
msh_vertRCount = msh_vertREnd - msh_vertRStart + 1
vertArray = ([[[float] for x in range(3)] for y in range(msh_vertRCount)])
normArray = ([[[float] for x in range(3)] for y in range(msh_vertRCount)])
uvw0Array = ([[[float] for x in range(3)] for y in range(msh_vertRCount)])
uvw1Array = ([[[float] for x in range(3)] for y in range(msh_vertRCount)])
uvSet = []
for i in range(0, msh_vertRCount):
vertArray[i] = geo.vertices.data[msh_vertRStart + i]
for i in range(0, msh_vertRCount):
normArray[i] = geo.normals.data[msh_vertRStart + i]
if len(geo.texcoord0.data) >= msh_vertRStart + msh_vertRCount:
for i in range(0, msh_vertRCount):
uvw0Array[i] = geo.texcoord0.data[msh_vertRStart + i]
uvSet.append(uvw0Array)
if len(geo.texcoord1.data) >= msh_vertRStart + msh_vertRCount:
for i in range(0, msh_vertRCount):
uvw1Array[i] = geo.texcoord1.data[msh_vertRStart + i]
uvSet.append(uvw1Array)
faceArray = []
matidArray = []
matidArray = [mat_index for i in range(int(msh_batchCount / 3))]
read_triangle_list(geo.faces, faceArray, msh_batchCount, msh_batchStart, -msh_vertRStart)
msh = mesh(
vertices=vertArray,
tverts=uvSet,
normals=normArray,
faces=faceArray,
obj_name=msh_name,
flipAxis=True,
mscale=mscale,
materials=mats,
materialIDs=matidArray,
position=(msh_tx, -msh_tz, msh_ty)
)
else:
uvSet = []
if len(geo.texcoord0.data) > 0:
uvSet.append(geo.texcoord0.data)
if len(geo.texcoord1.data) > 0:
uvSet.append(geo.texcoord1.data)
faceArray = []
read_triangle_list(geo.faces, faceArray, geo.face_count, 0, 0)
msh = mesh (
vertices=geo.vertices.data,
tverts=uvSet,
normals=geo.normals.data,
faces=faceArray,
flipAxis=True,
mscale=mscale
)
return True
# Callback when file(s) are selected
def offroad_legends_imp_callback(fpath="", files=[], clearScene=True, mscale = 1.0):
if len(files) > 0 and clearScene: deleteScene(['MESH', 'ARMATURE'])
for file in files:
read(fpath + file.name, mscale)
if len(files) > 0:
messageBox("Done!")
return True
else:
return False
# Wrapper that Invokes FileSelector to open files from blender
def offroad_legends_imp(reload=False):
# Un-Register Operator
if reload and hasattr(bpy.types, "IMPORTHELPER_OT_offroad_legends_imp"): # print(bpy.ops.importhelper.offroad_legends_imp.idname())
try:
bpy.types.TOPBAR_MT_file_import.remove(
bpy.types.Operator.bl_rna_get_subclass_py('IMPORTHELPER_OT_offroad_legends_imp').menu_func_import)
except:
print("Failed to Unregister2")
try:
bpy.utils.unregister_class(bpy.types.Operator.bl_rna_get_subclass_py('IMPORTHELPER_OT_offroad_legends_imp'))
except:
print("Failed to Unregister1")
# Define Operator
class ImportHelper_offroad_legends_imp(bpy.types.Operator):
# Operator Path
bl_idname = "importhelper.offroad_legends_imp"
bl_label = "Select File"
# Operator Properties
# filter_glob: bpy.props.StringProperty(default='*.jpg;*.jpeg;*.png;*.tif;*.tiff;*.bmp', options={'HIDDEN'})
filter_glob: bpy.props.StringProperty(default='*.geo', options={'HIDDEN'}, subtype='FILE_PATH')
# Variables
filepath: bpy.props.StringProperty(subtype="FILE_PATH") # full path of selected item (path+filename)
filename: bpy.props.StringProperty(subtype="FILE_NAME") # name of selected item
directory: bpy.props.StringProperty(subtype="FILE_PATH") # directory of the selected item
files: bpy.props.CollectionProperty(type=bpy.types.OperatorFileListElement) # a collection containing all the selected items as filenames
# Controls
my_float1: bpy.props.FloatProperty(name="Scale", default=1.0, description="Changes Scale of the imported Mesh")
my_bool1: bpy.props.BoolProperty(name="Clear Scene", default=True, description="Deletes everything in the scene prior to importing")
# Runs when this class OPENS
def invoke(self, context, event):
# Retrieve Settings
try: self.filepath = bpy.types.Scene.offroad_legends_imp_filepath
except: bpy.types.Scene.offroad_legends_imp_filepath = bpy.props.StringProperty(subtype="FILE_PATH")
try: self.directory = bpy.types.Scene.offroad_legends_imp_directory
except: bpy.types.Scene.offroad_legends_imp_directory = bpy.props.StringProperty(subtype="FILE_PATH")
try: self.my_float1 = bpy.types.Scene.offroad_legends_imp_my_float1
except: bpy.types.Scene.offroad_legends_imp_my_float1 = bpy.props.FloatProperty(default=1.0)
try: self.my_bool1 = bpy.types.Scene.offroad_legends_imp_my_bool1
except: bpy.types.Scene.offroad_legends_imp_my_bool1 = bpy.props.BoolProperty(default=False)
# Open File Browser
# Set Properties of the File Browser
context.window_manager.fileselect_add(self)
context.area.tag_redraw()
return {'RUNNING_MODAL'}
# Runs when this Window is CANCELLED
def cancel(self, context): print("run bitch")
# Runs when the class EXITS
def execute(self, context):
# Save Settings
bpy.types.Scene.offroad_legends_imp_filepath = self.filepath
bpy.types.Scene.offroad_legends_imp_directory = self.directory
bpy.types.Scene.offroad_legends_imp_my_float1 = self.my_float1
bpy.types.Scene.offroad_legends_imp_my_bool1 = self.my_bool1
# Run Callback
offroad_legends_imp_callback(self.directory + "\\", self.files, self.my_bool1, self.my_float1)
return {"FINISHED"}
# Window Settings
def draw(self, context):
self.layout.row().label(text="Import Settings")
self.layout.separator()
self.layout.row().prop(self, "my_bool1")
self.layout.row().prop(self, "my_float1")
self.layout.separator()
col = self.layout.row()
col.alignment = 'RIGHT'
col.label(text=" Author:", icon='QUESTION')
col.alignment = 'LEFT'
col.label(text="mariokart64n")
col = self.layout.row()
col.alignment = 'RIGHT'
col.label(text="Release:", icon='GRIP')
col.alignment = 'LEFT'
col.label(text="March 16, 2021")
def menu_func_import(self, context):
self.layout.operator("importhelper.offroad_legends_imp", text="Off Road Legends (*.geo)")
# Register Operator
bpy.utils.register_class(ImportHelper_offroad_legends_imp)
bpy.types.TOPBAR_MT_file_import.append(ImportHelper_offroad_legends_imp.menu_func_import)
# Call ImportHelper
bpy.ops.importhelper.offroad_legends_imp('INVOKE_DEFAULT')
if not useOpenDialog:
deleteScene(['MESH', 'ARMATURE']) # Clear Scene
clearListener() # clears out console
read(
"C:\\Users\\Corey\\Downloads\\Cars The Video Game\\OL_Brokencars\\BrokenCars\\jeep\\chassis.geo"
)
messageBox("Done!")
else:
offroad_legends_imp(True)