It loads mesh data (vertex, face, normals and UVs) from a *.fed model.
fmt_recolove_fed.py:
Code: Select all
# v2: Fixed face direction and UV
import math
from inc_noesis import *
def registerNoesisTypes():
""" register plugin
"""
handle = noesis.register("Recolove Model", ".fed")
noesis.setHandlerTypeCheck(handle, fedCheckType)
noesis.setHandlerLoadModel(handle, fedLoadModel)
# noesis.logPopup() # show console
return 1
def fedCheckType(data):
""" check header
"""
FED_HEADER = 0x43415046 # FPAC
bs = NoeBitStream(data)
bs.seek(0)
if bs.readInt() != FED_HEADER:
return 0
return 1
def fedLoadModel(data, mdlList):
""" load model (mesh and UV)
"""
bs = NoeBitStream(data)
meshes = []
myMeshes = []
# find face sets. eg. (0, 1, 2)
bs.seek(0)
while not bs.checkEOF():
firstFace = (bs.readUShort(), bs.readUShort(), bs.readUShort())
tailVals = (bs.readUShort(), bs.readUShort(), bs.readUShort(),
bs.readUShort(), bs.readUShort())
if (firstFace == (0, 1, 2) and sum(tailVals) < 30):
myMeshes.append({"faceOffset": bs.tell() - 16})
if len(myMeshes) == 0:
print("no face sets.")
return 0
# find face counts
for myMesh in myMeshes:
isFound = False
ofs = myMesh["faceOffset"]
while True:
ofs -= 16 # go up
if isFound:
break
if ofs < 0:
print("face count not found.")
return 0
bs.seek(ofs)
numFaceX3 = bs.readUShort()
bs.readUShort() # unknown
magic1C = bs.readInt()
magicZero1 = bs.readInt64()
numVert = bs.readInt()
magicZero2 = bs.readInt()
magicZero3 = bs.readInt64()
if (numFaceX3 % 3 == 0 and magic1C == 0x1C and magicZero1 == 0 and magicZero2 == 0 and magicZero3 == 0):
isFound = True
myMesh["numFace"] = numFaceX3 // 3
myMesh["numVert"] = numVert
myMesh["vertOffset"] = bs.tell()
for myMesh in myMeshes:
print(myMesh)
verts = []
bs.seek(myMesh["vertOffset"])
for i in range(myMesh["numVert"]):
x = _getFloat(bs)
y = _getFloat(bs)
z = _getFloat(bs)
verts.append(NoeVec3((x, y, z)))
_padding(bs)
normals = []
for i in range(myMesh["numVert"]):
x = _getFloat(bs)
y = _getFloat(bs)
z = _getFloat(bs)
normals.append(NoeVec3((x, y, z)))
_padding(bs)
if bs.readBytes(4)[3] == 0xFF: # Skip, may be weights
bs.seek(-4, NOESEEK_REL)
bs.readBytes(myMesh["numVert"] * 4)
_padding(bs)
uvs = []
for i in range(myMesh["numVert"]):
u = _getFloat(bs)
v = _getFloat(bs) + 1.0
uvs.append(NoeVec3((u, v, 0.0)))
_padding(bs)
if bs.tell() != myMesh["faceOffset"]:
print(" SKIP: UV exceeds faceOffset: %d" % bs.tell())
continue
idxList = []
for i in range(myMesh["numFace"]):
indices = (bs.readUShort(), bs.readUShort(), bs.readUShort())
idxList.append(indices[2])
idxList.append(indices[1])
idxList.append(indices[0])
_padding(bs)
meshName = "mesh_%08X" % myMesh["vertOffset"]
mesh = NoeMesh(idxList, verts, meshName)
mesh.setNormals(normals)
mesh.setUVs(uvs)
meshes.append(mesh)
mdlList.append(NoeModel(meshes))
return 1
def _getFloat(bs: NoeBitStream):
""" get float value, skipping NaN
"""
val = bs.readFloat()
if not math.isnan(val):
return val
else:
return bs.readFloat()
def _padding(bs: NoeBitStream):
""" padding by 16 bytes
"""
mod = bs.tell() % 16
if mod != 0:
bs.readBytes(16 - mod)
You will also need to use the following tools.
1. CPK Unpacker (to obtain fed files):
https://github.com/esperknight/CriPakTo ... ster/Build
2. QuickBMS and this script (to extract *.tex files):
http://aluigi.altervista.org/bms/fpactex.bms
3. Texture Unpacker (to convert textures):
https://github.com/xdanieldzd/GXTConvert/releases