Important information: this site is currently scheduled to go offline indefinitely by end of the year.

Noesis tutorial Basic Model

Read or post any tutorial related to file format analysis for modding purposes.
chrrox
Moderator
Posts: 2602
Joined: Sun May 18, 2008 3:01 pm
Has thanked: 57 times
Been thanked: 1422 times

Noesis tutorial Basic Model

Post by chrrox »

First thing is a quote from Señor Casaroja.

Code: Select all

Yo bebo cuando tengo ocasión, ya veces cuando no tengo ocasión.
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

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
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

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 
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
Image

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
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.

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
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

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']
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
Image
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
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
Image
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
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
Image
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
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

#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
You do not have the required permissions to view the files attached to this post.
Mr.Mouse
Site Admin
Posts: 4073
Joined: Wed Jan 15, 2003 6:45 pm
Location: Dungeons of Doom
Has thanked: 450 times
Been thanked: 682 times
Contact:

Re: Noesis tutorial (WIP) Updated 11/27/2011

Post by Mr.Mouse »

Yo bebo cuando tengo ocasión, ya veces cuando no tengo ocasión. :D

Great work, Chrrox! :drunken:
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Noesis tutorial (WIP) Updated 11/27/2011

Post by MrAdults »

Mr.Mouse wrote:Great work, Chrrox! :drunken:
Indeed.
LUBDAR
veteran
Posts: 95
Joined: Wed Jun 08, 2011 7:14 am
Has thanked: 18 times
Been thanked: 9 times

Re: Noesis tutorial (WIP) Updated 11/29/2011

Post by LUBDAR »

chrrox wrote: There are several great reasons to use noesis over other methods.

Thanks chrrox, this is just...so so awesome!
alon
mega-veteran
mega-veteran
Posts: 163
Joined: Mon Nov 29, 2010 10:38 am
Has thanked: 32 times
Been thanked: 2 times

Re: Noesis tutorial (WIP) Updated 11/29/2011

Post by alon »

Thanks chrrox
I'm not completely sure, but sucess.
Last edited by alon on Sun Apr 15, 2012 1:43 pm, edited 1 time in total.
pixellegolas
ultra-veteran
ultra-veteran
Posts: 423
Joined: Mon Aug 11, 2008 11:30 pm
Has thanked: 27 times
Been thanked: 15 times

Re: Noesis tutorial (WIP) Updated 11/29/2011

Post by pixellegolas »

Nice one man! Hopefully will unlock alot of more importers etc ;)
finale00
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2382
Joined: Sat Apr 09, 2011 1:22 am
Has thanked: 170 times
Been thanked: 307 times

Re: Noesis tutorial (WIP) Updated 11/29/2011

Post by finale00 »

I'd like a template that I could just fill in without having to deal with the details.
Perhaps someone can make one such that I only have to write the parser function (otherwise I'll probably end up making one eventually after figuring out efficient ways to do it).

You know, something like

Code: Select all


#register plugin and stuff
def ...

#load model
def ...

#build model
def ...

def...

#parser functions
def...

def...

def...
So for example you're reading vertex information, and then you want to pass it to the "builder" function that takes your list of vertices.
Of course, it could get complicated, but maybe for very common things like loading vertices or faces I just have to pass in a list and don't care what happens after.
Privateer
veteran
Posts: 104
Joined: Fri Oct 21, 2011 1:33 pm
Location: On my Network, unless I'm somewhere else
Has thanked: 9 times
Been thanked: 30 times

Re: Noesis tutorial Basic Model

Post by Privateer »

Very good tutorial Mate, Thank You.
:up:
I've used this as the base to do the SWTOR exporter instead of codeing a stand alone program.
Give a man a fish and he'll eat for a day.
Try to teach a man to fish and he'll gripe at you for not just giving him the Damn Fish!
finale00
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2382
Joined: Sat Apr 09, 2011 1:22 am
Has thanked: 170 times
Been thanked: 307 times

Re: Noesis tutorial Basic Model

Post by finale00 »

Nice tutorial, followed it and the samples that came with noesis while writing a template
The template is pretty much just me copying code from Sanae3D though, only because I'm used to it, and I like to use a lot of functions or methods. At least that way, I can re-use functions quickly without having to worry about whether it will conflict with any existing code.

Code: Select all

...
def noepyLoadModel(data, mdlList):
   ctx = rapi.rpgCreateContext()
   parser = CrucisFatalFake_LMD(data)
   parser.parse_file()
   mdl = NoeModel(parser.meshList, None, None)
   mdlList.append(mdl)
   mdl.setModelMaterials(NoeModelMaterials(parser.texList, parser.matList))
   return 1

class CrucisFatalFake_LMD(object):
    
   def __init__(self, data):
       
      self.inFile = StructReader(data)
      self.meshList = []
      self.uvList = []
      self.vertList = ""
...
Now I just need to figure out how to assign materials flexibly...

Image

Maybe parse entire file first, and then go through the data and start building meshes and stuff...

EDIT: There we go...now I want to load the textures directly from the file.

Image
finale00
M-M-M-Monster veteran
M-M-M-Monster veteran
Posts: 2382
Joined: Sat Apr 09, 2011 1:22 am
Has thanked: 170 times
Been thanked: 307 times

Re: Noesis tutorial Basic Model

Post by finale00 »

lol chrrox your tutorials getting nuked.
chrrox
Moderator
Posts: 2602
Joined: Sun May 18, 2008 3:01 pm
Has thanked: 57 times
Been thanked: 1422 times

Re: Noesis tutorial Basic Model

Post by chrrox »

meh not to big a deal.
I am going to wait for the restructure to be over before i post much to avoid it being lost.
also i don't think anyone ever used it since i didn't see any new people contribute to the site.
I don't think all these sites cared about file samples being posted i think the issue Google had was people directly linking to warez releases.
Demonsangel
mega-veteran
mega-veteran
Posts: 241
Joined: Fri Aug 05, 2011 9:31 pm
Location: Antwerp
Has thanked: 13 times
Been thanked: 41 times

Re: Noesis tutorial Basic Model

Post by Demonsangel »

chrrox wrote:also i don't think anyone ever used it since i didn't see any new people contribute to the site.
Hey! I know I don't brainfart a new script every hour like finale does, but I started with this tutorial for Noesis.
Vindis
advanced
Posts: 44
Joined: Fri Apr 08, 2011 9:31 pm
Has thanked: 16 times
Been thanked: 41 times

Re: Noesis tutorial Basic Model

Post by Vindis »

Hi Chrrox,

can i ask how do you use "MaterialInfo.append([MaterialName, TexID[0], IndStart[0], IndCount[0]])" the IndStart list? Because I cant see, as if it was used. If it's not than what tells to Noesis which set of face use which mat? Only the position in the list?
chrrox
Moderator
Posts: 2602
Joined: Sun May 18, 2008 3:01 pm
Has thanked: 57 times
Been thanked: 1422 times

Re: Noesis tutorial Basic Model

Post by chrrox »

MaterialInfo.append([MaterialName, TexID[0], IndStart[0], IndCount[0]]) is just an array i made
it tells me how to load the mesh data correctly.
they just give a triangle list then they tell you how its broken down by materials.
so like say i had 90 faces
they would say break it down
start 0 count 25
then the next would be
start 25 count 15
then it would go
start 40 count x until no faces were left
and i am just assigning the materials to the faces as i load them.
Vindis
advanced
Posts: 44
Joined: Fri Apr 08, 2011 9:31 pm
Has thanked: 16 times
Been thanked: 41 times

Re: Noesis tutorial Basic Model

Post by Vindis »

thanks for the info, in the meantime i finished my plugin :) just one thing left. How can i say "look for the textures in this folder". Because every folder have a texture folder and now i have to copy all texture file into the same folder as the file, and that is, hmm annoying.
My code is based on your tutorial, i use rpg, nothing immediate draw and other fancy things.
Post Reply