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

Spotlight: Señor Casaroja's Noesis

General game file tools that are useful for more than one game
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

Oops, thanks for spotting that. Yeah, texture flags are used. I'm setting them in the Desert Strike script, but apparently I just set tex.flags instead of using tex.setFlags. These are the available flags:

Code: Select all

#define NTEXFLAG_ISNORMALMAP			(1<<0)
#define NTEXFLAG_SEGMENTED				(1<<1)
#define NTEXFLAG_STEREO					(1<<2) //indicates this is a stereo (side by side) image
#define NTEXFLAG_STEREO_SWAP			(1<<3) //should only be used in conjunction with NTEXFLAG_STEREO. Indicates left and right eyes are switched.
#define NTEXFLAG_FILTER_NEAREST			(1<<4) //nearest neighbor filtering is preferred
#define NTEXFLAG_WRAP_CLAMP				(1<<5) //clamp at edges
(these are registered under the noesis module, so reference with noesis.NTEXFLAG_ISNORMALMAP and so on)
The first 2 flags don't actually do anything, I can't remember what I had in mind for them. I might be using them in one of the plugins for local logic, which would be silly, but I can be silly sometimes.
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: Señor Casaroja's Noesis

Post by Demonsangel »

Code: Select all

class NoeBitStream(NoeUnpacker):
	def getBuffer(self, startOfs = None, endOfs = None):
		if startOfs is not None and endOfs is not none:   ### Should be None instead of none
I like this nextPass feature, trying to get 'Team Colors' to show.
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: Señor Casaroja's Noesis

Post by finale00 »

Is there a way to set the path to the python plugins folder?
Or perhaps specify a search path?

That way I can just point to my dropbox folder.

Also, suppose I had a game that used the same format, but were distributed on two separate platforms and therefore used big and small endian.

Code: Select all

bs = NoeBitStream(data, 1) # big endian
bs.read('3f')
It would be convenient if the read function reads as big endian if no byte order was specified.
Since the bit stream is declared as big endian, methods like

Code: Select all

bs.readUInt()
bs.readFloat()
are basically byte-order independent since I can set options when I check the type of format.

Code: Select all

rapi.rpgSetOption(noesis.RPGOPT_BIGENDIAN, 1)
For now I am just reading each field one by one rather than tossing everything in with a format string.
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

Yeah, you can use os.chdir(os.path.dirname(noesis.getMainPath()) + "\plugins\python")

And yeah, you can do exactly what you did there:

Code: Select all

bs = NoeBitStream(data, NOE_BIGENDIAN)
And all typed reads (such as readUInt and readFloat) will be done as bigendian.
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: Señor Casaroja's Noesis

Post by finale00 »

How about the read with a format string?
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

Oh, is that what you meant? You still have to explicitly put a ">" or "<" before the format string to specify endian there, regardless of the stream's endian mode. I could potentially change that functionality, but that would be a bad idea at this point, since it could break old scripts that do mixed-endian reading.
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: Señor Casaroja's Noesis

Post by finale00 »

How about a custom read method? Like...xread LOL
But ya I could just stick with

Code: Select all

bs.readUInt()
bs.readUInt()
bs.readUInt()
bs.readUInt()
bs.readUInt()
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

This ended up being a much larger release than I'd planned. Too much spare time lately.
-3.994 - Added import support for Stainless MDL/CNT files.
-3.994 - Added import support for Stainless TDX files.
-3.994 - Added extraction support for Stainless WAD files.
-3.994 - Added rapi.createBoneMap. See example_bonemaps.py in the Noesis scripts repository for usage. Also keep in mind that you can use -maxbones # when exporting models to have Noesis automatically split meshes to keep bone reference counts under #.
-3.994 - Added noesis.setTypeExportOptions, to specify default export options for a type. Also see example_bonemaps.py for usage.
-3.994 - Static chrome UV's are now used for HL1 MDL chrome surfaces.
-3.994 - Corrected a bug with HL1 MDL normals. Also added -hlmdlnonrm to discard HL1 MDL normals.
-3.994 - Fixed a typo that was making setFlags unusable in the NoeTexture class. Thanks to demonsangel for spotting it.
-3.994 - Now recognizing 0 and NaN cases in the half-float decode function. Thanks to demonsangel for pointing out that this was not happening.
-3.994 - Fixed another typo in the NoeBitStream class. Thanks to demonsangel for spotting this one too.
-3.994 - Added Noesis_InputReadFile/Noesis_InputReadFileW, which read files into memory relative to the path of the input file.
-3.994 - Added RPGOPT_SWAPHANDEDNESS.
-3.994 - Added swapHandedness methods to NoeMat43 and NoeMat44.
The Stainless stuff was done for iOS Carmageddon primarily. I've been playing it a lot and decided to dig around in it. It also happens to work on Magic: The Gathering – Duels of the Planeswalkers and probably some other Stainless Games stuff, though. Although they have conflicts between games for texture formats (the same format identifier means different pixel modes even though the headers between games are identical, kind of a pain in the ass), so Carmageddon is the only one I guarantee to work without issues.

Also, if you didn't see the thread I made for it, there's a new Noesis scripts/plugins repository. This is where I've put the example_bonemaps.py file referenced in the release notes, and I have latest versions of my tools scripts there. chrrox, revelation, demonsangel, and finale00 also have write access to the repository and have started using it to store stuff. This is now the official place to get the latest versions of everyones' scripts and plugins, and it should be easier to have everything there instead of randomly sprinkled throughout threads and file sharing sites.
RacingFreak
veteran
Posts: 136
Joined: Fri Feb 11, 2011 10:44 am
Location: Bulgaria
Has thanked: 50 times
Been thanked: 19 times

Re: Señor Casaroja's Noesis

Post by RacingFreak »

Awesome!! Now if only someone can do the same for Carmageddon 1, Carmageddon 2 and TDR2000 for Noesis :D

PS
Managed to fix the issue I had with the strips - actually had to re-export to pkg and import back using zmodeler and delete one stripe (will automatically erase the rest). Thanks for this great tool :)
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

-3.996 - Added "Visualizers". Plugins can now register visualizers to hook into preview rendering. See example_visualizer01 (effect rendering) and example_visualizer02 (allows you to select and export triangles) in my folder on the plugins repository.
-3.996 - New shared Noesis GL interface for native plugins to perform API-agnostic rendering.
-3.996 - Tool menu items can now be checked/unchecked, using noesis.checkToolMenuItem.
-3.996 - New optional parameter at the end of registerTool allows tools to specify their help text. (which is displayed in the Noesis status bar when the user mouseovers the tool menu item)
-3.996 - New math functions: Math_WorldToScreenSpace, Math_ScreenToWorldSpace, Math_PointRelativeToPlane, Math_LineIntersectTri.
-3.996 - Added Noesis_RegisterUserButton, which allows native plugins to register new model control buttons.
-3.996 - Fixed a Python print-related crash. Thanks to demonsangel for reporting the crash.
-3.996 - More interface cleanup. Anim slider moves above buttons if window is too small for it to fit next to buttons.
There are 2 example visualizer plugins with source up on the plugins repository. One of them demonstrates hijacking the model rendering to perform a simple effect (which I dubbed the Magical Pony Fire effect):
Image
The other one implements a triangle picker, which allows you to select triangles by clicking them (or deselect them by ctrl+clicking them), and then you can export the selected triangles to new models using a custom tool button:
Image
Visualizers allow tons of flexibility and open the door for all kinds of new functionality to be implemented in Noesis. I hope someone else decides to have fun with them.
User avatar
Mirrorman95
ultra-veteran
ultra-veteran
Posts: 355
Joined: Tue Jul 20, 2010 2:08 am
Has thanked: 16 times
Been thanked: 24 times

Re: Señor Casaroja's Noesis

Post by Mirrorman95 »

I recently used the heightmap tool in the plugins repository on a lightmap I recently acquired. Gentlemen, may I present you with the island of Mata Nui:
Image
BBSFM and KH2FM+ saves are compatible with KH2.5. viewtopic.php?t=13424
Devilot
ultra-veteran
ultra-veteran
Posts: 443
Joined: Tue Sep 07, 2010 10:12 am
Has thanked: 159 times
Been thanked: 49 times

Re: Señor Casaroja's Noesis

Post by Devilot »

the repository is a great idea. I do hope that the plugins author upload instructions for them, however, like the Crisis core one.
MrAdults
Moderator
Posts: 1007
Joined: Mon Mar 23, 2009 2:57 am
Has thanked: 44 times
Been thanked: 505 times

Re: Señor Casaroja's Noesis

Post by MrAdults »

Instructions on how to use plugins are in the Noesis ReadMe.txt. Instructions on how to use specific plugins are typically not needed. You put the plugin in the plugins and/or python directory, then go double-click on the game files in Noesis.
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: Señor Casaroja's Noesis

Post by Demonsangel »

I tend to put some instructions inside the script, mostly concerning 1 variable for a path to textures etc.
Something like

Code: Select all

"""
Script: 
import requires all art assets to be extracted from the archive

How to use:
    needs:  .sm  file (link to materials and model file) This is the file loaded in Noesis
            .smd file (mesh file)
            .ast file (armature) if rig.ast is not present a file dialog will open for you to select an .ast
            textures @ TEXPATH (see below)
            
"""


################################
###  Only change this line  ####
################################
TEXPATH = r"c:\users\Andrew\Desktop\XentaxScript\PoE\Art"

################################
### Do not change below here ###
################################
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: Señor Casaroja's Noesis

Post by Demonsangel »

I am at a loss here. I was busy rewriting my Titan Quest script when I ran into some weirdness with the bones. Instead of harassing MrAdults with about a dozen pm's a day I'll put it here incase someone else might be able to help.

Old code:

Code: Select all

    def Bones(self):
        self.data.seek(4,1)
        numBones    = self.data.readUInt()
        self.bones  = []
        for i in range( numBones):
            bone = {'name' : noeStrFromBytes(self.data.readBytes(32))}
            bone['index']       = i
            bone['child']       = self.data.readUInt()
            bone['numChild']    = self.data.readUInt()
            boneBuff=self.data.readBytes(48)
            
            (a1,a2,a3),(a4,a5,a6),(a7,a8,a9),(x,y,z) = NoeMat43.fromBytes(boneBuff)
            
            tupl=(a1,a2,a3,0),(a4,a5,a6,0),(a7,a8,a9,0),(x,y,z,1)
            
            bone['matrix'] = NoeMat44(tupl)
            self.bones.append(bone)
        for bone in self.bones:
            for i in range( bone['numChild']):
                child = bone['child'] + i
                self.bones[child]['parent'] = bone['index']
                
        for bone in self.bones:
            if not 'parent' in bone:
                bone['parent'] = -1
            else:
                matrix = bone['matrix']
                parent = self.bones[ bone['parent']]['matrix']
                bone['matrix'] = matrix.__mul__(parent)
                m = bone['matrix'].mat44
        for bone in self.bones:
            m = bone['matrix'].mat44
            k=[0,0,0,0]
            for t in range(len(m)):
                k[t] = NoeVec3(m[t][:-1])
            bone['matrix'] = NoeMat43(list(k))
            self.boneList.append(NoeBone(bone['index'],
                                         bone['name'],
                                         bone['matrix'],
                                         None,
                                         bone['parent']))
        
        return
new code:

Code: Select all

    def Bones(self):
        self.data.seek(4,1)
        numBones    = self.data.readUInt()
        self.bones  = []
        for i in range( numBones):
            bone = {'name' : noeStrFromBytes(self.data.readBytes(32))}
            bone['index']       = i
            bone['child']       = self.data.readUInt()
            bone['numChild']    = self.data.readUInt()
            bone['matrix']      = NoeMat43.fromBytes(self.data.readBytes(48))
            self.bones.append(bone)
        
        for bone in self.bones:
            for i in range( bone['numChild']):
                child = bone['child'] + i
                self.bones[child]['parent'] = bone['index']
        
        for bone in self.bones:
            if not 'parent' in bone:
                bone['parent'] = -1
            else:
                matrix = bone['matrix']
                parent = self.bones[ bone['parent']]['matrix']
                bone['matrix'] = matrix.__mul__(parent)
                #bone['matrix'] = bone['matrix'].transpose()
                m = bone['matrix'].mat43
            self.boneList.append(NoeBone(bone['index'],
                                         bone['name'],
                                         bone['matrix'],
                                         None,
                                         bone['parent']))
        
        #self.boneList = rapi.multiplyBones(self.boneList)
Old code:
Image

new code:
Image

According to me I'm basically doing the same, but I must be missing some detail.

Edit: updated old code to more readable version
Post Reply