Noesis tutorial Basic Model
Posted: Mon Nov 28, 2011 3:07 am
First thing is a quote from Señor Casaroja.
Ok so noesis now supports python scripting to import and export 3d models.
There are several great reasons to use noesis over other methods.
1.Tons of supported functions are already ready for us and do not need to be added like xbox and ps2 images.
2.No need to compile scripts can be tested in real time.
3.Very fast scripting language.
http://oasis.xentax.com/index.php?content=downloads
So the first thing you will want is a file format to add to noesis.
In our example we will use [X360]Strike Witches Shirogane no Tsubasa
First thing is use quickbms to extract our files the script is here for this game
viewtopic.php?f=16&t=7751
We end up with a bunch of files the files we care about are
player01.bin the file to extract
the texture files .dds
and the model files .gmo
So we will look at CH01_TOP.GMO
So first thing we need is a bare minimum noesis script
So lets look at what we have here
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
This line tells noesis what file type to use this script with
The first part is a text line that tells the user the name of the game "Strike Witches Shirogane no Tsubasa"
the second part is the actual file type of the format. ".gmo"
The next part
NOEPY_HEADER = "GXXM0124"
This is what this file format always starts out with and is very important to make sure noesis loads the correct script with our format.
We use this here
so in our example file the header is always
GXXM0124 with padding 00's to make it 16 bytes
so in noesis we did 2 checks
the first if len(data) < 16:
return 0
this says if our file is < 16 bytes it cant be a valid file
The next thing we do is declare a file stream
bs = NoeBitStream(data)
now with our file stream open we read 16 bytes as a string and see if it matches our header
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
so then if both of these are true we return a 1 at the end and noesis will continue on to the next part of our script.
def noepyLoadModel(data, mdlList):
this says start the model loading process
then we need to create a context for our model
ctx = rapi.rpgCreateContext()
the next command clears out all buffers this is good to add at the end of your script to free up memory it also prevents noesis from throwing an error when no model is loaded
rapi.rpgClearBufferBinds()
then finally we end our code with a success command
return 1
now if you run this code you should get this when you open the model
Now we want to start reading our format so
the first interesting part of our data is at 0x1C
So we see to that offset
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
Here i see 3 values i want to read as little endian values. "normally 360 games are big endian games this game was just written so bad they dint bother to change it from pc format"
hdrInfo = bs.read("iii")
if this was big endian all you have to do in python is change it to this
hdrInfo = bs.read(">iii")
the i stands for integer in python this is a great reference table for the possible values
http://docs.python.org/library/struct.html
http://www.tutorialspoint.com/python/py ... rators.htm
this command stored 3 little endian ints into the array hdrInfo
so if we do
print(hdrInfo)
we get this in our output window
(1, 23, 1)
which is perfect as the data we read looked like this
the next piece of information we want is at offset 0x34
so ill seek to that offset but this time i will seek relative to my current position 0x28
bs.seek(0xC, NOESEEK_REL) #Seek past junk
This next value is the vertex count so
VCount = bs.read("i")
then we skip 4 bytes
bs.seek(0x4, NOESEEK_REL) #Seek past junk
and read the face count
FCount = bs.read("i")
OK that's all we really need from the header so now we seek to the start of the data.
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
ok so here remember we read those 3 values into our array we will use them now those values represented
Texture Count 1
Bone Count 23
Material Count 1
so we make a loop to read our texture name(s)
TexNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
and we will print it to verify it looks right
print(TexNames)
So now our output looks like this
(1, 23, 1)
['ch01_mm.tga']
Perfect.
So your script should be like this now.
Ok next come the bones
The bone format is 32 bytes bone name and 0x40 bytes bone data
so for now we will just import the name and worry about the data later
BoneNames = []
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
and the output from the print command is
So next is the material section
this section's size in total is 0x130
and is broken down by
0xC0 -stuff
0x28 -Material Name
0x48 -stuff
so for now we will once again read in the name
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
Ok so now we are at the vertex data
0xA68
each vertex is 0x58 in size
so noesis has a very nice way to get this data for us.
VertBuff = bs.readBytes(VCount[0] * 0x58)
so now that we read all that data into a buffer we can parse that buffer
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
this says read 3 flaots at position 0 in the VertBuff and that each vertex is 88 bytes
Now we can test this quickly with these lines here
rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
mdl = rapi.rpgConstructModel()
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
So now we have this
and here is our code so far
so now that we know our verts are good we can try to load the faces so
comment out
#rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
and put
FaceBuff = bs.readBytes(FCount[0] * 2)
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FCount[0], noesis.RPGEO_TRIANGLE, 1)
you might notice the model looks transparent without turning off face culling if thats the case just add this line
rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
So now you have
and the code
Ok so now we will add in our normals and uv coordinates.
rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 12)
rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 48)
so now that we have our uv's all set we want to test them so lets make a material and assign the texture we think is needed to it.
so we need 2 arrays
texList = []
matList = []
Then we create our material with a diffuse texture
rapi.rpgSetMaterial("CH01_MM")
material = NoeMaterial("CH01_MM", "")
material.setTexture("CH01_MM.DDS")
matList.append(material)
and after we construct the model we pass
mdl.setModelMaterials(NoeModelMaterials(texList, matList))
So now we should get this result
and this should be your code so far
Now in the model CH01_TOP.GMO there is only one material and one texture but in CH01_UNDER.GMO There are 3 textures and 3 materials.
So in order to fix this we need to find out more about our materials.
I have found the relevant information in the material and here is the structure
Structure Material {
Long -Texture ID
0xBC -unkown / not needed "its probably some color information about the material"
Material Name 0x20
0x10 -unkown / not needed
Long - Material Start Indecie
Long - Number of indecies in the material
0x38 -unkown / not needed
}
So now that we know How our material is made up lets get that information stored for use.
we will change
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
into
MaterialInfo = []
for i in range(0, hdrInfo[2]):
TexID = bs.read("i")
bs.seek(0xBC, NOESEEK_REL) #Seek past Material stuff
MaterialName = (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x10, NOESEEK_REL) #Seek past Material stuff
IndStart = bs.read("i")
IndCount = bs.read("i")
bs.seek(0x38, NOESEEK_REL) #Seek past Material stuff
MaterialInfo.append([MaterialName, TexID[0], IndStart[0], IndCount[0]])
print(MaterialInfo)
Now we will add a loop to create our materials with the texture assigned
for i in range(0, hdrInfo[2]):
material = NoeMaterial(MaterialInfo[0],"")
material.setTexture(TexNames[MaterialInfo[1]] + ".dds")
matList.append(material)
print(matList)
Now we need to have our faces load with the correct material so we use this
for i in range(0, hdrInfo[2]):
FaceBuff = bs.readBytes(MaterialInfo[3] * 2)
rapi.rpgSetMaterial(MaterialInfo[0]) #call this before before commit triangles it assignes this set of triangles to the material name given
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, MaterialInfo[3], noesis.RPGEO_TRIANGLE, 1)
So now both models load with the correct textures and our code should look like this.
Code: Select all
Yo bebo cuando tengo ocasión, ya veces cuando no tengo ocasión.
There are several great reasons to use noesis over other methods.
1.Tons of supported functions are already ready for us and do not need to be added like xbox and ps2 images.
2.No need to compile scripts can be tested in real time.
3.Very fast scripting language.
http://oasis.xentax.com/index.php?content=downloads
So the first thing you will want is a file format to add to noesis.
In our example we will use [X360]Strike Witches Shirogane no Tsubasa
First thing is use quickbms to extract our files the script is here for this game
viewtopic.php?f=16&t=7751
We end up with a bunch of files the files we care about are
player01.bin the file to extract
the texture files .dds
and the model files .gmo
So we will look at CH01_TOP.GMO
So first thing we need is a bare minimum noesis script
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
bs = NoeBitStream(data)
rapi.rpgClearBufferBinds()
return 1
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
This line tells noesis what file type to use this script with
The first part is a text line that tells the user the name of the game "Strike Witches Shirogane no Tsubasa"
the second part is the actual file type of the format. ".gmo"
The next part
NOEPY_HEADER = "GXXM0124"
This is what this file format always starts out with and is very important to make sure noesis loads the correct script with our format.
We use this here
Code: Select all
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
GXXM0124 with padding 00's to make it 16 bytes
so in noesis we did 2 checks
the first if len(data) < 16:
return 0
this says if our file is < 16 bytes it cant be a valid file
The next thing we do is declare a file stream
bs = NoeBitStream(data)
now with our file stream open we read 16 bytes as a string and see if it matches our header
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
so then if both of these are true we return a 1 at the end and noesis will continue on to the next part of our script.
def noepyLoadModel(data, mdlList):
this says start the model loading process
then we need to create a context for our model
ctx = rapi.rpgCreateContext()
the next command clears out all buffers this is good to add at the end of your script to free up memory it also prevents noesis from throwing an error when no model is loaded
rapi.rpgClearBufferBinds()
then finally we end our code with a success command
return 1
now if you run this code you should get this when you open the model
Now we want to start reading our format so
the first interesting part of our data is at 0x1C
So we see to that offset
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
Here i see 3 values i want to read as little endian values. "normally 360 games are big endian games this game was just written so bad they dint bother to change it from pc format"
hdrInfo = bs.read("iii")
if this was big endian all you have to do in python is change it to this
hdrInfo = bs.read(">iii")
the i stands for integer in python this is a great reference table for the possible values
http://docs.python.org/library/struct.html
http://www.tutorialspoint.com/python/py ... rators.htm
this command stored 3 little endian ints into the array hdrInfo
so if we do
print(hdrInfo)
we get this in our output window
(1, 23, 1)
which is perfect as the data we read looked like this
Code: Select all
01 00 00 00 17 00 00 00 01 00 00 00
so ill seek to that offset but this time i will seek relative to my current position 0x28
bs.seek(0xC, NOESEEK_REL) #Seek past junk
This next value is the vertex count so
VCount = bs.read("i")
then we skip 4 bytes
bs.seek(0x4, NOESEEK_REL) #Seek past junk
and read the face count
FCount = bs.read("i")
OK that's all we really need from the header so now we seek to the start of the data.
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
ok so here remember we read those 3 values into our array we will use them now those values represented
Texture Count 1
Bone Count 23
Material Count 1
so we make a loop to read our texture name(s)
TexNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
and we will print it to verify it looks right
print(TexNames)
So now our output looks like this
(1, 23, 1)
['ch01_mm.tga']
Perfect.
So your script should be like this now.
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
bs = NoeBitStream(data)
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
hdrInfo = bs.read("iii")
print(hdrInfo)
bs.seek(0xC, NOESEEK_REL) #Seek past junk
VCount = bs.read("i")
bs.seek(0x4, NOESEEK_REL) #Seek past junk
FCount = bs.read("i")
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
TexNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
print(TexNames)
rapi.rpgClearBufferBinds()
return 1
The bone format is 32 bytes bone name and 0x40 bytes bone data
so for now we will just import the name and worry about the data later
BoneNames = []
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
and the output from the print command is
Code: Select all
['ch01_top_BackCloth', 'ch01_top_BackHair', 'ch01_top_BackHair2', 'ch01_top_FrontCloth', 'ch01_top_FrontHair', 'ch01_top_Gun', 'ch01_top_Head', 'ch01_top_LeftArm', 'ch01_top_LeftEar', 'ch01_top_LeftFinger', 'ch01_top_LeftForeArm', 'ch01_top_LeftHand', 'ch01_top_LeftShoulder', 'ch01_top_RightArm', 'ch01_top_RightEar', 'ch01_top_RightFinger', 'ch01_top_RightForeArm', 'ch01_top_RightHand', 'ch01_top_RightShoulder', 'ch01_top_Spine', 'ch01_top_Spine1', 'ch01_top_Top', 'ch01_top_neck']
this section's size in total is 0x130
and is broken down by
0xC0 -stuff
0x28 -Material Name
0x48 -stuff
so for now we will once again read in the name
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
Ok so now we are at the vertex data
0xA68
each vertex is 0x58 in size
so noesis has a very nice way to get this data for us.
VertBuff = bs.readBytes(VCount[0] * 0x58)
so now that we read all that data into a buffer we can parse that buffer
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
this says read 3 flaots at position 0 in the VertBuff and that each vertex is 88 bytes
Now we can test this quickly with these lines here
rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
mdl = rapi.rpgConstructModel()
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
So now we have this
and here is our code so far
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
bs = NoeBitStream(data)
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
hdrInfo = bs.read("iii")
print(hdrInfo)
bs.seek(0xC, NOESEEK_REL) #Seek past junk
VCount = bs.read("i")
bs.seek(0x4, NOESEEK_REL) #Seek past junk
FCount = bs.read("i")
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
TexNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
print(TexNames)
BoneNames = []
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
VertBuff = bs.readBytes(VCount[0] * 0x58)
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
mdl = rapi.rpgConstructModel()
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
rapi.rpgClearBufferBinds()
return 1
comment out
#rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
and put
FaceBuff = bs.readBytes(FCount[0] * 2)
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FCount[0], noesis.RPGEO_TRIANGLE, 1)
you might notice the model looks transparent without turning off face culling if thats the case just add this line
rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
So now you have
and the code
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
bs = NoeBitStream(data)
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
hdrInfo = bs.read("iii")
print(hdrInfo)
bs.seek(0xC, NOESEEK_REL) #Seek past junk
VCount = bs.read("i")
bs.seek(0x4, NOESEEK_REL) #Seek past junk
FCount = bs.read("i")
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
TexNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
print(TexNames)
BoneNames = []
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
VertBuff = bs.readBytes(VCount[0] * 0x58)
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
#rapi.rpgCommitTriangles(None, noesis.RPGEODATA_USHORT, VCount[0], noesis.RPGEO_POINTS, 1)
FaceBuff = bs.readBytes(FCount[0] * 2)
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FCount[0], noesis.RPGEO_TRIANGLE, 1)
mdl = rapi.rpgConstructModel()
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
rapi.rpgClearBufferBinds()
return 1
rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 12)
rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 48)
so now that we have our uv's all set we want to test them so lets make a material and assign the texture we think is needed to it.
so we need 2 arrays
texList = []
matList = []
Then we create our material with a diffuse texture
rapi.rpgSetMaterial("CH01_MM")
material = NoeMaterial("CH01_MM", "")
material.setTexture("CH01_MM.DDS")
matList.append(material)
and after we construct the model we pass
mdl.setModelMaterials(NoeModelMaterials(texList, matList))
So now we should get this result
and this should be your code so far
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
bs = NoeBitStream(data)
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
hdrInfo = bs.read("iii")
print(hdrInfo)
bs.seek(0xC, NOESEEK_REL) #Seek past junk
VCount = bs.read("i")
bs.seek(0x4, NOESEEK_REL) #Seek past junk
FCount = bs.read("i")
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
texList = []
matList = []
TexNames = []
BoneNames = []
MaterialNames = []
for i in range(0, hdrInfo[0]):
TexNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
print(TexNames)
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
mname = "CH01_MM"
material = NoeMaterial("CH01_MM", "")
material.setTexture("CH01_MM.DDS")
matList.append(material)
VertBuff = bs.readBytes(VCount[0] * 0x58)
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 12)
rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 48)
FaceBuff = bs.readBytes(FCount[0] * 2)
rapi.rpgSetMaterial("CH01_MM")
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, FCount[0], noesis.RPGEO_TRIANGLE, 1)
mdl = rapi.rpgConstructModel()
mdl.setModelMaterials(NoeModelMaterials(texList, matList))
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
rapi.rpgClearBufferBinds()
return 1
So in order to fix this we need to find out more about our materials.
I have found the relevant information in the material and here is the structure
Structure Material {
Long -Texture ID
0xBC -unkown / not needed "its probably some color information about the material"
Material Name 0x20
0x10 -unkown / not needed
Long - Material Start Indecie
Long - Number of indecies in the material
0x38 -unkown / not needed
}
So now that we know How our material is made up lets get that information stored for use.
we will change
MaterialNames = []
for i in range(0, hdrInfo[2]):
bs.seek(0xC0, NOESEEK_REL) #Seek past Material stuff
MaterialNames.append (bs.readBytes(40).decode("ASCII").rstrip("\0"))
bs.seek(0x48, NOESEEK_REL) #Seek past Material stuff
print(MaterialNames)
into
MaterialInfo = []
for i in range(0, hdrInfo[2]):
TexID = bs.read("i")
bs.seek(0xBC, NOESEEK_REL) #Seek past Material stuff
MaterialName = (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x10, NOESEEK_REL) #Seek past Material stuff
IndStart = bs.read("i")
IndCount = bs.read("i")
bs.seek(0x38, NOESEEK_REL) #Seek past Material stuff
MaterialInfo.append([MaterialName, TexID[0], IndStart[0], IndCount[0]])
print(MaterialInfo)
Now we will add a loop to create our materials with the texture assigned
for i in range(0, hdrInfo[2]):
material = NoeMaterial(MaterialInfo[0],"")
material.setTexture(TexNames[MaterialInfo[1]] + ".dds")
matList.append(material)
print(matList)
Now we need to have our faces load with the correct material so we use this
for i in range(0, hdrInfo[2]):
FaceBuff = bs.readBytes(MaterialInfo[3] * 2)
rapi.rpgSetMaterial(MaterialInfo[0]) #call this before before commit triangles it assignes this set of triangles to the material name given
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, MaterialInfo[3], noesis.RPGEO_TRIANGLE, 1)
So now both models load with the correct textures and our code should look like this.
Code: Select all
#Noesis Python model import+export test module, imports/exports some data from/to a made-up format
from inc_noesis import *
import noesis
#rapi methods should only be used during handler callbacks
import rapi
#registerNoesisTypes is called by Noesis to allow the script to register formats.
#Do not implement this function in script files unless you want them to be dedicated format modules!
def registerNoesisTypes():
handle = noesis.register("Strike Witches Shirogane no Tsubasa", ".gmo")
noesis.setHandlerTypeCheck(handle, noepyCheckType)
noesis.setHandlerLoadModel(handle, noepyLoadModel) #see also noepyLoadModelRPG
#noesis.setHandlerWriteModel(handle, noepyWriteModel)
#noesis.setHandlerWriteAnim(handle, noepyWriteAnim)
noesis.logPopup()
#print("The log can be useful for catching debug prints from preview loads.\nBut don't leave it on when you release your script, or it will probably annoy people.")
return 1
NOEPY_HEADER = "GXXM0124"
#check if it's this type based on the data
def noepyCheckType(data):
bs = NoeBitStream(data)
if len(data) < 16:
return 0
if bs.readBytes(16).decode("ASCII").rstrip("\0") != NOEPY_HEADER:
return 0
return 1
#load the model
def noepyLoadModel(data, mdlList):
ctx = rapi.rpgCreateContext()
rapi.rpgSetOption(noesis.RPGOPT_TRIWINDBACKWARD, 1)
bs = NoeBitStream(data)
bs.seek(0x1C, NOESEEK_ABS) #Seek to header info
hdrInfo = bs.read("iii")
print(hdrInfo)
bs.seek(0xC, NOESEEK_REL) #Seek past junk
VCount = bs.read("i")
bs.seek(0x4, NOESEEK_REL) #Seek past junk
FCount = bs.read("i")
bs.seek(0x70, NOESEEK_ABS) #Seek to File Start
texList = []
matList = []
TexNames = []
BoneNames = []
MaterialInfo = []
for i in range(0, hdrInfo[0]):
TexNames.append (rapi.getExtensionlessName(bs.readBytes(40).decode("ASCII").rstrip("\0")))
print(TexNames)
for i in range(0, hdrInfo[1]):
BoneNames.append (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x40, NOESEEK_REL) #Seek past bone stuff
print(BoneNames)
for i in range(0, hdrInfo[2]):
TexID = bs.read("i")
bs.seek(0xBC, NOESEEK_REL) #Seek past Material stuff
MaterialName = (bs.readBytes(32).decode("ASCII").rstrip("\0"))
bs.seek(0x10, NOESEEK_REL) #Seek past Material stuff
IndStart = bs.read("i")
IndCount = bs.read("i")
bs.seek(0x38, NOESEEK_REL) #Seek past Material stuff
MaterialInfo.append([MaterialName, TexID[0], IndStart[0], IndCount[0]])
print(MaterialInfo)
for i in range(0, hdrInfo[2]):
material = NoeMaterial(MaterialInfo[i][0],"")
material.setTexture(TexNames[MaterialInfo[i][1]] + ".dds")
matList.append(material)
print(matList)
VertBuff = bs.readBytes(VCount[0] * 0x58)
rapi.rpgBindPositionBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 0)
rapi.rpgBindNormalBufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 12)
rapi.rpgBindUV1BufferOfs(VertBuff, noesis.RPGEODATA_FLOAT, 88, 48)
for i in range(0, hdrInfo[2]):
FaceBuff = bs.readBytes(MaterialInfo[i][3] * 2)
rapi.rpgSetMaterial(MaterialInfo[i][0])
rapi.rpgCommitTriangles(FaceBuff, noesis.RPGEODATA_USHORT, MaterialInfo[i][3], noesis.RPGEO_TRIANGLE, 1)
mdl = rapi.rpgConstructModel()
mdl.setModelMaterials(NoeModelMaterials(texList, matList))
mdlList.append(mdl) #important, don't forget to put your loaded model in the mdlList
rapi.rpgClearBufferBinds()
return 1