Gravity Rush 2 - Epichao - 06-26-2018
Hey, guys! So with PS4 games starting to get some exposure, I've decided to start ripping models from Gravity Rush 2. I've had some help over on Xentax, and I've managed to write a Python script that does a pretty good job with Kat/Kitten's many outfits:
(Unfortunately, I don't have the files for the 2B outfit.)
The script works for Raven/Crow too, but I think most models are quite different from the playable characters.
kit00.gfx-kit21.gfx for Kat
cro00.gfx-cro03.gfx for Raven
The script works with Model Researcher Pro. I want to fine-tune it so that there aren't any errors or missing faces, but I also want to try to get the bones, which are in separate files (.mot) and must be assigned weights when the models are loaded in-game. The only problem with that is that I'm not entirely sure how the bone data looks in a hex file, or if I can use Model Researcher to visualize them. I may have to write a Blender script for this sooner than I'd hoped, haha! If you guys know a thing or two about ripping bones and weights, please do tell.
Code: import mrp
import random
file = mrp.get_bfile()
vertStart = 0
subMeshes = []
#we gotta create the meshes.
#this should be done as we collect
#the vertices, faces, and uvs.
fileComplete = False
meshCounter = 1
#there are some instances where
#vertex blocks are 36 bytes, not 32.
#vertexType will tell me
#what size I'm processing.
vertexType = 32
while True:
file.seek(30,1)
checkedByte = file.readHalfFloat()
if checkedByte == 1.0 or checkedByte == -1.0:
file.seek(-32,1)
vertStart = file.tell()
break
while fileComplete == False:
print(" ")
print("Begin Mesh" + str(meshCounter).zfill(2))
print("vertexType: " + str(vertexType))
mesh = mrp.create_mesh("Mesh" + str(meshCounter).zfill(2))
verts = []
faces = []
uvs = []
dp = 0x400 #(1024)
meshComplete = False
#set vertex and uv data simultaneously
file.seek(vertStart)
vertCount = 0
print("Vertices: " + file.tellHex())
while meshComplete == False:
#first, confirm that this is a vertex
file.seek(vertexType - 1,1)
checkedByte = file.readByte()
file.seek(-vertexType,1)
file.seek(16,1)
byte40 = file.readShort()
file.seek(20,1)
altByte40 = file.readHalfFloat()
file.seek(-40,1)
if vertexType == 40 and byte40 != 65535 and altByte40 != 0.0:
meshComplete = True
continue
elif vertexType != 40 and checkedByte != 60 and checkedByte != 188:
meshComplete = True
continue
#read vertex
v1,v2,v3 = file.read3Float()
verts.append((v1,v2,v3))
file.seek(8,1) #seek to uv data
u1,u2 = file.read2short()
u1 /= dp
u2 /= dp
uvs.append((u1,-u2))
file.seek(vertexType - 24,1) #seek next vertex
vertCount = vertCount + 1
#end vertex & uv data
print(file.tellHex() + " Count: " + str(vertCount))
padding = True
#loop to reach face data
while padding == True:
#print(file.tellHex())
checkedByte = file.readByte()
if checkedByte != 0:
file.seek(-1,1)
padding = False
#while file.tell() % 32 != 0:
#file.seek(-1,1) #let's hope this doesn't corrupt good face data
#end loop
#now we need to confirm tht we're
#starting at the right place.
while file.tell() % 32 != 0:
file.seek(-1,1)
meshComplete = False
print("Faces: " + file.tellHex())
#face data
#peek 31 (0x1f) bytes ahead.
#if the block is not the end of a vertex,
#step back 31 bytes and read 16 times.
showData = True
finalSubFaces = []
subMeshStart40 = False
simpleSubMesh = False
while meshComplete == False:
#if meshCounter == 78:
#print("Squad")
subMeshStart = False
foundVertex = False
file.seek(30,1)
checkedByte = file.readHalfFloat()
file.seek(-32,1)
file.seek(34,1)
altByte36 = file.readHalfFloat()
file.seek(-36,1)
file.seek(3,1)
vertCheck1 = file.readByte()
file.seek(3,1)
vertCheck2 = file.readByte()
file.seek(3,1)
vertCheck3 = file.readByte()
file.seek(2,1) #14 bytes from start
checkedByte2 = file.readShort()
byte40 = file.readShort()
file.seek(20,1)
altByte40 = file.readHalfFloat()
file.seek(-40,1)
file.seek(17,1)
minorByte28 = file.readByte()
file.seek(8,1) #26 bytes from start
altByte28 = file.readHalfFloat()
file.seek(-28,1)
file.seek(1,1)
faceConfirm1 = file.readByte()
file.seek(1,1)
faceConfirm2 = file.readByte()
file.seek(1,1)
faceConfirm3 = file.readByte()
file.seek(-6,1)
faceConfirmFinal = False
subMeshStartFace = 0
if faceConfirm1 == 0x3c:
if faceConfirm1 == faceConfirm2 and faceConfirm2 == faceConfirm3:
faceConfirmFinal = True
if faceConfirmFinal == False and (file.tell() % 32 == 0) and vertCheck1 >= 0x00 and vertCheck1 <= 0xbf and vertCheck2 >= 0x3b and vertCheck2 <= 0xbf and vertCheck3 >= 0x3b and vertCheck3 <= 0xbf:
if byte40 == 65535 and altByte40 == 0.0:
foundVertex = True
vertexType = 40
#print("vertexType: " + str(vertexType))
elif (altByte36 == 1.0 or altByte36 == -1.0) and (byte40 >= 65278 and byte40 <= 65535 or byte40 == 255):
foundVertex = True
vertexType = 36
#print("vertexType: " + str(vertexType))
elif (altByte28 == 1.0 or altByte28 == -1.0) and (minorByte28 >= 1 and minorByte28 <= 3):
foundVertex = True
vertexType = 28
print("vertexType: " + str(vertexType))
elif (checkedByte == 1.0 or checkedByte == -1.0) and (byte40 >= 255 and byte40 <= 65535 or byte40 == 12): #previously 65278 - 65535
foundVertex = True
vertexType = 32
#print("vertexType: " + str(vertexType))
elif checkedByte2 == 32768 and checkedByte == 0.0 or checkedByte2 == 32768 and checkedByte == 1.875:
#end of compatible mesh data
fileComplete = True
elif subMeshStart40 == True:
#this will be used to make
#subMeshStart true.
subMeshes.append(file.tell())
subMeshStart40 = False
elif simpleSubMesh == True:
subMeshes.append(file.tell())
simpleSubMesh = False
for f in subMeshes:
if file.tell() == f:
subMeshStart = True
subMeshStartFace = f
if subMeshStart == True:
subMeshes.remove(subMeshStartFace)
if subMeshStart == False and foundVertex == False and fileComplete == False:
for i in range(1):
f1,f2,f3 = file.read3Short()
if f1 == 0 and f2 == 0 and f3 == 0:
padding = True
padCount = 0
while padding == True:
#skip 0-bytes
checkedByte = file.readByte()
if checkedByte != 0:
file.seek(-1,1)
padding = False
elif file.tell() % 32 == 0:
#if the first vertex is 0, I don't need to do a full
#type32-vertex check. Instead, I can simply check
#the last half-float. That's enough info to continue the loop.
file.seek(30,1)
checkedByte = file.readHalfFloat()
file.seek(-32,1)
if (checkedByte == 1.0 or checkedByte == -1.0):
#print("possible vert at" + file.tellHex())
break
else:
padCount = padCount + 1
if padCount == 32 - 8:
simpleSubMesh = True
#print(file.tellHex())
if vertexType == 40: #we've found a lowpoly model. submeshes are separated by zeros.
subMeshStart40 = True
#if file.tell() % 32 == 0:
#simpleSubMesh = True
break
elif f1 == 0 and f2 == 0 and file.tell() % 32 != 0:
file.seek(-2,1)
break
faces.append((f1,f2,f3))
#if showData == True and meshCounter == 42:
#print(str(f1) + ", " + str(f2) + ", " + str(f3))
#print(file.tellHex())
#print(i)
else:
meshComplete = True
vertStart = file.tell()
if fileComplete == True:
print("File Complete at Mesh" + str(meshCounter).zfill(2))
elif subMeshStart == True:
#we've encountered a submesh!
meshComplete = False
faceCount = int((len(faces)) / 3)
for i in range(faceCount):
finalSubFaces.append(faces[i])
faces = []
print("Count: " + str(faceCount) + "- Submesh at " + file.tellHex())
#end face data
#make sure to use only the first third of the faces array.
#considering the values involved,
#you divide by 18, not 3
finalFaces = finalSubFaces[:]
faceCount = int((len(faces)) / 3)
for i in range(faceCount):
finalFaces.append(faces[i])
print(file.tellHex() + " Count: " + str(faceCount))
#material
material = mrp.create_material("Material" + str(meshCounter).zfill(2))
material.set_color(random.randint(0, 256), random.randint(0, 256), random.randint(0, 256))
material.set_texture("C:\\Users\\freez\\Pictures\\GR2\\kit01_face_00.bmp", "RGB", True)
#end material
mesh.set_vertices(verts,"ZYX","z","Float")
mesh.set_faces(finalFaces, tp="Short")
mesh.set_uvs(uvs)
mesh.set_material(material)
mrp.view_uvs("Mesh" + str(meshCounter).zfill(2))
mrp.render("Mesh" + str(meshCounter).zfill(2))
#mrp.print_mesh("Mesh" + str(meshCounter).zfill(2))
print("Mesh" + str(meshCounter).zfill(2) + " complete")
meshCounter = meshCounter + 1
#failsafe for infinite loops
if meshCounter == 90:
fileComplete = True
mrp.render("All")
RE: Gravity Rush 2 - Lazov - 07-02-2018
If you get the animation, please let me know. I plan to add to the program to support skeletal animation.
RE: Gravity Rush 2 - Epichao - 07-03-2018
(07-02-2018, 05:45 AM)Lazov Wrote: If you get the animation, please let me know. I plan to add to the program to support skeletal animation.
Alright. I'll keep you posted on any developments.
|