Mod Tool: Difference between revisions

From OniGalore
Jump to navigation Jump to search
(new v3 with fix for PPGs; redoing code piece "import an image clip only once")
m (archive link)
 
(88 intermediate revisions by 3 users not shown)
Line 1: Line 1:
==General information==
Autodesk's Mod Tool used to be the main choice for modders when working on level and character models (at least in Windows, since it's Windows-only). It was a free and accessible program, but has long since been discontinued. Nowadays, modders are encouraged to use [[Blender]] instead.
Various modders of the community use [http://www.moddb.com/downloads/autodesk-softimage-mod-tool-75 "Autodesk Softimage Mod Tool"]. Usually we just call it "Mod Tool" or "XSI" (old name of the program).
{| class="wikitable" style="float:right;"
 
|width="200px"|
Beside the free version, there's also the retail and student version "Autodesk Softimage [year]"
;sub pages
: Those versions are also available as 64-bit versions.
: [[Mod_Tool/Scripting]]
: If you are a student you can get Autodesk Software for free if your school is a partner of Autodesk.
: [[Mod_Tool/Rigging]]
:: In that case you can register with your email address given by your school.
: [[Mod_Tool/OniTools_addon]]
 
: [[Mod_Tool/Oni_level_rebuilder]]
But in the very most cases "Mod Tool" isn't really in disadvantage towards the retail version when it comes to modding Oni.
|}
==Installation==
Full name: "Autodesk Softimage Mod Tool". Usually we just call it "Mod Tool" or "XSI", the old name of the program.


There's also the possibility to get files that extent Mod Tool's range of functions.
'''Download'''
: Those are scripts (.vbs, .js), toolbars (.xsitb) and addons (.xsiaddon).
* '''Get it from [https://www.moddb.com/downloads/autodesk-softimage-mod-tool-75 Mod DB].'''
: They can be create on one's own and also shared with other users.
* It's no longer possible to download Softimage from Autodesk because the [https://www.autodesk.com/products/softimage/overview software was discontinued] by Autodesk in favor of their major software packages Maya and 3ds Max.


'''Newer Windows installations usually require .NET 2.0 to be installed before the XSI installer can fully run.'''
: Downloading and installing .Net 2.0 manually from the internet might not work.
: '''Therefore use the Windows search and look for "features".''' (Enable/disable Windows features.)
: From the Windows feature page install '''".NET framework 3.5 (includes .NET 2.0 and 3.0)"'''.


'''Wanted knowledge:'''
There are various ways to extend Mod Tool's range of functions: scripts (.vbs, .js), toolbars (.xsitb) and addons (.xsiaddon). They can be created on one's own and also shared with other users.
* How to setup particle in Mod Tool? We still need an Oni particle editor!
* How to rig models and then extract the animation data?


 
===Extensions===
==Extensions==
'''links'''
'''links'''
* [http://www.si-community.com/community/viewtopic.php?f=35&t=796 "Roadkill" UV Tool]
* [https://www.si-community.com/community/viewtopic.php?f=35&t=796 "Roadkill" UV Tool]
* [http://www.xsidatabase.com/content/drag_and_drop_obj_files_jscript drag and drop support for OBJ files]  
* [http://web.archive.org/web/20121001104022/http://www.xsidatabase.com:80/content/drag_and_drop_obj_files_jscript drag and drop support for OBJ files]  
* [http://www.si-community.com/community/viewtopic.php?f=35&t=797 OBJ exporter]
* [https://www.si-community.com/community/viewtopic.php?f=35&t=797 OBJ exporter]
* [http://mods.oni2.net/node/210 OBJ + OFGA exporter] for Oni
* [http://mods.oni2.net/node/210 OBJ + OFGA exporter] for Oni
** might need the regular OBJ exporter to be installed first
** might need the regular OBJ exporter to be installed first
* [http://mods.oni2.net/node/210 adjusting existing fw throws] [http://www.youtube.com/watch?v=Zcbv7y5HvRI (instructions)]
* [http://mods.oni2.net/node/210 adjusting existing fw throws] [https://www.youtube.com/watch?v=vDTPYfvMf4M (instructions)]
* ''put more links here''




Line 54: Line 56:




'''[http://softimage.wiki.softimage.com/sdkdocs/cus_addons_WalkthroughBuildinganAddon.htm building add-ons]'''
'''building add-ons (<nowiki>http://softimage.wiki.softimage.com/sdkdocs/cus_addons_WalkthroughBuildinganAddon.htm</nowiki>, dead link)'''
* Building an add-on file needs more effort than the other 'extensions' but for the end user it's best because it can hold all other files: toolbars, plugins (scripts), etc.
* Building an add-on file needs more effort than the other 'extensions' but for the end user it's best because it can hold all other files: toolbars, plugins (scripts), etc.
** The user can install a newer version of the addon by repeating the installation with the new file: it's unnecessary to remove the old version.
** The user can install a newer version of the addon by repeating the installation with the new file: it's unnecessary to remove the old version.
Line 146: Line 148:
  end if
  end if
  end function
  end function


==Selected wisdom==
==Selected wisdom==
We've also a [http://oni.bungie.org/community/forum/viewtopic.php?id=1229 tutorial thread on OCF.]
We've also a [http://oni.bungie.org/forum/viewtopic.php?id=1229 tutorial thread on OCF.]


Hotkey list can be found [http://www.keyxl.com/aaacbe0/415/SoftImage-XSI-keyboard-shortcuts.htm here].
Hotkey list can be found [http://www.keyxl.com/aaacbe0/415/SoftImage-XSI-keyboard-shortcuts.htm here].
Line 189: Line 190:
** B) goto Model > Modify > Poly. Mesh > Bridge Boundary Points/Edges (if blue lines appear try to disable the checkbox "Angle > 90")
** B) goto Model > Modify > Poly. Mesh > Bridge Boundary Points/Edges (if blue lines appear try to disable the checkbox "Angle > 90")
* merging two objects at blue lines: merge the objects at first, then proceed with the point above "filling a hole"
* merging two objects at blue lines: merge the objects at first, then proceed with the point above "filling a hole"
* '''merging two objects with UVs''': Create > Poly. Mesh > [http://cryrid.com/images/temp/XSI/merge_materials.jpg Merge]
* '''merging two objects with UVs''': Create > Poly. Mesh > [https://web.archive.org/web/20160225163131/http://cryrid.com/images/temp/XSI/merge_materials.jpg Merge]
** extracted Oni characters have their materials and hence textures directly grouped under the mesh (see Explorer)
** extracted Oni characters have their materials and hence textures directly grouped under the mesh (see Explorer)
** when adding (merging) a new part to the existing mesh, the materials/textures will be grouped under (Explorer again) mesh > Polygon Mesh > Clusters > ...
** when adding (merging) a new part to the existing mesh, the materials/textures will be grouped under (Explorer again) mesh > Polygon Mesh > Clusters > ...
Line 245: Line 246:


===Material and texture===
===Material and texture===
[[Image:apply_new_main_texture_in_Mod_Tool.png|thumb|200px|right|how to apply a new main texture]]
[[Image:apply_new_polygon_cluster_texture_in_Mod_Tool.png|thumb|200px|right|how to apply a new polygon cluster texture]]
* objects use the scene material by default, objects need their own material if they should have individual textures
* objects use the scene material by default, objects need their own material if they should have individual textures
* selecting a material and surface: Model > Material > Phong (simply close the windows that pops up)
* selecting a material and surface: Model > Material > Phong (simply close the windows that pops up)
Line 254: Line 257:
** choose "diffuse" as illumination mode (for instance "ambient" won't work - the texture would be visible in Mod Tool but Onisplit can't use it)
** choose "diffuse" as illumination mode (for instance "ambient" won't work - the texture would be visible in Mod Tool but Onisplit can't use it)
* now "Image" can be double-clicked (the Texture Editor will pop up), in the section "Image" click on "New" to choose an image
* now "Image" can be double-clicked (the Texture Editor will pop up), in the section "Image" click on "New" to choose an image
* to change textures '''don't drag and drop textures directly one meshes''', this creates textures layers which are ignored by OniSplit




Line 276: Line 280:
* freeze object to ''bake'' projections (green lines in the 3D view will disappear)
* freeze object to ''bake'' projections (green lines in the 3D view will disappear)
* getting a picture of the UV: apply first a black texture then enter the Texture Editor [Alt] + [7], Edit > Stamp UV Mesh [Shift] + [S], save the texture
* getting a picture of the UV: apply first a black texture then enter the Texture Editor [Alt] + [7], Edit > Stamp UV Mesh [Shift] + [S], save the texture
Things to test:
* https://vimeo.com/67783125
===Rendering===
Rendering doesn't work well on my PCs.--[[User:Paradox-01|paradox-01]] ([[User talk:Paradox-01|talk]]) 01:09, 9 March 2016 (CET)
Especially PNG, it works one time. But as soon as I import another dae the next render will fail.
So, for creating a object library we might use blender. Nice thing is, it can also render in the background when called from CMD. This means you could keep working in XSI and render images in blender at the same time.
example of a batch file:
cd C:\Program Files\Blender Foundation\Blender
blender -b --python C:\Users\Paradox-01\Desktop\blender-out\pythonScript.py
example of a render script:
import bpy
# delete blender default cube object
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete(use_global=False)
# load new object
bpy.ops.wm.collada_import(filepath = "C:/Oni/r.dae")
# render settings
bpy.data.scenes['Scene'].render.resolution_x = 900
bpy.data.scenes['Scene'].render.resolution_y = 900
# default resolution percentage seems to be 50
bpy.data.scenes['Scene'].render.resolution_percentage = 100
bpy.data.scenes['Scene'].render.use_antialiasing = True
# 5 | 8 | 11 | 16
bpy.data.scenes['Scene'].render.antialiasing_samples = "8"
#TGA IRIS JPEG MOVIE IRIZ RAWTGA
#AVIRAW AVIJPEG PNG BMP FRAMESERVER
#no gif ? could be created by more code, maybe merging single frames of jpgs
bpy.data.scenes['Scene'].render.image_settings.file_format = 'JPEG'
bpy.data.scenes['Scene'].render.image_settings.quality = 100
# file suffix is overwritten by file_formate
#bpy.data.scenes['Scene'].render.filepath = 'C:/Oni/renders/test.jpg'
bpy.data.scenes['Scene'].render.filepath = 'C:/Oni/renders/test'
# start render
bpy.ops.render.render( write_still=True )




===Animating===
===Animating===
[[Image:Scale_animations_in_Mod_Tool_Animation_Editor_DopeSheet.png|thumb|200px|right|an animation that got scaled down from 116 to 58 frames]]
* preparing ModTool to save animations Oni can use: File > Preferences... > Preferences > Tools > Output Format
* preparing ModTool to save animations Oni can use: File > Preferences... > Preferences > Tools > Output Format
** Frame Step: 1
** Frame Step: 1
** Frame Format: Custom framerate
** Frame Format: Custom framerate
** Frame Rate: 60
** '''Frame Rate: 60'''
* for real-time playback click on "Playback" option button and then "Real-Time Playback"
* for real-time playback click on "Playback" option button and then "Real-Time Playback"
* opening the Animation Editor : [0]
* opening the Animation Editor : [0]
* opening the DopeSheet: from any other Animation Editor, Editor > DopeSheet
* opening the DopeSheet: from any other Animation Editor, Editor > DopeSheet
* making an animation shorter or longer: in DopeSheet click Region button (arrow with rectangle), click and hold to select keyframes, at selection edge a) [shift] plus left-click and move mouse or b) middle-click and move mouse
* '''making an animation shorter or longer:''' in DopeSheet click Region button (arrow with rectangle), click and hold to select keyframes, at selection edge a) [shift] plus left-click and move mouse or b) middle-click and move mouse
 
 
====Issues when saving and sharing animation files (*.dae, *.exp)====
That what you currently see in Mod Tool and that what you get when saving data can be different.


1) *.dae files might save data differently than expected.


===Scripting===
''Rotation flips'' might appear.
* open the Script Editor: [Alt] + [4]
 
* run the current code in the Script Editor window: [F5]
2) *.exp and *.dae save data differently.
* clear the log: Edit > Clear History Log
* Actions from button embedded code will be logged.
* Actions from code files that are linked in a button won't be logged. (This results in a performance boost.)
* Logged stuff will be rewritten if you change the script language. (File > Preferences...)
* You can also change the language by right-click the white script box and click on "Set to JScript" or "Set to VBScript".
* Python can be added as script language if you install it on your PC.
* Right-clicking the white script box gives you also access to a few code piece, e.g. "Syntax Help" > "If..Else" or "Catch Error".
* Mark code you want to disable ("Comment Out") or enable ("Comment Remove").
* You can save your code to a file via "File" > "Save As..." or "Save Selection"


*.exp files store the frames as they are.


'''Links'''
*.dae file store their actions (rotations/translation/...) in seconds and eventually depend on what framerate a Modder uses.
* [http://softimage.wiki.softimage.com/index.php?title=Scripting_Tips_and_Tricks_%28XSISDK%29 xsi wiki page about scripting]
* '''[http://softimage.wiki.softimage.com/sdkdocs/scriptsdb/scriptsdb/scrdb_vbscript.htm many vbscript examples]'''
* [http://www.activexperts.com/activmonitor/windowsmanagement/adminscripts/filesfolders/files/ objFSO/objWSHShell: Scripts to manage Files] (replace "Wscript.Echo" with "logmessage")
* [http://activexperts.com/activmonitor/windowsmanagement/adminscripts/other/textfiles/ objFSO/objWSHShell: Scripts to manage Text Files]
* [http://www.kxcad.net/Softimage_XSI/Softimage_XSI_Documentation/script_basics_IncludingExternalScripts.htm using external scripts]
* [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_om/XSIUIToolkit.html,topicNumber=si_om_XSIUIToolkit_html progress bar and open file dialog<!-- (hm, InitialDirectory code for quick save idea ?)-->]
<!--* [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=files/cus_ppg_FileBrowserWidget.htm,topicNumber=d30e11980,hash=WS34BA39B437A993419C80CAB58E3BEFA1-0059 text box]-->




'''Notes about polygon extraction'''
'''Following scenario: a modder wants to share an animation as *.exp and another modder wants to use that animation.'''
* a grid of 9 polygons (ID 0 to 8)
* you extract the middle polygon (ID 4)
* grid's polygon of ID 8 will change to ID 4
* if you now extract 5th, the 7th gets the ID
* now the ID order is: 0, 1, 2, 3, 6, 5, 4
* this means that an automatic extraction of several polygons requires two changes in the code
* 1) the ID array of selected polygons must be sorted
* 2) the extraction must start with the last ID to avoid ID shift during the process (see code pieces)


Modder A uses 30 fps in his settings and exports an *.exp that is 58 frame long.


'''Some basics of VBScript'''
Modder B uses 60 fps in his setting and imports the *.exp, to his surprise the animation is 116 frames long. What now?
* In VBS you declare variables only as a variant e.g. "dim PolygonCount". Mod Tool decides what type it becomes when it uses the variable the first time.
* You can put "option explicit" at the start of your script. Then you will be forced to declare all variables with "dim". That looks quite unnecessary but help to find mistakes for example if you mistyped a variable somewhere in a long script.


* The commands are not case-sensitive, e.g. you can write "LogMessage" or "logmessage".
If Modder B saves the *.exp under a new name would not help, the frames will be still the same.
* Vbs functions can be found [http://www.w3schools.com/vbscript/vbscript_ref_functions.asp HERE.]


' while it is possible to use + in strings,
Instead Modder B could scale the animation. The way to do so is described in the [[#Animating|Animating]] section, "making an animation shorter or longer".
' you should actually use & only for strings
 
' and + only for numbers
Modder B scales the animation and saves it as dae.
 
logmessage "How about ... " + "a riddle?"
Now he wants to see if it worked. He opens a new scene [Strg]+[N] and loads the dae.
logmessage "Where can you find following phrase in Oni? " & "The day is mine !!"
 
logmessage "Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to " & cstr(2 + 0 + 3 + 2) & "."
 
'''Summary of issue-holding aspects'''
' INFO : How about ... a riddle?
' INFO : Where can you find following phrase in Oni? The day is mine !!
' INFO : Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to 7.


* '''Frames Per Second (FPS)'''
When you share *.dae files with other Modders be sure that the output formate is 60 fps. Different framerates among modders can cause confusion.


'''More tips'''
Dae file store their actions (rotation/translation/...) in seconds.


* You can replace each "logmessage" with a variable. In that case don't forget the = sign.
An example:
* Selection(0) means that from all selected objects the first one is taken.
* Use a loop to get all objects.


  for each n in selection
  <float_array count="10" id="pelvis_rotation_x_ANGLE-anim-input-array">
logmessage n
    0.000000 0.166833 0.333667 0.500500 0.600600 0.934267 1.267933 1.601600 1.768433 1.935267
  next
  </float_array>


* In order to shorten the code you might want to set script objects.
action at [s] * framerate [f/s] = frame [f]
: The command to create a standard cube is ''CreatePrim "Cube", "MeshSurface"''
 
: If you use '''''set name = ''''' the cube or other object will be available under that ''name''.
In this example the last rotation occurs at 1.935267s.
: Then you can use any valid method or property with that ''name''.
 
: Example:
: Modder A uses 30 fps, his last frame is 58.
: Modder B uses 60 fps, his last frame is 116.
set c = CreatePrim ("Cube", "MeshSurface")
 
 
logmessage selection(0).name
* '''Euler rotations'''
logmessage selection(0).length.value
 
logmessage c.name
you will get [[wikipedia:Bézier_curve|BEZIER]] (cubic) interpolation instead of LINEAR if you use
logmessage c.length.value
: "Convert Quarternion Rotation to Euler"  
: "Spline Interpolation" in the animation (fcurve) editor
' INFO : cube
 
' INFO : 8
Those smooth the rotations.
' INFO : cube
' INFO : 8


[...]


'''Some ModTool commands in vbs language that might be interesting for Oni related stuff'''


{| class="wikitable" width="100%"
* '''Quaternion rotations'''
| width=50% valign=top | LogMessage <nowiki>[string | var | object]</nowiki>
 
| A command to log information. It requires a string or a variable. Example:
Don't use this.
: LogMessage "The object's name is: " & selection(0).Name
 
|-
[...]
| logmessage selection.count
 
| Logs the number of selected objects.
 
|-
* '''Make Rotation Keys Continuous'''
| logmessage selection(0).Name
 
| Logs the object name.
[...]
|-
 
| logmessage selection(0).Material.Name
 
| Logs the material name.
====Animation mixer====
|-
This could help with creating cutscenes and complex animations (e.g. run cycles for FILM files).
|valign=top| logmessage selection(0).Material.CurrentImageClip.source.filename.value
 
| Logs absolute path of the texture. Output example:
Open the animation mixer with [Alt]+[0].
: ''C:\Users\RRM\Desktop\TV_tex.png''
 
|-
Further information over [http://web.archive.org/web/20170103065301/http://softimage.wiki.softimage.com/xsidocs/nla_mixer.htm#Rdw10200 HERE.]
|valign=top| logmessage selection(0).Material.CurrentImageClip.source.Parameters("XRes").Value<br>logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
 
| Logs the horizontal (X) and vertical (Y) pixel length of the texture. Output example:
Here are a few videos:
: ''512''
* '''https://www.youtube.com/watch?v=-xC31Q7zpM0'''
|-
* https://www.youtube.com/watch?v=njLrAIpDOFU
| logmessage selection(0).sclx.value<br>logmessage selection(0).scly.value<br>logmessage selection(0).sclz.value
* https://www.youtube.com/watch?v=PRDZaEo5CGo
|valign=top| Logs object's scaling (X, Y, Z).
 
|-
 
| logmessage selection(0).rotx.value<br>logmessage selection(0).roty.value<br>logmessage selection(0).rotz.value
=====Sounds=====
|valign=top| Logs object's rotation (X, Y, Z).
Within the animation mixer also sounds can be added.
|-
 
| logmessage selection(0).posx.value<br>logmessage selection(0).posy.value<br>logmessage selection(0).posz.value
Further information over [http://web.archive.org/web/20170102120711/http://softimage.wiki.softimage.com/xsidocs/audio_LoadingAudioFilesintheAnimationMixer.htm#Rdy36984 HERE.]
|valign=top| Logs object's position (X, Y, Z).
 
|-
This could be used to synchronize sounds effect with physical actions in the scene.
|valign=top| logmessage selection(0).rotorder.value
 
| Logs object's rotation order. Oni characters have ZYX as rotation order.
 
:0 = XYZ
===Scripting===
:1 = XZY
* open the Script Editor: [Alt] + [4]
:2 = YXZ
* run the current code in the Script Editor window: [F5]
:3 = YZX
* clear the log: Edit > Clear History Log
:4 = ZXY
* Actions from button embedded code will be logged.
:5 = ZYX
* Actions from code files that are linked in a button won't be logged. (This results in a performance boost.)
|-
* Logged stuff will be rewritten if you change the script language. (File > Preferences...)  
|valign=top| SelectObj <nowiki>[string | var]</nowiki>
* You can also change the language by right-click the white script box and click on "Set to JScript" or "Set to VBScript".
| select one or more objects. Comma serves as separator. Example:
* Python can be added as script language if you install it on your PC.
: SelectObj "grid,grid1"
* Right-clicking the white script box gives you also access to a few code piece, e.g. "Syntax Help" > "If..Else" or "Catch Error".
|-
* Mark code you want to disable ("Comment Out") or enable ("Comment Remove").
|valign=top| ToggleSelection <nowiki>[string | var]</nowiki>
* You can save your code to a file via "File" > "Save As..." or "Save Selection"
| Adds one or more objects from current selection. If the objects are already selected then they become subtracted from the selection. Example:
 
: ToggleSelection "grid2,grid3"
 
|-
'''Links'''
|valign=top| [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/SelectAllUsingFilter.html,topicNumber=si_cmds_SelectAllUsingFilter_html SelectAllUsingFilter] <nowiki>[SelFilter], [CheckComponentVisibility], [AffectSelectionList], [CheckObjectSelectability]</nowiki>
* [http://web.archive.org/web/20160803061035/http://softimage.wiki.softimage.com/index.php?title=Scripting_Tips_and_Tricks_%28XSISDK%29 xsi wiki page about scripting]
|Selects everything that matches the filter and other options. Example:
* '''[http://web.archive.org/web/20170616035120/http://softimage.wiki.softimage.com/sdkdocs/scriptsdb/scriptsdb/scrdb_vbscript.htm many vbscript examples]'''
: SelectAllUsingFilter "object", siCheckComponentVisibility
* '''[https://ss64.com/vb/ vbs commands]'''
:: selects all roots, lights, cameras, and geometry (meshes)
* [https://web.archive.org/web/20070510173452/https://www.activexperts.com/activmonitor/windowsmanagement/adminscripts/filesfolders/files/ objFSO/objWSHShell: Scripts to manage Files] (replace "Wscript.Echo" with "logmessage")
: SelectAllUsingFilter "geometry", siCheckComponentVisibility
* [https://web.archive.org/web/20150504221146/http://activexperts.com/activmonitor/windowsmanagement/adminscripts/other/textfiles/ objFSO/objWSHShell: Scripts to manage Text Files]
:: selects all geometry (meshes)
* [http://web.archive.org/web/20080905102848/http://www.kxcad.net/softimage_xsi/Softimage_XSI_Documentation/script_basics_IncludingExternalScripts.htm using external scripts]
|-
* [https://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_om/XSIUIToolkit.html,topicNumber=si_om_XSIUIToolkit_html progress bar and open file dialog<!-- (hm, InitialDirectory code for quick save idea ?)-->]
| DeselectAll
<!--* [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=files/cus_ppg_FileBrowserWidget.htm,topicNumber=d30e11980,hash=WS34BA39B437A993419C80CAB58E3BEFA1-0059 text box]-->
| deselects everything
 
|-
 
|valign=top| DeleteObj <nowiki>[string | var]</nowiki>
'''Notes about polygon extraction'''
| Deletes one or more objects. Comma used as separator. Example:
* a grid of 9 polygons (ID 0 to 8)
: DeleteObj "grid"
* you extract the middle polygon (ID 4)
: DeleteObj "grid,grid1"
* grid's polygon of ID 8 will change to ID 4
|-
* if you now extract 5th, the 7th gets the ID
| FreezeObj
* now the ID order is: 0, 1, 2, 3, 6, 5, 4
| Makes all changes final. But saving those changes is still necessary. (Click on key symbol so it gets red.)
* this means that an automatic extraction of several polygons requires two changes in the code
|-
* 1) the ID array of selected polygons must be sorted
| [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/Translate.html,topicNumber=si_cmds_Translate_html Translate] <nowiki>[InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Snap], [SnapReference], [SnapFilter], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents]</nowiki>
* 2) the extraction must start with the last ID to avoid ID shift during the process (see code pieces)
| see link
|-
| [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/Rotate.html,topicNumber=si_cmds_Rotate_html Rotate] <nowiki>[InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Reference], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents]</nowiki>
| see link
|}




====Code pieces (VB Script)====
'''Some basics of VBScript'''
Remember that you can use the search function of your web browser.
* In VBS you declare variables only as a variant e.g. "dim PolygonCount". Mod Tool decides what type it becomes when it uses the variable the first time.
* You can put "option explicit" at the start of your script. Then you will be forced to declare all variables with "dim". That looks quite unnecessary but help to find mistakes for example if you mistyped a variable somewhere in a long script.
 
* The commands are not case-sensitive, e.g. you can write "LogMessage" or "logmessage".
* Vbs functions can be found [https://web.archive.org/web/20150707131602/https://www.w3schools.com/vbscript/vbscript_ref_functions.asp HERE.]


If you see absolute paths adapt them so that the code works on your system too.
' while it is possible to use + in strings,
' you should actually use & only for strings
' and + only for numbers
logmessage "How about ... " + "a riddle?"
logmessage "Where can you find following phrase in Oni? " & "The day is mine !!"
logmessage "Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to " & cstr(2 + 0 + 3 + 2) & "."
' INFO : How about ... a riddle?
' INFO : Where can you find following phrase in Oni? The day is mine !!
' INFO : Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to 7.


{| class="wikitable" width="100%"
 
|width=33%| [1] creating and extending an array
'''More tips'''
|width=33%| [2] check selection mode
 
| [3] getting the IDs of selected polygons plus sorting them
* You can replace each "logmessage" with a variable. In that case don't forget the = sign.
|-
* Selection(0) means that from all selected objects the first one is taken.
| [4] converting euler rotations (in degrees) to quaternions
* Use a loop to get all objects.
| [5] converting quaternions to euler rotations (in degrees)
 
| [6] get and set values of the playcontrol (timeline)
for each n in selection
|-
logmessage n
| [7] get keyframes
next
| [8] message box
 
| [9] input box
* In order to shorten the code you might want to set script objects.
|-
: The command to create a standard cube is ''CreatePrim "Cube", "MeshSurface"''
| [10] getting the desktop path
: If you use '''''set name = ''''' the cube or other object will be available under that ''name''.
| [11] selecting a folder
: Then you can use any valid method or property with that ''name''.
| [12] selecting a file of a specific type
: Example:
|-
| [13] reading out environment variables
set c = CreatePrim ("Cube", "MeshSurface")
| [14] check if file exist
| [15] check if folder exist + create folder
logmessage selection(0).name
|-
logmessage selection(0).length.value
| [16] write xml file
logmessage c.name
| [17] read xml file
logmessage c.length.value
| [18] read xml file (attribute/content-based)
|-
' INFO : cube
| [19] create txt file + write lines
' INFO : 8
| [20] read txt file
' INFO : cube
| [21] check onisplit version
' INFO : 8
 
 
'''Some ModTool commands in vbs language that might be interesting for Oni related stuff'''
 
{| class="wikitable" width="100%"
| width=50% valign=top | LogMessage <nowiki>[string | var | object]</nowiki>
| A command to log information. It requires a string or a variable. Example:
: LogMessage "The object's name is: " & selection(0).Name
|-
|-
| [22] call CMD, e.g. to use onisplit
| logmessage selection.count
| [23] building forms in ModTool
| Logs the number of selected objects.
| [24] UserDataBlob
|-
| logmessage selection(0).Name
| Logs the object name.
|-
|-
| [25] getting the position of points (with selection mode point)
| logmessage selection(0).Materials(0).Name
| [26] getting the position of points (with selection mode object)
| Logs the material name.
| [27] getting and setting the position of points (without selection)
|-
|-
| [28] getting the rotation and position of selected objects
| logmessage selection(0).Materials(0).Library.name
| [29] getting the rotation and position of not selected objects
| Logs the material library name.
| [30] decrypting merged integer flags
|-
|-
| [31] disabling PPG popups
| logmessage selection(0).Materials(0).shaders(0).name
| [32] disabling logmessages
| Logs the material shader (e.g. phong).
| [33] getting bounding box values
|-
|-
| [34] open an explorer window
|valign=top| logmessage selection(0).Materials(0).CurrentImageClip.source.filename.value
| [35] working with hierarchies
| Logs absolute path of the texture. Output example:
| [36] drag and drop (DnD) support for specific oni files
: ''C:\Users\RRM\Desktop\TV_tex.png''
|-
|-
| [37] dae export
|valign=top| logmessage selection(0).Materials(0).CurrentImageClip.source.Parameters("XRes").Value<br>logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
| [38] fbx export
| Logs the horizontal (X) and vertical (Y) pixel length of the texture. Output example:
| [39] import an image clip only once
: ''512''
|}
|-
 
| logmessage selection(0).sclx.value<br>logmessage selection(0).scly.value<br>logmessage selection(0).sclz.value
 
|valign=top| Logs object's scaling (X, Y, Z).
' '''[1] creating and extending an array'''
|-
| logmessage selection(0).rotx.value<br>logmessage selection(0).roty.value<br>logmessage selection(0).rotz.value
|valign=top| Logs object's rotation (X, Y, Z).
' creating an array
|-
' () sets the number of array items
| logmessage selection(0).posx.value<br>logmessage selection(0).posy.value<br>logmessage selection(0).posz.value
redim nums (3)
|valign=top| Logs object's position (X, Y, Z).
for i = 0 to 3
|-
nums(i) = (i)
|valign=top| logmessage selection(0).rotorder.value
next
| Logs object's rotation order. Oni characters have ZYX as rotation order.
:0 = XYZ
logmessage "output all items"
:1 = XZY
for each n in nums
:2 = YXZ
logmessage n
:3 = YZX
next
:4 = ZXY
logmessage "first array item holds: " & lbound(nums)
:5 = ZYX
logmessage "last array item holds: " & ubound(nums)
|-
logmessage "-------------------------------------"
|valign=top| SelectObj <nowiki>[string | var]</nowiki>
| select one or more objects. Comma serves as separator. Example:
: SelectObj "grid,grid1"
' extending an array
|-
' use "preserve" to keep the old content
|valign=top| ToggleSelection <nowiki>[string | var]</nowiki>
' find next higher value
| Adds one or more objects from current selection. If the objects are already selected then they become subtracted from the selection. Example:
plus_one = ubound(nums) + 1
: ToggleSelection "grid2,grid3"
redim preserve nums (plus_one)
|-
nums(plus_one) = 4
|valign=top| [https://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/SelectAllUsingFilter.html,topicNumber=si_cmds_SelectAllUsingFilter_html SelectAllUsingFilter] <nowiki>[SelFilter], [CheckComponentVisibility], [AffectSelectionList], [CheckObjectSelectability]</nowiki>
|Selects everything that matches the filter and other options. Example:
for each n in nums
: SelectAllUsingFilter "object", siCheckComponentVisibility
logmessage n
:: selects all roots, lights, cameras, and geometry (meshes)
next
: SelectAllUsingFilter "geometry", siCheckComponentVisibility
logmessage "first array item holds: " & lbound(nums)
:: selects all geometry (meshes)
logmessage "last array item holds: " & ubound(nums)
|-
| DeselectAll
| deselects everything
' INFO : output all items
|-
' INFO : 0
|valign=top| DeleteObj <nowiki>[string | var]</nowiki>
' INFO : 1
| Deletes one or more objects. Comma used as separator. Example:
' INFO : 2
: DeleteObj "grid"
' INFO : 3
: DeleteObj "grid,grid1"
' INFO : first array item holds: 0
|-
' INFO : last array item holds: 3
| FreezeObj
' INFO : -------------------------------------
| Makes all changes final. But saving those changes is still necessary. (Click on key symbol so it gets red.)
' INFO : 0
|-
' INFO : 1
| [https://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/Translate.html,topicNumber=si_cmds_Translate_html Translate] <nowiki>[InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Snap], [SnapReference], [SnapFilter], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents]</nowiki>
' INFO : 2
| see link
' INFO : 3
|-
' INFO : 4
| [https://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_cmds/Rotate.html,topicNumber=si_cmds_Rotate_html Rotate] <nowiki>[InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Reference], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents]</nowiki>
' INFO : first array item holds: 0
| see link
' INFO : last array item holds: 4
|}
 
 
====Code pieces (VB Script)====
Remember that you can use the search function of your web browser.


If you see absolute paths adapt them so that the code works on your system too.


' '''[2] check selection mode '''
([[Mod_Tool/Scripting|Here]]'s another page for math related code pieces.)
checkfilter
'use sub to make use of the exit command
sub checkfilter
Select Case Selection.Filter.Name
'caution: case-sensitive
Case "object"
logmessage "object mode"
Case "Edge"
logmessage "edge mode"
Case "Vertex"
logmessage "point mode"
Case "Polygon"
logmessage "polygon mode"
Case Else
logmessage "unknown mode"
exit sub
End Select
end sub


 
{| class="wikitable" width="100%"
' '''[3] getting the IDs of selected polygons plus sorting them'''
|width=33%| [1] creating and extending an array
|width=33%| [2] check selection mode
poly_count = Selection(0).SubComponent.ComponentCollection.count
| [3] getting the IDs of selected polygons plus sorting them
logmessage "poly_count: " & poly_count
|-
redim selected (poly_count)
| [4] converting euler rotations (in degrees) to quaternions
logmessage "Show ID as it is."
| [5] converting quaternions to euler rotations (in degrees)
for i=0 to poly_count - 1
| [6] get and set values of the playcontrol (timeline)
' build ID array
|-
selected(i) = Selection(0).SubComponent.ComponentCollection(i).index
| [7] get keyframes
logmessage selected(i)
| [8] message box
next
| [9] input box
|-
' change sort order by replacing ">" with "<"
| [10] getting the desktop path
For i = 0 To poly_count - 1
| [11] selecting a folder
For j = 0 To poly_count - 1
| [12] selecting a file of a specific type
If selected(i) > selected(j) Then
|-
tmp = selected(i)
| [13] reading out environment variables
selected(i) = selected(j)
| [14] check if file exist
selected(j) = tmp
| [15] check if folder exist + create folder
End If
|-
Next
| [16] write xml file
Next
| [17] read xml file
logmessage "Show sorted ID."
| [18] read xml file (attribute/content-based)
|-
for i=0 to poly_count - 1
| [19] create txt file + write lines
logmessage selected(i)
| [20] read txt file
next
| [21] check onisplit version
 
|-
 
| [22] call CMD, e.g. to use onisplit
  ' '''[4] converting euler rotations (in degrees) to quaternions'''
| [23] building forms in ModTool
| [24] UserDataBlob
|-
| [25] getting the position of points (with selection mode point)
| [26] getting the position of points (with selection mode object)
| [27] getting and setting the position of points (without selection)
|-
| [28] getting the rotation and position of selected objects
| [29] getting the rotation and position of not selected objects
| [30] decrypting merged integer flags
|-
| [31] disabling PPG popups
| [32] disabling logmessages
| [33] getting bounding box values
|-
| [34] open an explorer window
| [35] working with hierarchies
| [36] drag and drop (DnD) support for specific oni files
|-
| [37] dae export
| [38] fbx export
| [39] import an image clip only once
|-
| [40] layers
| [41] onisplit update
| [42] get vertex color
|-
| [43] get all used textures
| [44] get global point position
|
|}
 
 
  ' '''[1] creating and extending an array'''
   
   
dim x, y, z, dRotation, qRotation
x = 90
y = 0
z = 0
   
   
  set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z))  
  ' creating an array
  set qRotation = XSIMath.CreateQuaternion()  
' () sets the number of array items
redim nums (3)
for i = 0 to 3
nums(i) = (i)
next
logmessage "output all items"
for each n in nums
logmessage n
next
logmessage "first array item holds: " & lbound(nums)
  logmessage "last array item holds: " & ubound(nums)
logmessage "-------------------------------------"
   
   
dRotation.GetQuaternion (qRotation)
' extending an array
LogMessage qRotation.W
' use "preserve" to keep the old content
LogMessage qRotation.X
' find next higher value
LogMessage qRotation.Y
plus_one = ubound(nums) + 1
LogMessage qRotation.Z
redim preserve nums (plus_one)
' INFO : 0,707106781186548
  nums(plus_one) = 4
' INFO : 0,707106781186547
' INFO : 0
' INFO : 0
 
 
  ' '''[5] converting quaternions to euler rotations (in degrees)'''
   
   
  dim qW, qX, qY, qZ, qRotation, x, y, z
  for each n in nums
logmessage n
next
logmessage "first array item holds: " & lbound(nums)
logmessage "last array item holds: " & ubound(nums)
   
   
qW = 0.707106781186548
qX = 0.707106781186547
qY = 0
qZ = 0
   
   
  set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
  ' INFO : output all items
   
  ' INFO : 0
qRotation.GetXYZAngleValues x, y, z
' INFO : 1
logmessage XSIMath.RadiansToDegrees(x)
' INFO : 2
logmessage XSIMath.RadiansToDegrees(y)
' INFO : 3
logmessage XSIMath.RadiansToDegrees(z)
' INFO : first array item holds: 0
' INFO : 89,9999999999999
' INFO : last array item holds: 3
' INFO : 0
' INFO : -------------------------------------
' INFO : 0
' INFO : 0
' INFO : 1
' INFO : 2
' INFO : 3
' INFO : 4
' INFO : first array item holds: 0
' INFO : last array item holds: 4




  ' '''[6] get and set values of the playcontrol (timeline)'''
  ' '''[2] check selection mode '''
   
   
  ' timeline variables (tl)
  checkfilter
  dim tlStart, tlEnd, tlCurrent
  'use sub to make use of the exit command
   
   
  ' get values
  sub checkfilter
tlStart = GetValue ("PlayControl.In")
Select Case Selection.Filter.Name
tlEnd = GetValue ("PlayControl.Out")
'caution: case-sensitive
tlCurrent = GetValue ("PlayControl.Current")
   
   
' set values
Case "object"
SetValue ("PlayControl.In"), 7
logmessage "object mode"
SetValue ("PlayControl.Out"), 70
Case "Edge"
SetValue ("PlayControl.Current"), 14
logmessage "edge mode"
Case "Vertex"
logmessage "point mode"
Case "Polygon"
logmessage "polygon mode"
Case Else
logmessage "unknown mode"
exit sub
End Select
end sub




  ' '''[7] get keyframes'''
  ' '''[3] getting the IDs of selected polygons plus sorting them'''
   
   
  ' keyframe counting code like "selection(0).rotx.Source.Keys.count" will produce an error if no keys exist
  poly_count = Selection(0).SubComponent.ComponentCollection.count
  ' that's why we need to catch possible errors for each counting
logmessage "poly_count: " & poly_count
redim selected (poly_count)
logmessage "Show ID as it is."
  for i=0 to poly_count - 1
' build ID array
selected(i) = Selection(0).SubComponent.ComponentCollection(i).index
logmessage selected(i)
next
   
   
  on error resume next
  ' change sort order by replacing ">" with "<"
  logmessage "--------------------------------------------------"
For i = 0 To poly_count - 1
logmessage "X rotation keys: " & selection(0).rotx.Source.Keys.count
  For j = 0 To poly_count - 1
for each k in selection(0).rotx.Source.Keys
If selected(i) > selected(j) Then
  logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
tmp = selected(i)
  next
selected(i) = selected(j)
  if err <> 0 then
selected(j) = tmp
logmessage "no X rotation keys"
  End If
end if
  Next
on error goto 0
  Next
logmessage "Show sorted ID."
   
   
  on error resume next
  for i=0 to poly_count - 1
logmessage "--------------------------------------------------"
  logmessage selected(i)
  logmessage "Y rotation keys: " & selection(0).roty.Source.Keys.count
next
for each k in selection(0).roty.Source.Keys
 
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
 
next
' '''[4] converting euler rotations (in degrees) to quaternions'''
  if err <> 0 then
logmessage "no Y rotation keys"
dim x, y, z, dRotation, qRotation
  end if
  x = 90
  on error goto 0
  y = 0
  z = 0
   
   
  on error resume next
  set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z))  
logmessage "--------------------------------------------------"
set qRotation = XSIMath.CreateQuaternion()
logmessage "Z rotation keys: " & selection(0).rotz.Source.Keys.count
for each k in selection(0).rotz.Source.Keys
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
next
if err <> 0 then
logmessage "no Z rotation keys"
end if
on error goto 0
   
   
on error resume next
dRotation.GetQuaternion (qRotation)
  logmessage "--------------------------------------------------"
  LogMessage qRotation.W
  logmessage "X position keys: " & selection(0).posx.Source.Keys.count
  LogMessage qRotation.X
  for each k in selection(0).posx.Source.Keys
LogMessage qRotation.Y
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
LogMessage qRotation.Z
  next
  ' INFO : 0,707106781186548
if err <> 0 then
' INFO : 0,707106781186547
logmessage "no X position keys"
' INFO : 0
end if
  ' INFO : 0
  on error goto 0
 
 
  ' '''[5] converting quaternions to euler rotations (in degrees)'''
   
   
  on error resume next
  dim qW, qX, qY, qZ, qRotation, x, y, z
logmessage "--------------------------------------------------"
logmessage "Y position keys: " & selection(0).posy.Source.Keys.count
for each k in selection(0).posy.Source.Keys
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
next
if err <> 0 then
logmessage "no Y position keys"
end if
on error goto 0
   
   
on error resume next
  qW = 0.707106781186548
logmessage "--------------------------------------------------"
  qX = 0.707106781186547
  logmessage "Z position keys: " & selection(0).posz.Source.Keys.count
  qY = 0
  for each k in selection(0).posz.Source.Keys
  qZ = 0
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
  next
if err <> 0 then
  logmessage "no Z position keys"
end if
on error goto 0
   
   
set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
   
   
' output example (0 rot keys, 2 pos keys):
qRotation.GetXYZAngleValues x, y, z
logmessage XSIMath.RadiansToDegrees(x)
' INFO : --------------------------------------------------
logmessage XSIMath.RadiansToDegrees(y)
' INFO : no X rotation keys
logmessage XSIMath.RadiansToDegrees(z)
' INFO : --------------------------------------------------
' INFO : 89,9999999999999
' INFO : no Y rotation keys
' INFO : 0
' INFO : --------------------------------------------------
' INFO : 0
' INFO : no Z rotation keys
' INFO : --------------------------------------------------
' INFO : X position keys: 2
' INFO : index: 0 // time: 2 // value: 5,14534318031942
' INFO : index: 1 // time: 6 // value: 8,2411504340802
' INFO : --------------------------------------------------
' INFO : Y position keys: 2
' INFO : index: 0 // time: 2 // value: 0,365325291829147
' INFO : index: 1 // time: 6 // value: 1,11923927115289
' INFO : --------------------------------------------------
' INFO : Z position keys: 2
' INFO : index: 0 // time: 2 // value: 1,96103417177471
' INFO : index: 1 // time: 6 // value: 1,88564277384233




  ' '''[8] message box'''
  ' '''[6] get and set values of the playcontrol (timeline)'''
   
   
  [http://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_om/XSIUIToolkit.MsgBox.html,topicNumber=si_om_XSIUIToolkit_MsgBox_html msgbox] "message", , "title"
  ' timeline variables (tl)
 
dim tlStart, tlEnd, tlCurrent
 
' '''[9] input box'''
   
   
  logmessage inputbox ("message", "title" , "pre-entered content")
  ' get values
 
tlStart = GetValue ("PlayControl.In")
 
tlEnd = GetValue ("PlayControl.Out")
' '''[10] getting the desktop path'''
tlCurrent = GetValue ("PlayControl.Current")
   
   
  ' this can be useful for ''default locations'' like when selecting a folder
  ' set values
  DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
  SetValue ("PlayControl.In"), 7
  logmessage DesktopPath
SetValue ("PlayControl.Out"), 70
  SetValue ("PlayControl.Current"), 14




  ' '''[11] selecting a folder'''
  ' '''[7] get keyframes'''
   
   
  ' the default path is in this case my desktop
  ' keyframe counting code like "selection(0).rotx.Source.Keys.count" will produce an error if no keys exist
  ' to make it work on other systems, you would need the code of "getting the desktop path" section and replace my path with the ''DesktopPath''
  ' that's why we need to catch possible errors for each counting
logmessage XSIUIToolkit.PickFolder("C:\Users\RMM\Desktop\", "title" )
 
on error resume next
 
logmessage "--------------------------------------------------"
  ' '''[12] selecting a file of a specific type'''
logmessage "X rotation keys: " & selection(0).rotx.Source.Keys.count
for each k in selection(0).rotx.Source.Keys
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
next
if err <> 0 then
logmessage "no X rotation keys"
end if
  on error goto 0
   
   
  set oFileBrowser = XSIUIToolkit.FileBrowser
  on error resume next
oFileBrowser.DialogTitle = "Select an image file (png/tga/jpg)"  
logmessage "--------------------------------------------------"
' (default folder)
logmessage "Y rotation keys: " & selection(0).roty.Source.Keys.count
oFileBrowser.InitialDirectory = "c:\"
for each k in selection(0).roty.Source.Keys
oFileBrowser.Filter = "JPEG (*.jpg)|*.jpg|PNG (*.png)|*.png| Targa (*.tga)|*.tga||"
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
oFileBrowser.ShowOpen
next
If oFileBrowser.FilePathName <> "" Then
  if err <> 0 then
logmessage "User selected " & oFileBrowser.FilePathName
  logmessage "no Y rotation keys"
  Else
  end if
  logmessage "User pressed cancel"
  on error goto 0
  End If
 
 
  ' '''[13] reading out environment variables'''
   
   
  ' some infos about env vars: [http://softimage.wiki.softimage.com/xsidocs/config_envirovars.htm (1)], [http://softimage.wiki.softimage.com/xsidocs/EnvVars_SettingandUsingEnvironmentVariables.htm (2)], [http://softimage.wiki.softimage.com/xsidocs/EnvVars_EnvironmentVariableReference.htm (3)]
  on error resume next
' those variables are stored inside the setenv.bat, two disadvantages:
logmessage "--------------------------------------------------"
' adding or editing those vars appears to not work in vbs or I just did it wrong
logmessage "Z rotation keys: " & selection(0).rotz.Source.Keys.count
' anyway, new vars can only be read out after app restart
for each k in selection(0).rotz.Source.Keys
' therefore let's concentrate on reading out existing ones and then how to create our own via txt files (example: [https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/Oni_env_vars.txt Oni_env_vars.txt])
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
  ' code for reading out:
next
logmessage XSIUtils.ResolvePath("$SI_HOME/")
  if err <> 0 then
 
logmessage "no Z rotation keys"
 
end if
  ' '''[14] check if file exist'''
  on error goto 0
   
   
  Set objFSO = CreateObject("Scripting.FileSystemObject")
  on error resume next
If objFSO.FileExists ("C:\folder\file.txt") then
logmessage "--------------------------------------------------"
  logmessage "File exists."
logmessage "X position keys: " & selection(0).posx.Source.Keys.count
  else
  for each k in selection(0).posx.Source.Keys
  logmessage "File doesn't exist."
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
next
  if err <> 0 then
  logmessage "no X position keys"
  end if
  end if
 
  on error goto 0
 
  ' '''[15] check if folder exist + create folder'''
   
   
  dim strDirectory
  on error resume next
strDirectory = "C:\test"
logmessage "--------------------------------------------------"
logmessage "Y position keys: " & selection(0).posy.Source.Keys.count
Set objFSO = CreateObject("Scripting.FileSystemObject")
for each k in selection(0).posy.Source.Keys
If objFSO.FolderExists(strDirectory) Then
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
    Set objFolder = objFSO.GetFolder(strDirectory)
next
    logmessage strDirectory & " already exists"
  if err <> 0 then
  Else
logmessage "no Y position keys"
    Set objFolder = objFSO.CreateFolder(strDirectory)
  end if
    logmessage "created folder " & strDirectory
  on error goto 0
  End If
 
 
  ' '''[16] write xml file'''
   
   
  FolderName = CreateObject("WScript.Shell").SpecialFolders("Desktop")
  on error resume next
FileName = "test"
logmessage "--------------------------------------------------"
FilePath = FolderName & "\OBAN" & FileName & ".xml"
logmessage "Z position keys: " & selection(0).posz.Source.Keys.count
   
for each k in selection(0).posz.Source.Keys
Set oFS = CreateObject("Scripting.FileSystemObject")
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
  Set objXMLFile = oFS.OpenTextFile(FilePath, 2, True, 0)
next
  if err <> 0 then
logmessage "no Z position keys"
  end if
on error goto 0
   
   
' quote sign in a string needs two quote signs
objXMLFile.WriteLine "<?xml version=""1.0"" encoding=""utf-8""?>"
objXMLFile.WriteLine "<Oni>"
objXMLFile.WriteLine "..."
objXMLFile.Close
' '''[17] read xml file'''
   
   
  Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
  ' output example (0 rot keys, 2 pos keys):
xmlDoc.Async = "False"
path = "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level19_Final\BINACJBOWeapon.xml"
xmlDoc.Load( path )
   
   
  Set colPosition = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Position" )
  ' INFO : --------------------------------------------------
  Set colRotation = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Rotation" )
' INFO : no X rotation keys
  Set colClass = xmlDoc.selectNodes( "Oni/Objects/WEAP/OSD/Class" )
' INFO : --------------------------------------------------
   
' INFO : no Y rotation keys
loop_count = xmlDoc.selectNodes( "Oni/Objects/WEAP").length
' INFO : --------------------------------------------------
  logmessage "found " & loop_count & " weapons:"
' INFO : no Z rotation keys
logmessage "================"
' INFO : --------------------------------------------------
for i=0 to loop_count - 1
' INFO : X position keys: 2
logmessage colClass.item(i).text
' INFO : index: 0 // time: 2 // value: 5,14534318031942
logmessage colPosition.item(i).text
  ' INFO : index: 1 // time: 6 // value: 8,2411504340802
logmessage colRotation.item(i).text
' INFO : --------------------------------------------------
logmessage "----------------"
' INFO : Y position keys: 2
  next
  ' INFO : index: 0 // time: 2 // value: 0,365325291829147
  ' INFO : index: 1 // time: 6 // value: 1,11923927115289
  ' INFO : --------------------------------------------------
' INFO : Z position keys: 2
' INFO : index: 0 // time: 2 // value: 1,96103417177471
' INFO : index: 1 // time: 6 // value: 1,88564277384233
 
 
  ' '''[8] message box'''
   
   
[https://download.autodesk.com/global/docs/softimage2013/en_us/sdkguide/index.html?url=si_om/XSIUIToolkit.MsgBox.html,topicNumber=si_om_XSIUIToolkit_MsgBox_html msgbox] "message", , "title"
' '''[9] input box'''
   
   
  ' INFO : found 2 weapons:
  logmessage inputbox ("message", "title" , "pre-entered content")
' INFO : ================
' INFO : w5_sbg
' INFO : 23.16747 84.8193359 757.1958
' INFO : 0 0 0
' INFO : ----------------
' INFO : w4_psm
' INFO : 18.1105652 84.8193359 749.121
' INFO : 0 0 0
' INFO : ----------------




' '''[10] getting the desktop path'''
' this can be useful for ''default locations'' like when selecting a folder
DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
logmessage DesktopPath


  ' '''[18] read xml file (attribute/content-based)'''
 
  ' '''[11] selecting a folder'''
   
   
  Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
  ' the default path is in this case my desktop
' to make it work on other systems, you would need the code of "getting the desktop path" section and replace my path with the ''DesktopPath''
logmessage XSIUIToolkit.PickFolder("C:\Users\RMM\Desktop\", "title" )
 
 
' '''[12] selecting a file of a specific type'''
   
   
  ' tells the program to wait until the file was loaded conpletely
  set oFileBrowser = XSIUIToolkit.FileBrowser
  xmlDoc.Async = "False"
  oFileBrowser.DialogTitle = "Select an image file (png/tga/jpg)"  
  xmlDoc.Load( "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level0_Final\BINAEINOimpact_effects.xml" )
  ' (default folder)
oFileBrowser.InitialDirectory = "c:\"
oFileBrowser.Filter = "JPEG (*.jpg)|*.jpg|PNG (*.png)|*.png| Targa (*.tga)|*.tga||"
oFileBrowser.ShowOpen
If oFileBrowser.FilePathName <> "" Then
logmessage "User selected " & oFileBrowser.FilePathName
Else
logmessage "User pressed cancel"
End If
 
 
' '''[13] reading out environment variables'''
   
   
' some infos about env vars: [http://web.archive.org/web/20170309190841/http://softimage.wiki.softimage.com/xsidocs/config_envirovars.htm (1)], [http://web.archive.org/web/20180416194626/http://softimage.wiki.softimage.com/xsidocs/EnvVars_SettingandUsingEnvironmentVariables.htm (2)], [http://web.archive.org/web/20170617095054/http://softimage.wiki.softimage.com/xsidocs/EnvVars_EnvironmentVariableReference.htm (3)]
' those variables are stored inside the setenv.bat, two disadvantages:
' adding or editing those vars appears to not work in vbs or I just did it wrong
' anyway, new vars can only be read out after app restart
' therefore let's concentrate on reading out existing ones and then how to create our own via txt files (example: <nowiki>https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/Oni_env_vars.txt Oni_env_vars.txt</nowiki> (dead link))
' code for reading out:
logmessage XSIUtils.ResolvePath("$SI_HOME/")
' '''[14] check if file exist'''
   
   
  ' example 1 ('''we use this one''')
  Set objFSO = CreateObject("Scripting.FileSystemObject")
' nodes only with a certain attribute and conent will be collected
If objFSO.FileExists ("C:\folder\file.txt") then
logmessage "File exists."
else
logmessage "File doesn't exist."
end if
 
 
' '''[15] check if folder exist + create folder'''
   
   
  ' content-based search: node [child_node = 'content']
  dim strDirectory
  ' attribute-based search: node [@attribute_name = 'attribute_value']
  strDirectory = "C:\test"
   
   
  ' if things get too long an underscore _ can be used to make a linebreak
  Set objFSO = CreateObject("Scripting.FileSystemObject")
  '''set w1_tap = xmlDoc.selectNodes _'''
  If objFSO.FolderExists(strDirectory) Then
'''( "Oni/ImpactEffects/Impact[@Name = 'w1_tap']/Material[@Name = 'Unbreak_Glass']/ImpactEffect [Component = 'Damage']" )'''
    Set objFolder = objFSO.GetFolder(strDirectory)
    logmessage strDirectory & " already exists"
Else
    Set objFolder = objFSO.CreateFolder(strDirectory)
    logmessage "created folder " & strDirectory
End If
   
   
' ### or simply:
dir = "C:\test"
XSIUtils.EnsureFolderExists (dir)
' '''[16] write xml file'''
   
   
  ' example 2
  FolderName = CreateObject("WScript.Shell").SpecialFolders("Desktop")
  ' use "or" for a search of various material
  FileName = "test"
  'set w1_tap = xmlDoc.selectNodes _
  FilePath = FolderName & "\OBAN" & FileName & ".xml"
' ( "Oni/ImpactEffects/Impact/Material[@Name = 'Default' or @Name = 'Character']/ImpactEffect" )
   
   
Set oFS = CreateObject("Scripting.FileSystemObject")
Set objXMLFile = oFS.OpenTextFile(FilePath, 2, True, 0)
   
   
  ' example 3
  ' quote sign in a string needs two quote signs
  ' use (|) for a search of various content, e.g. ''<ImpactEffect><Modifier>Light'' plus ''<ImpactEffect><Modifier>Medium''
  objXMLFile.WriteLine "<?xml version=""1.0"" encoding=""utf-8""?>"
  'set w1_tap = xmlDoc.selectNodes _
objXMLFile.WriteLine "<Oni>"
  ' ( "Oni/ImpactEffects/Impact/Material/(ImpactEffect [Modifier = 'Light'] | ImpactEffect [Modifier = 'Medium'])" )
objXMLFile.WriteLine "..."
objXMLFile.Close
 
 
' '''[17] read xml file'''
   
Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
xmlDoc.Async = "False"
  path = "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level19_Final\BINACJBOWeapon.xml"
xmlDoc.Load( path )
   
   
Set colPosition = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Position" )
Set colRotation = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Rotation" )
Set colClass =    xmlDoc.selectNodes( "Oni/Objects/WEAP/OSD/Class" )
   
   
loop_count = xmlDoc.selectNodes( "Oni/Objects/WEAP").length
   
   
  for each element in w1_tap
  logmessage "found " & loop_count & " weapons:"
logmessage "Impact: " & element.parentNode.parentNode.getAttribute("Name")
logmessage "================"
logmessage "Material: " & element.parentNode.getAttribute("Name")
for i=0 to loop_count - 1
'split string into pices as array, space is used as seperator by default
'element 0 = X; element 1 = Y; element 2 = Z
pos = split(colPosition.item(i).text)
rot = split(colRotation.item(i).text)
 
 
  ' outputs "tag name: tag content"
  ' Mod Tool wants comma instead of point for decimal seperator
  logmessage element.childNodes(0).nodename & ": " & element.childNodes(0).text
  pos(0) = replace (pos(0), ".", ",")
  logmessage element.childNodes(1).nodename & ": " & element.childNodes(1).text
  pos(1) = replace (pos(1), ".", ",")
logmessage "-----------------------------------"
pos(2) = replace (pos(2), ".", ",")
 
 
  'check for sounds
  rot(0) = replace (rot(0), ".", ",")
if element.childNodes(2).childNodes.length = 0 then
rot(1) = replace (rot(1), ".", ",")
logmessage "sound not present"
rot(2) = replace (rot(2), ".", ",")
else
logmessage "sound is present"
logmessage "-----------------------------------"
logmessage element.childNodes(2).childNodes(0).nodename & ": " & element.childNodes(2).childNodes(0).text
logmessage element.childNodes(2).childNodes(1).nodename & ": " & element.childNodes(2).childNodes(1).text
logmessage element.childNodes(2).childNodes(2).nodename & ": " & element.childNodes(2).childNodes(2).text
logmessage element.childNodes(2).childNodes(3).nodename & ": " & element.childNodes(2).childNodes(3).text
end if
logmessage "-----------------------------------"
   
   
  ' check for particle
  logmessage colClass.item(i).text
if element.childNodes(3).childNodes.length = 0 then
logmessage pos(0) & " " & pos(1) & " " & pos(2)
  logmessage "no particle present"
  logmessage rot(0) & " " & rot(1) & " " & rot(2)
  else
logmessage "----------------"
logmessage "number of particle: " & element.childNodes(3).childNodes.length
  next
for each particle_section in element.childNodes(3).childNodes
logmessage "-----------------------------------"
for each particle_tag in particle_section.childnodes
logmessage particle_tag.nodename & ": " & particle_tag.text
next
next
end if
logmessage "==================================="
  next
   
   
   
   
  ' INFO : Impact: w1_tap
  ' INFO : found 2 weapons:
  ' INFO : Material: Unbreak_Glass
  ' INFO : ================
  ' INFO : Component: Damage
  ' INFO : w5_sbg
  ' INFO : Modifier: Any
  ' INFO : 23,16747 84,8193359 757,1958
  ' INFO : -----------------------------------
  ' INFO : 0 0 0
' INFO : sound not present
  ' INFO : ----------------
  ' INFO : -----------------------------------
  ' INFO : w4_psm
  ' INFO : number of particle: 2
  ' INFO : 18,1105652 84,8193359 749,121
  ' INFO : -----------------------------------
  ' INFO : 0 0 0
  ' INFO : Name: d__GLASSCRACK
  ' INFO : ----------------
' INFO : Orientation: 0
  ' INFO : Location: 4
' INFO : Decal1: false
' INFO : Decal2: true
' INFO : -----------------------------------
' INFO : Name: w1_tap_x03
' INFO : Orientation: 0
' INFO : Location: 1
' INFO : Offset: -1
' INFO : ===================================






  ' '''[19] create txt file + write lines'''
  ' '''[18] read xml file (attribute/content-based)'''
   
   
txt_location = "C:\Softimage\Softimage_Mod_Tool_7.5\test.txt"
  Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
  Set fso = CreateObject ("Scripting.FileSystemObject")
Set wText = fso.CreateTextFile (txt_location, 1)
wText.WriteLine "I'm a test file."
wText.WriteLine "Yo!"
wText.Close
 
 
' '''[20] read txt file'''
   
   
  Set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Softimage\Softimage_Mod_Tool_7.5\test.txt", 1)
  ' tells the program to wait until the file was loaded conpletely
xmlDoc.Async = "False"
xmlDoc.Load( "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level0_Final\BINAEINOimpact_effects.xml" )
   
   
do while not objFileToRead.AtEndOfStream
    strLine = objFileToRead.ReadLine()
    logmessage strLine
loop
' INFO : I'm a test file.
' INFO : Yo!
   
   
  objFileToRead.Close
  ' example 1 ('''we use this one''')
Set objFileToRead = Nothing
' nodes only with a certain attribute and conent will be collected
 
 
' '''[21] check onisplit version'''
   
   
  Set objFSO = CreateObject("Scripting.FileSystemObject")
  ' content-based search: node [child_node = 'content']
  logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")
  ' attribute-based search: node [@attribute_name = 'attribute_value']
   
   
  ' result looks like this:
  ' if things get too long an underscore _ can be used to make a linebreak
  ' INFO : 0.9.59.0
  '''set w1_tap = xmlDoc.selectNodes _'''
 
'''( "Oni/ImpactEffects/Impact[@Name = 'w1_tap']/Material[@Name = 'Unbreak_Glass']/ImpactEffect [Component = 'Damage']" )'''
 
' '''[22] call CMD, e.g. to lunch onisplit or the game'''
   
   
' relative path
   
   
  ' the "GameDataFolder" isn't inside the "install" folder
  ' example 2
  ' so we will use ..\ to go one folder backwards
' use "or" for a search of various material
  'set w1_tap = xmlDoc.selectNodes _
' ( "Oni/ImpactEffects/Impact/Material[@Name = 'Default' or @Name = 'Character']/ImpactEffect" )
   
   
' additional quote signs tells the program where the
' paths strings start and end in case the path contains spaces
   
   
  ' if you are going to use the xml file right after its extraction (which is likely)
  ' example 3
  ' then the "/wait" argument inside the onisplit_action string is important
' use (|) for a search of various content, e.g. ''<ImpactEffect><Modifier>Light'' plus ''<ImpactEffect><Modifier>Medium''
' without it the code would continue and might try to read the not existing xml file and produce an error
'set w1_tap = xmlDoc.selectNodes _
  ' ( "Oni/ImpactEffects/Impact/Material/(ImpactEffect [Modifier = 'Light'] | ImpactEffect [Modifier = 'Medium'])" )
   
   
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
output_folder = """..\GameDataFolder"""
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "relative path: " & onisplit_action
' expected logmessage:
' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
   
   
   
   
  ' absolute path
  for each element in w1_tap
logmessage "Impact: " & element.parentNode.parentNode.getAttribute("Name")
logmessage "Material: " & element.parentNode.getAttribute("Name")
' outputs "tag name: tag content"
logmessage element.childNodes(0).nodename & ": " & element.childNodes(0).text
logmessage element.childNodes(1).nodename & ": " & element.childNodes(1).text
logmessage "-----------------------------------"
'check for sounds
if element.childNodes(2).childNodes.length = 0 then
logmessage "sound not present"
else
logmessage "sound is present"
logmessage "-----------------------------------"
logmessage element.childNodes(2).childNodes(0).nodename & ": " & element.childNodes(2).childNodes(0).text
logmessage element.childNodes(2).childNodes(1).nodename & ": " & element.childNodes(2).childNodes(1).text
logmessage element.childNodes(2).childNodes(2).nodename & ": " & element.childNodes(2).childNodes(2).text
logmessage element.childNodes(2).childNodes(3).nodename & ": " & element.childNodes(2).childNodes(3).text
end if
logmessage "-----------------------------------"
   
   
'adapt paths so it works on your computer
' check for particle
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
if element.childNodes(3).childNodes.length = 0 then
input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
  logmessage "no particle present"
output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
else
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "number of particle: " & element.childNodes(3).childNodes.length
logmessage "absolute path: " & onisplit_action
for each particle_section in element.childNodes(3).childNodes
' expected logmessage:
logmessage "-----------------------------------"
' <small>INFO : absolute path: cmd /C start OniSplit.exe -extract:xml "F:\Program Files (x86)\Oni\Edition\GameDataFolder" "F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"</small>
for each particle_tag in particle_section.childnodes
  XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
logmessage particle_tag.nodename & ": " & particle_tag.text
next
next
end if
logmessage "==================================="
  next
   
   
   
   
  ' you can also lunch bat files
  ' INFO : Impact: w1_tap
   
' INFO : Material: Unbreak_Glass
  onibat = "cmd /C start run_wind.bat"
' INFO : Component: Damage
  onilocation = "F:\Program Files (x86)\Oni\Edition"
' INFO : Modifier: Any
  XSIUtils.LaunchProcess onibat, 0, onilocation
' INFO : -----------------------------------
 
' INFO : sound not present
  ' alternative to cmd: call onisplit via winmgmts
' INFO : -----------------------------------
' INFO : number of particle: 2
' INFO : -----------------------------------
' INFO : Name: d__GLASSCRACK
  ' INFO : Orientation: 0
  ' INFO : Location: 4
' INFO : Decal1: false
' INFO : Decal2: true
' INFO : -----------------------------------
  ' INFO : Name: w1_tap_x03
  ' INFO : Orientation: 0
' INFO : Location: 1
  ' INFO : Offset: -1
' INFO : ===================================
 
 
' '''[] remove xml nodes'''
 
Set objXMLDoc = CreateObject("Microsoft.XMLDOM")
objXMLDoc.async = False
   
   
  ' slightly modified code from [http://blogs.technet.com/b/heyscriptingguy/archive/2006/12/08/how-can-i-start-a-process-and-then-wait-for-the-process-to-end-before-terminating-the-script.aspx that site]
  Dim XMLFile
  ' ''logmessage "onisplit finished."'' will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers
  XMLFile = "C:\Softimage\Softimage_Mod_Tool_7.5\OniLevels\ONLV\test\ONLVAirport.xml"
' if you are going to use this method consider to extent the code to check if input file and output directory exist
  objXMLDoc.load(XMLFile)
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
  Set nodes = objXMLDoc.selectNodes("Oni/CRSA/*")
osp_action = "-extract:xml"
  For Each node In nodes
  osp_output = """C:\OniAE\Edition\GameDataFolder"""
    node.parentNode.removeChild(node)
  osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
  Next
  osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
  logmessage osp_total
   
   
  strComputer = "."
  objXMLDoc.Save(XMLFile)
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objWMIService.Create osp_total, null, null, intProcessID
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' wait 3 second to find event - should be enough time for single actions on a slow computer
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")
Do Until i = 1
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
        i = 1
    End If
Loop
logmessage "onisplit finished."
' now you can work with the extracted xml file




{| border=0 cellpadding=0 style="float:right"
  ' '''[19] create txt file + write lines'''
| [http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/XSI_modding/PPG_zpsda38090b.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/XSI_modding/PPG_tn_zps84e07fe6.png]
|}
 
  ' '''[23] building a form (PPG)'''
   
   
  dim oPSet, oPPGLayout, oItem, PPG_exist
  txt_location = "C:\Softimage\Softimage_Mod_Tool_7.5\test.txt"
Set fso = CreateObject ("Scripting.FileSystemObject")
Set wText = fso.CreateTextFile (txt_location, 1)
wText.WriteLine "I'm a test file."
wText.WriteLine "Yo!"
wText.Close
 
 
' '''[20] read txt file'''
   
   
  ' set check value to 0
  Set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Softimage\Softimage_Mod_Tool_7.5\test.txt", 1)
PPG_exist = 0
   
   
  ' check if PPG exists
  do while not objFileToRead.AtEndOfStream
for each prop in ActiveProject.ActiveScene.Root.Properties
    strLine = objFileToRead.ReadLine()
logmessage prop.name
    logmessage strLine
if instr(1, prop.name, "my_new_PPG") > 0 then
loop
PPG_exist = 1
' INFO : I'm a test file.
logmessage "found " & """" & prop.name & """"
  ' INFO : Yo!
end if
  next
   
   
  ' create PPG if it doesn't exist
  objFileToRead.Close
  if PPG_exist = 0 then
  Set objFileToRead = Nothing
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_new_PPG")
 
set oPPGLayout = oPSet.PPGLayout
 
' '''[21] check onisplit version'''
' setup PPG parameters
' ###################################################################################
Set objFSO = CreateObject("Scripting.FileSystemObject")
' create checkbox and remove key symboles by setting parameter "Animatable" to false
logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")
oPSet.AddParameter3 "Check1", siBool, 0, , , false
oPSet.AddParameter3 "Check2", siBool, 0, , , false
' result looks like this:
oPSet.AddParameter3 "Check3", siBool, 1, , , false
' INFO : 0.9.59.0
oPSet.AddParameter3 "Check4", siBool, 1, , , false
 
oPSet.AddParameter3 "text1", siString
 
' last parameter of "int1" is set to ReadOnly
' '''[22] call CMD, e.g. to lunch onisplit or the game'''
oPSet.AddParameter3 "int1", siInt2, , , , false, 1
oPSet.AddParameter3 "int2", siInt2, , , , false, 0
' relative path
' add PPG items
' the "GameDataFolder" isn't inside the "install" folder
' ###################################################################################
' so we will use ..\ to go one folder backwards
oPPGLayout.AddItem "text1", "Hi there!"
oPPGLayout.AddItem "int1", "number 1"
' additional quote signs tells the program where the
oPPGLayout.AddItem "int2", "number 2"
' paths strings start and end in case the path contains spaces
oPPGLayout.AddGroup "4 checkboxes", true
oPPGLayout.AddRow
' if you are going to use the xml file right after its extraction (which is likely)
oPPGLayout.AddItem "Check1", "C1"
' then the "/wait" argument inside the onisplit_action string is important
oPPGLayout.AddItem "Check3", "C3"
' without it the code would continue and might try to read the not existing xml file and produce an error
oPPGLayout.EndRow
oPPGLayout.AddRow
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
oPPGLayout.AddItem "Check2", "C2"
input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
oPPGLayout.AddItem "Check4", "C4"
output_folder = """..\GameDataFolder"""
oPPGLayout.EndRow
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
oPPGLayout.EndGroup
logmessage "relative path: " & onisplit_action
' expected logmessage:
oPPGLayout.AddButton("log_values", "log PPG values").setAttribute siUICX, 120
' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
oPPGLayout.Logic = "sub log_values_OnClicked" & vbCrlf & _
" logmessage ""text 1 = "" & getvalue(""my_new_PPG.text1"")" & vbCrlf & _
" logmessage ""number 1 = "" & getvalue(""my_new_PPG.int1"")" & vbCrlf & _
' absolute path
" logmessage ""C1 = "" & getvalue(""my_new_PPG.Check1"")" & vbCrlf & _
" end sub"
oPPGLayout.Language = "VBScript" 'Optional because this is the default
   
   
' open PPG
  'adapt paths so it works on your computer
InspectObj oPSet
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
  else
  input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
' open that PPG if it already exist
  output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
InspectObj "my_new_PPG"
  onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
  end if
  logmessage "absolute path: " & onisplit_action
 
' expected logmessage:
  ' set values from outside the PPG
' <small>INFO : absolute path: cmd /C start OniSplit.exe -extract:xml "F:\Program Files (x86)\Oni\Edition\GameDataFolder" "F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"</small>
  setvalue "my_new_PPG.text1", "any text could stand here"
  XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
  setvalue "my_new_PPG.int1", "42"
  setvalue "my_new_PPG.check1", true
   
   
' get values from outside the PPG
logmessage getvalue("my_new_PPG.text1")
logmessage getvalue("my_new_PPG.int1")
logmessage getvalue("my_new_PPG.check1")
{| border=0 cellpadding=0 style="float:right"
| [http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/UserDataBlob.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/UserDataBlob_tn.png]
|}
' '''[24] UserDataBlob'''
   
   
  ' you can attach data to objects, e.g. new properties for the scene root or Oni Trigger Volumes
  ' you can also lunch bat files
' the so-called "UserDataBlob" can't be saved in *.dae, so save the whole scene to keep your progress
   
   
  ' check if my property exist
  onibat = "cmd /C start run_wind.bat"
found_my_prop = 0
  onilocation = "F:\Program Files (x86)\Oni\Edition"
  set oProps = ActiveProject.ActiveScene.Root
  XSIUtils.LaunchProcess onibat, 0, onilocation
logmessage "root name: " & ActiveProject.ActiveScene.Root
 
  for each prop in oProps.Properties
  ' alternative to cmd: call onisplit via winmgmts
' remove apostroph in line beneath to log all root properties
  'LogMessage prop.Name
if instr(1, prop.Name, "my_prop") = 1 then
found_my_prop = 1
end if
  next
   
   
  ' prop doesn't exist, create it
  ' slightly modified code from [https://devblogs.microsoft.com/scripting/how-can-i-start-a-process-and-then-wait-for-the-process-to-end-before-terminating-the-script/ that site]
  if found_my_prop = 0 then
' ''logmessage "onisplit finished."'' will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers
' create property (must also have content, in this case "yes")
  ' if you are going to use this method consider to extent the code to check if input file and output directory exist
oProps.AddProperty( "UserDataBlob", , "my_prop" ).value = "yes"
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
logmessage "created my new prop"
osp_action = "-extract:xml"
  end if
osp_output = """C:\OniAE\Edition\GameDataFolder"""
osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
  logmessage osp_total
   
   
  ' prop exists, change/read its value
  strComputer = "."
  if found_my_prop = 1 then
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
' change property
objWMIService.Create osp_total, null, null, intProcessID
oProps.Properties( "my_prop" ).value = "no"
  Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' wait 3 second to find event - should be enough time for single actions on a slow computer
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")
   
   
' read property
Do Until i = 1
logmessage oProps.Properties( "my_prop" ).value
    Set objLatestProcess = colMonitoredProcesses.NextEvent
' if you want to read a property from another script be sure that it was already created
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
' reuse code under "check if my property exist"
        i = 1
  end if
    End If
Loop
logmessage "onisplit finished."
  ' now you can work with the extracted xml file
 


[[Image:XSI_Mod_Tool_PPG.png|thumb]]


  ' '''[25] getting the position of points (with selection mode point)'''
  ' '''[23] building a form (PPG)'''
   
   
  ' a point must be selected
  dim oPSet, oPPGLayout, oItem, PPG_exist
' gets xyz position of first selected point of the first selected object
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
logmessage Selection(0).SubComponent.ComponentCollection(0).position.z
 
 
' '''[26] getting the position of points (with selection mode object)'''
   
   
  ' an object must be selected
  ' set check value to 0
' gets xyz position of point 0 of the first selected object
  PPG_exist = 0
logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
logmessage selection(0).activeprimitive.geometry.Points(0).Position.z
 
 
' '''[27] getting and setting the position of points (without selection) right after object creation'''
   
   
  ' point positions are relative to the object's center
  ' check if PPG exists
  ' to get the absolute point positions add center to point
  for each prop in ActiveProject.ActiveScene.Root.Properties
  ' to set the absolute point positions subtract center from point
logmessage prop.name
if instr(1, prop.name, "my_new_PPG") > 0 then
PPG_exist = 1
logmessage "found " & """" & prop.name & """"
end if
  next
   
   
  set oRoot = application.activeproject.activescene.root
  ' create PPG if it doesn't exist
set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
if PPG_exist = 0 then
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_new_PPG")
' to test our code move center to somewhere else
set oPPGLayout = oPSet.PPGLayout
Translate oObj, 9, 11, 13, siRelative, siGlobal, siCtr, siXYZ, , , , , , , , , , 0
SaveKey oObj & ".kine.local.posx," & oObj & ".kine.local.posy," & oObj & ".kine.local.posz", 1, , , , True
' setup PPG parameters
' ###################################################################################
FreezeObj oObj
' create checkbox and remove key symboles by setting parameter "Animatable" to false
set oGeometry = oObj.activeprimitive.geometry
oPSet.AddParameter3 "Check1", siBool, 0, , , false
aPositions = oGeometry.Points.PositionArray
oPSet.AddParameter3 "Check2", siBool, 0, , , false
' get old position
oPSet.AddParameter3 "Check3", siBool, 1, , , false
'                                         (xyz, point)
oPSet.AddParameter3 "Check4", siBool, 1, , , false
logmessage "old point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
oPSet.AddParameter3 "text1", siString
logmessage "old point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
' last parameter of "int1" is set to ReadOnly
logmessage "old point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
oPSet.AddParameter3 "int1", siInt2, , , , false, 1
oPSet.AddParameter3 "int2", siInt2, , , , false, 0
' set new position
aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
' add PPG items
aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
' ###################################################################################
aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
oPPGLayout.AddItem "text1", "Hi there!"
oPPGLayout.AddItem "int1", "number 1"
' update the array
oPPGLayout.AddItem "int2", "number 2"
oGeometry.Points.PositionArray = aPositions
oPPGLayout.AddGroup "4 checkboxes", true
' get new position
oPPGLayout.AddRow
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
oPPGLayout.AddItem "Check1", "C1"
logmessage "new point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
oPPGLayout.AddItem "Check3", "C3"
logmessage "new point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
oPPGLayout.EndRow
oPPGLayout.AddRow
oPPGLayout.AddItem "Check2", "C2"
oPPGLayout.AddItem "Check4", "C4"
oPPGLayout.EndRow
oPPGLayout.EndGroup
oPPGLayout.AddButton("log_values", "log PPG values").setAttribute siUICX, 120
oPPGLayout.Logic = "sub log_values_OnClicked" & vbCrlf & _
" logmessage ""text 1 = "" & getvalue(""my_new_PPG.text1"")" & vbCrlf & _
" logmessage ""number 1 = "" & getvalue(""my_new_PPG.int1"")" & vbCrlf & _
" logmessage ""C1 = "" & getvalue(""my_new_PPG.Check1"")" & vbCrlf & _
" end sub"
oPPGLayout.Language = "VBScript" 'Optional because this is the default
   
   
  ' INFO : old point 0 posx: -4
' open PPG
  ' INFO : old point 0 posy: -4
InspectObj oPSet
  ' INFO : old point 0 posz: -4
  else
' open that PPG if it already exist
InspectObj "my_new_PPG"
end if
 
  ' set values from outside the PPG
setvalue "my_new_PPG.text1", "any text could stand here"
setvalue "my_new_PPG.int1", "42"
  setvalue "my_new_PPG.check1", true
   
   
  ' INFO : new point 0 posx: -7
  ' get values from outside the PPG
  ' INFO : new point 0 posy: -7
  logmessage getvalue("my_new_PPG.text1")
  ' INFO : new point 0 posz: -7
  logmessage getvalue("my_new_PPG.int1")
logmessage getvalue("my_new_PPG.check1")
 


[[Image:XSI_Mod_Tool_UserDataBlob.png|thumb]]


  ' '''[28] getting the scaling, rotation and position of selected objects'''
  ' '''[24] UserDataBlob'''
' you can attach data to objects, e.g. new properties for the scene root or Oni Trigger Volumes
' the so-called "UserDataBlob" can't be saved in *.dae, so save the whole scene to keep your progress
   
   
  logmessage "mesh name: " & selection(0)
  ' check if my property exist
  logmessage selection(0).sclx.value
  found_my_prop = 0
  logmessage selection(0).scly.value
  set oProps = ActiveProject.ActiveScene.Root
  logmessage selection(0).sclz.value
  logmessage "root name: " & ActiveProject.ActiveScene.Root
  logmessage selection(0).rotx.value
  for each prop in oProps.Properties
logmessage selection(0).roty.value
' remove apostroph in line beneath to log all root properties
logmessage selection(0).rotz.value
  'LogMessage prop.Name
logmessage selection(0).posx.value
if instr(1, prop.Name, "my_prop") = 1 then
logmessage selection(0).posy.value
found_my_prop = 1
logmessage selection(0).posz.value
end if
 
  next
 
  ' '''[29] getting the scaling, rotation, and position of not selected objects'''
   
   
  ' GetValue("NAME.kine.global.rotx")
  ' prop doesn't exist, create it
  ' NAME must be the exact mesh name
  if found_my_prop = 0 then
' let's say you want the data of one character's pelvis
' create property (must also have content, in this case "yes")
logmessage GetValue("pelvis.kine.global.sclx")
oProps.AddProperty( "UserDataBlob", , "my_prop" ).value = "yes"
logmessage GetValue("pelvis.kine.global.scly")
logmessage "created my new prop"
logmessage GetValue("pelvis.kine.global.sclz")
  end if
  logmessage GetValue("pelvis.kine.global.rotx")
  logmessage GetValue("pelvis.kine.global.roty")
' prop exists, change/read its value
logmessage GetValue("pelvis.kine.global.rotz")
  if found_my_prop = 1 then
  logmessage GetValue("pelvis.kine.global.posx")
' change property
logmessage GetValue("pelvis.kine.global.posy")
oProps.Properties( "my_prop" ).value = "no"
  logmessage GetValue("pelvis.kine.global.posz")
   
' read property
logmessage oProps.Properties( "my_prop" ).value
' if you want to read a property from another script be sure that it was already created
' reuse code under "check if my property exist"
  end if




  ' '''[30] decrypting merged integer flags'''
  ' '''[25] getting the position of points (with selection mode point)'''
   
   
  ' for example the [[XML:BINA/OBJC/TRGV|BINACJBOTrigger Volume.xml]] file has flags as strings for the <Flags> tag
  ' a point must be selected
  ' and integer values for the <Teams> tag whereby the values are merged to one value
' gets xyz position of first selected point of the first selected object
  ' so if <Teams> tag holds "101" we have to think what flags it holds, the flags can be broken down with following code
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
logmessage Selection(0).SubComponent.ComponentCollection(0).position.z
 
 
' '''[26] getting the position of points (with selection mode object)'''
  ' an object must be selected
' gets xyz position of point 0 of the first selected object
logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
logmessage selection(0).activeprimitive.geometry.Points(0).Position.z
 
 
  ' '''[27] getting and setting the position of points (without selection) right after object creation'''
   
   
  ' x: hypothetical value of <Teams>
  ' point positions are relative to the object's center
  x=101
  ' to get the absolute point positions add center to point
  ' 101 = 64 + 32 + 4 + 1
  ' to set the absolute point positions subtract center from point
   
   
  x_255
  set oRoot = application.activeproject.activescene.root
  sub x_255
  set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
if x > 255 then
msgbox "Flags bigger than 255 are not allowed.", ,"error"
' to test our code move center to somewhere else
exit sub
Translate oObj, 9, 11, 13, siRelative, siGlobal, siCtr, siXYZ, , , , , , , , , , 0
end if
SaveKey oObj & ".kine.local.posx," & oObj & ".kine.local.posy," & oObj & ".kine.local.posz", 1, , , , True
if x >= 128 and x <= 255 then
logmessage "128"
FreezeObj oObj
x = x - 128
set oGeometry = oObj.activeprimitive.geometry
end if
aPositions = oGeometry.Points.PositionArray
if x >= 64 and x < 128 then
' get old position
logmessage "64"
'                                        (xyz, point)
x = x - 64
logmessage "old point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
end if
logmessage "old point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
if x >= 32 and x < 64 then
logmessage "old point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
logmessage "32"
x = x - 32
' set new position
end if
aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
if x >= 16 and x < 32 then
aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
logmessage "16"
aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
x = x - 16
end if
' update the array
if x >= 8 and x < 16 then
oGeometry.Points.PositionArray = aPositions
logmessage "8"
' get new position
x = x - 8
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
end if
logmessage "new point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
if x >= 4 and x < 8 then
logmessage "new point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
logmessage "4"
x = x - 4
' INFO : old point 0 posx: -4
end if
' INFO : old point 0 posy: -4
if x >= 2 and x < 4 then
  ' INFO : old point 0 posz: -4
logmessage "2"
x = x - 2
end if
if x = 2 then
logmessage "2"
x = x - 2
end if
if x = 1 then
logmessage "1"
x = x - 1
end if
if x = 0 then
logmessage "finished check"
end if
  end sub
   
   
  ' INFO : 64
  ' INFO : new point 0 posx: -7
  ' INFO : 32
  ' INFO : new point 0 posy: -7
' INFO : 4
  ' INFO : new point 0 posz: -7
  ' INFO : 1
' INFO : finished check




  ' '''[31] disabling PPG popups'''
  ' '''[28] getting the scaling, rotation and position of selected objects'''
   
   
  ' for example if code of a script button creates numerous objects in one go, the same number of property pages (PPG) can appear
  logmessage "mesh name: " & selection(0)
  ' for user convenience those PPG popups can be disabled
  logmessage selection(0).sclx.value
   
  logmessage selection(0).scly.value
  ' disable PPG popup
  logmessage selection(0).sclz.value
  Preferences.SetPreferenceValue "Interaction.autoinspect", false
  logmessage selection(0).rotx.value
   
  logmessage selection(0).roty.value
  ' creates the cube mesh but no PPG will show up
  logmessage selection(0).rotz.value
  CreatePrim "Cube", "MeshSurface"
  logmessage selection(0).posx.value
   
  logmessage selection(0).posy.value
  ' enable PPG popup again
  logmessage selection(0).posz.value
Preferences.SetPreferenceValue "Interaction.autoinspect", true




  ' '''[32] disabling logmessages'''
  ' '''[29] getting the scaling, rotation, and position of not selected objects'''
   
   
  ' msglogverbos is probably already set to false by default
  ' GetValue("NAME.kine.global.rotx")
  ' could be set at the start of a final script to increase performance
  ' NAME must be the exact mesh name
  setvalue "preferences.scripting.msglogverbose", false
' let's say you want the data of one character's pelvis
  setvalue "preferences.scripting.msglog", false
logmessage GetValue("pelvis.kine.global.sclx")
logmessage GetValue("pelvis.kine.global.scly")
logmessage GetValue("pelvis.kine.global.sclz")
logmessage GetValue("pelvis.kine.global.rotx")
logmessage GetValue("pelvis.kine.global.roty")
  logmessage GetValue("pelvis.kine.global.rotz")
  logmessage GetValue("pelvis.kine.global.posx")
logmessage GetValue("pelvis.kine.global.posy")
logmessage GetValue("pelvis.kine.global.posz")




  ' '''[33] getting bounding box values'''
  ' '''[30] decrypting merged integer flags'''
   
   
  ' this could be useful to create a bounding box for [[OBD_talk:OFGA#XML|OFGA files]]
  ' for example the [[XML:BINA/OBJC/TRGV|BINACJBOTrigger Volume.xml]] file has flags as strings for the <Flags> tag
' and integer values for the <Teams> tag whereby the values are merged to one value
' so if <Teams> tag holds "101" we have to think what flags it holds, the flags can be broken down with following code
   
   
  ' let's get the bounding box of a simple cylinder
  ' x: hypothetical value of <Teams>
  ' the output values will be absolute positions
  x=101
  CreatePrim "Cylinder", "MeshSurface"
  ' 101 = 64 + 32 + 4 + 1
   
   
  dim xmin, ymin, zmin, xmax, ymax, zmax
  x_255
  dim list
  sub x_255
'if you use SelectionList the objects will be treated as one single object
if x > 255 then
'set list = GetValue( "SelectionList" )
msgbox "Flags bigger than 255 are not allowed.", ,"error"
set list = GetValue( selection(0) )
exit sub
GetBBox list, TRUE, xmin, ymin, zmin, xmax, ymax, zmax
end if
LogMessage "Lower Bound: " & xmin & " / " & ymin & " / " & zmin
if x >= 128 and x <= 255 then
LogMessage "Upper Bound: " & xmax & " / " & ymax & " / " & zmax
logmessage "128"
x = x - 128
' expected output:
end if
' INFO : Lower Bound: -1 / -2 / -1
if x >= 64 and x < 128 then
' INFO : Upper Bound: 1 / 2 / 1
logmessage "64"
 
x = x - 64
 
end if
' '''[34] open an explorer window'''
if x >= 32 and x < 64 then
logmessage "32"
' this could be used open an output folder ...
x = x - 32
strPath = "[http://support.microsoft.com/kb/152457 explorer.exe /e]," & XSIUtils.ResolvePath("$SI_HOME/")
end if
Set objShell = CreateObject("Wscript.Shell")
if x >= 16 and x < 32 then
objShell.Run strPath
logmessage "16"
x = x - 16
' to select a generated file inside the folder:
end if
strPath = "explorer.exe /select," & "C:\folder\test.txt"
if x >= 8 and x < 16 then
 
logmessage "8"
 
x = x - 8
{| border=0 cellpadding=0 style="float: right; padding-top: 10px"
end if
| SelectNeighborObj obj 5<br>[http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/XSI_modding/SelectNeighborObj_obj_5.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/XSI_modding/SelectNeighborObj_obj_5_tn.png]
if x >= 4 and x < 8 then
|}
logmessage "4"
' '''[35] working with hierarchies'''
x = x - 4
  end if
' let's be sure we have an object selected
  if x >= 2 and x < 4 then
if selection.count > 0 and typename(selection(0)) = "X3DObject" then
logmessage "2"
 
x = x - 2
  ' the object itself
  end if
  logmessage selection(0)
  if x = 2 then
logmessage "2"
 
x = x - 2
  ' the parent object
  end if
logmessage selection(0).parent
  if x = 1 then
logmessage "1"
 
  x = x - 1
  ' the children
  end if
for each n in selection(0).FindChildren( , , , 0 )
  if x = 0 then
  logmessage n
logmessage "finished check"
  next
  end if
  ' the 4the parameter of FindChildren can be set to 0 or 1
end sub
  ' with 0 you get all direct children
' with 1 you get everything located under that object no matter how deep the level
   
   
' INFO : 64
' INFO : 32
' INFO : 4
' INFO : 1
' INFO : finished check
' '''[31] disabling PPG popups'''
   
   
' count all objects
' for example if code of a script button creates numerous objects in one go, the same number of property pages (PPG) can appear
logmessage "children: " & selection(0).FindChildren( , , , 1).count
' for user convenience those PPG popups can be disabled
' caution: if the 3rd parameter is set to siMeshFamily
' then the selected object will be also counted
   
   
' find the root object of the hierarchy, any object could be selected
' disable PPG popup
SelectNeighborObj selection(0), 4
Preferences.SetPreferenceValue "Interaction.autoinspect", false
   
   
' creates the cube mesh but no PPG will show up
CreatePrim "Cube", "MeshSurface"
   
   
' go through the hierarchy in different directions
' enable PPG popup again
' SelectNeighborObj [parameter1], [parameter2], [parameter3], [parameter4]
Preferences.SetPreferenceValue "Interaction.autoinspect", true
' options of the second parameters
 
' 0 go one level up
 
' 1 go one level down
' '''[32] disabling logmessages'''
' 2 go to next silbing
' 3 go to previous silbing
' 4 go to highest level
' 5 go to deepest level of current selected obj
' 5: if there are more than one children, the path goes always for the first (left) child (see image)
   
   
   
  ' msglogverbos is probably already set to false by default
' select the hierarchy as a whole
' could be set at the start of a final script to increase performance
SelectObj "left_thigh", "TREE", true
setvalue "preferences.scripting.msglogverbose", false
' first parameter could be any object of the hierarchy
  setvalue "preferences.scripting.msglog", false
' after using this the selection of an Oni character will be "B:pelvis"
  else
msgbox "no object was selected"
end if




'''[36] drag and drop support for specific oni files'''
' '''[33] getting bounding box values'''
 
function XSILoadPlugin( in_reg )
in_reg.Author = ""
in_reg.Name = "Oni drag and drop support"
in_reg.Email = ""
in_reg.URL = ""
in_reg.Major = 1
in_reg.Minor = 0
   
   
in_reg.RegisterEvent "support_oni_DnD",siOnDragAndDrop
' this could be useful to create a bounding box for [[XML:OFGA|OFGA files]]
  'RegistrationInsertionPoint - do not remove this line
   
   
XSILoadPlugin = true
' let's get the bounding box of a simple cylinder
  end function
' the output values will be absolute positions
  CreatePrim "Cylinder", "MeshSurface"
   
   
  function XSIUnloadPlugin( in_reg )
  dim xmin, ymin, zmin, xmax, ymax, zmax
dim strPluginName
dim list
strPluginName = in_reg.Name
'if you use SelectionList the objects will be treated as one single object
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
'set list = GetValue( "SelectionList" )
XSIUnloadPlugin = true
set list = GetValue( selection(0) )
  end function
GetBBox list, TRUE, xmin, ymin, zmin, xmax, ymax, zmax
LogMessage "Lower Bound: " & xmin & " / " & ymin & " / " & zmin
LogMessage "Upper Bound: " & xmax & " / " & ymax & " / " & zmax
' expected output:
' INFO : Lower Bound: -1 / -2 / -1
' INFO : Upper Bound: 1 / 2 / 1
 
 
  ' '''[34] open an explorer window'''
   
   
  function support_oni_DnD_OnEvent( in_ctxt )
  ' this could be used open an output folder ...
Set objFSO = CreateObject("Scripting.FileSystemObject")
strPath = "[http://web.archive.org/web/20150212074443/http://support.microsoft.com/kb/152457 explorer.exe /e]," & XSIUtils.ResolvePath("$SI_HOME/")
Set objShell = CreateObject("Wscript.Shell")
objShell.Run strPath
   
   
FullFilePath = in_ctxt.GetAttribute("DragSource")
' to select a generated file inside the folder:
  FileExt = objFSO.GetExtensionName(FullFilePath)
strPath = "explorer.exe /select," & "C:\folder\test.txt"
FileName = objFSO.GetBaseName(FullFilePath)
 
 
[[Image:XSI_Mod_Tool_SelectNeighborObj_obj_5.png|thumb|SelectNeighborObj obj 5]]
' '''[35] working with hierarchies'''
   
   
  if instr(LCase(FileExt), "oni") = 1 then
' let's be sure we have an object selected
  in_ctxt.SetAttribute "DragSourceSupported", true
if selection.count > 0 and typename(selection(0)) = "X3DObject" then
    
    
  ' next line prevents the code from being repeated
' the object itself
if in_ctxt.GetAttribute("DragAndDropAction") = 1 then
logmessage selection(0)
' the parent object
logmessage selection(0).parent
' the children
for each n in selection(0).FindChildren( , , , 0 )
logmessage n
next
' the 4the parameter of FindChildren can be set to 0 or 1
' with 0 you get all direct children
' with 1 you get everything located under that object no matter how deep the level
   
   
logmessage "What I'm supposed to do with that oni?"
' do more stuff here
' ..................
' e.g. telling onisplit to extract the oni
   
   
end if
' count all objects
  end if
logmessage "children: " & selection(0).FindChildren( , , , 1).count
' caution: if the 3rd parameter is set to siMeshFamily
  ' then the selected object will be also counted
   
   
  support_oni_DnD_OnEvent = true
  ' find the root object of the hierarchy, any object could be selected
  end function
SelectNeighborObj selection(0), 4
' go through the hierarchy in different directions
' SelectNeighborObj [parameter1], [parameter2], [parameter3], [parameter4]
' options of the second parameters
' 0 go one level up
' 1 go one level down
' 2 go to next silbing
' 3 go to previous silbing
' 4 go to highest level
' 5 go to deepest level of current selected obj
' 5: if there are more than one children, the path goes always for the first (left) child (see image)
' select the hierarchy as a whole
SelectObj "left_thigh", "TREE", true
' first parameter could be any object of the hierarchy
' after using this the selection of an Oni character will be "B:pelvis"
else
msgbox "no object was selected"
  end if




'''[36] drag and drop support for specific oni files'''


'''[37] dae export'''
  function XSILoadPlugin( in_reg )
 
in_reg.Author = ""
  set oProps = ActiveProject.ActiveScene.Root.Properties
in_reg.Name = "Oni drag and drop support"
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
  in_reg.Email = ""
  logmessage "Export settings not set"
  in_reg.URL = ""
  CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
in_reg.Major = 1
  else
in_reg.Minor = 0
  logmessage "Export settings present"
   
  end if
  in_reg.RegisterEvent "support_oni_DnD",siOnDragAndDrop
  'RegistrationInsertionPoint - do not remove this line
XSILoadPlugin = true
  end function
   
   
  ' sets the extension to dae
  function XSIUnloadPlugin( in_reg )
SetValue "ExportCrosswalkOptions.Format", 1
dim strPluginName
' set export path and file name
strPluginName = in_reg.Name
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
' selection only
XSIUnloadPlugin = true
SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
  end function
' export
ExportCrosswalk "ExportCrosswalkOptions"
 
 
'''[38] fbx export'''
 
FBXExportLights (false)
FBXExportSelection (true)
' mark FBXExport and hit F1 to get more options
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )
 
 
'''[39] import an image clip only once'''
 
  image_path =  "C:\Users\RRM\Desktop\white_512_512.tga"
   
   
  Set FSO = CreateObject("Scripting.FileSystemObject")
  function support_oni_DnD_OnEvent( in_ctxt )
image_clip = FSO.GetBaseName(image_path) & "_" & FSO.GetExtensionName(image_path)
Set objFSO = CreateObject("Scripting.FileSystemObject")
logmessage image_clip
   
   
  if not typename(ActiveProject.ActiveScene.ImageClips(image_clip)) = "Nothing" then
FullFilePath = in_ctxt.GetAttribute("DragSource")
logmessage "found image clip"
  FileExt = objFSO.GetExtensionName(FullFilePath)
  else
FileName = objFSO.GetBaseName(FullFilePath)
logmessage "image clip not yet present, going to import ..."
   
  if instr(LCase(FileExt), "oni") = 1 then
SICreateImageClip image_path
  in_ctxt.SetAttribute "DragSourceSupported", true
  ' or as script object:
 
'set oImage = AddImageSource (image_path)
  ' next line prevents the code from being repeated
'set oImageClip = AddImageClip (oImage)
if in_ctxt.GetAttribute("DragAndDropAction") = 1 then
  end if
   
 
logmessage "What I'm supposed to do with that oni?"
 
' do more stuff here
====Script ideas====
' ..................
* Mod Tool addon
' e.g. telling onisplit to extract the oni
* character auto-rigger
   
* character exporter (TRBS/TRMA/TXMP)
end if
 
end if
The following sections feature some thoughts/code about those ideas.
 
support_oni_DnD_OnEvent = true
end function


=====OniTools addon=====
'''Important:'''
<!--
{| border=0 cellpadding=0 style="float: right;"
| exported warehouse TVs<br>[http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/TRGV_Oni_to_ModTool.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/TRGV_Oni_to_ModTool_tn.png]
|}-->


Install by drag'n'drop [https://dl.dropboxusercontent.com/u/139715/OniGalore/xsi_OniTools_addon_v3.zip OniTools.xsiaddon] into Mod Tool's viewport.
Mod Tool has its own drag and drop event handler for dae and xml file.


If we succeed in modifying and shipping that new file, we could finally trigger scripts with those files and process them.


'''tips'''
The original file can be found at:
* while in point mode (vertex) mode, creating an powerup/weapon will move it to the selected point
logmessage XSIUtils.ResolvePath("$XSI_HOME/") & "Application\DSScripts\Model.vbs"
* while in object mode, creating an powerup/weapon will spawn it at 0, 0, 0
' C:\Softimage\Softimage_Mod_Tool_7.5\Application\DSScripts\'''Model.vbs'''
** check point snap checkbox, then use move tool [V] with [Control] to move object to desired point
A backup of the original file can be found here: '''<nowiki>https://dl.dropboxusercontent.com/u/139715/OniGalore/ModToolScript/Model.vbs</nowiki> (dead link)'''


There are two issues that can be fixed by modifying this file.
* import of xml files created by onisplit
* import of dae and xml files with relative path from a html page


'''known issues (all versions)'''
Relative file pathes will be transformed to absolute pathes whereby an unwanted "file:///" is added.
* "(un)hide all" button inverts visibility for all objects individually
** e.g. if one object is hidden but rest is visible, the button will make it visible and the rest hidden


A solution to is to transform the path again.


'''version 3'''
if instr(in_Filename,"file:///") = 1 then
* weapon manager (plus BINACJBOWeapon'''.oni''' drag'n'drop support)
in_Filename = replace(in_Filename, "file:///", "")
* disables transparency on weapon textures
end if
* button to access OniXSI resources folder
* fixes glitchy PPGs (manager windows sometimes switched from "locked" to "refresh" mode, etc.)


Following function is always triggered, so we will modify it.
sub ImportDotXSIProc( in_Filename, in_Parent )
<font style="color:#DD0000">if instr(in_Filename,"file:///") = 1 then
in_Filename = replace(in_Filename, "file:///", "")
end if
Set objFSO = CreateObject("Scripting.FileSystem<Object")
FileExt = objFSO.GetExtensionName(in_Filename)</font>
if application.license = "Avid 3D" then
SIImportDotXSIFile in_Filename, in_Parent
else
Set oFS = CreateObject("Scripting.FileSystemObject")
<font style="color:#DD0000">if oFS.FileExists(in_Filename) = True then
if FileExt = "xml" then
' is this an Oni xml file ?
Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load(in_Filename)
if xmlDoc.selectNodes("Oni").length > 0 then
logmessage "Oni xml file detected"
read_xml "OFGA", in_Filename
end if
exit sub
end if</font>


'''version 2'''
The lines in red has been added.
* better support for future custom resources (check out the readme files in OniXSU resources folder)
** for example new LSIs can be added at ...\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\PowerUps\LSI




'''version 1'''
* powerups manager (plus BINACJBOPowerUp'''.oni''' drag'n'drop support)
** LSIs gets imported with a placeholder geometry (there's no easy way to detect what actual LSI is used for a level)
** using LSIs from the manager works though (just keep in mind that levels are fixed to one type of LSI)
* drag'n'drop support for OniSplit update


'''[37] dae export'''


'''todo list'''
set oProps = ActiveProject.ActiveScene.Root.Properties
* addon to make easily use of onisplit within Mod Tool [done]
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
** supports drag and drop of onisplit [done]
logmessage "Export settings not set"
* import/export of flag collection
CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
* import/export of patrol path collection
else
* import/export of weapons collection [done]
logmessage "Export settings present"
* import/export of powerup collection [done]
end if
* [...]
<!--
' sets the extension to dae
pushed back
SetValue "ExportCrosswalkOptions.Format", 1
* Trigger volumes: import / add / export
' set export path and file name
* camera OBAN: DnD import / export
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
-->
' selection only
SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
' export
ExportCrosswalk "ExportCrosswalkOptions"




These points are less certain. Maybe they will be added, maybe not.
'''[38] fbx export'''
* making loops from parts of animations
* copying animation data from one character to another


FBXExportLights (false)
FBXExportSelection (true)
' mark FBXExport and hit F1 to get more options
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )


=====Selection tracking camera=====
* https://dl.dropbox.com/u/139715/auto_selection_tracking.txt


' enable automatic tracking
'''[39] import an image clip only once'''
set_auto_tracking ("yes")
' disable automatic tracking
set_auto_tracking ("no")


There could be other buttons to jump to previous/next object grouped under their parent.
image_path =  "C:\Users\RRM\Desktop\white_512_512.tga"
  SelectNeighborObj , 2
  SelectNeighborObj , 3
Set FSO = CreateObject("Scripting.FileSystemObject")
image_clip = FSO.GetBaseName(image_path) & "_" & FSO.GetExtensionName(image_path)
logmessage image_clip
if not typename(ActiveProject.ActiveScene.ImageClips(image_clip)) = "Nothing" then
logmessage "found image clip"
  else
logmessage "image clip not yet present, going to import ..."
SICreateImageClip image_path
  ' or as script object:
'set oImage = AddImageSource (image_path)
'set oImageClip = AddImageClip (oImage)
  end if




=====New camera animations=====
'''[40] layers'''
{| border=0 cellpadding=0 style="float: right;"
| last frame of OBANOutroCam02<br>[http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/Mod_Tool/camera_OBAN_examination_zpsaf16d39e.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/Mod_Tool/camera_OBAN_examination_tn_zpsc8295474.png]
|}


There's already an excel macro available but now I want to build cam anims without excel.
[[Image:ModTool_layers.png|thumb|200px|right|how to access scene layers]]


The last frame's rotation of level1_Final file OBANOutroCam02 is: -0.0158410165 0.854542 0.0261086561 -0.518483639.
To access scene layers switch from Main Control Panel (MCP) to Keying Panel/Layers (KP/L) and then click "Scene".


Those quaternions are sorted in X Y Z -W order, so we've to watch out a bit when setting the input for converting them to euler rotations in degrees.
After the user became used to layers, they can help to organize work in a scene. This is especially interesting for some scripting tasks like getting TRGV/FURN data for xml files.


If a newly created object (in this case a cube) shall serve as camera placeholder then the X output has to be multiplied by -1 and the Y output has to be reduced by -180. Those changes must repeated again when exporting the object's rotation to xml.
' get current layer
GetCurrentLayer CurrentLayer
logmessage CurrentLayer


'''Update: Sept. 29, 2012'''


If camera is a real one then the additional X and Y changes aren't necessary. (Primitive > Camera > ''any should do'')
' create a new layer named "FurnLayer" if it doesn't already exist
' make new layer the current layer
if typename(ActiveProject.ActiveScene.Layers("FurnLayer")) = "Nothing" then
SICreateLayer , "FurnLayer", FurnLayer
SetCurrentLayer FurnLayer
else
SetCurrentLayer "Layers.FurnLayer"
end if


Positions and rotations keyframes can be applied to the camera root object.


' get objects in FurnLayer and count objects
' exit sub if there are no objects
SelectMembers "Layers.FurnLayer"
if selection.count = 0 then
MsgBox "No OFGA data found to export.", , "Export canceled"
exit sub
end if


=====Adding multiple textures to level geometry=====
Material and textures are normally stored right under the object. But AKEV geometry can have more than one texture. Here comes polygon clusters in to play.
' if there are Furn objects
for each obj in selection
' do something e.g. create xml
next


An example:


  set oCube = Application.ActiveProject.ActiveScene.Root.AddGeometry("Cube","MeshSurface")
' '''[41] onisplit update'''
  SelectObj "cube", , True
  SelectGeometryComponents "cube.poly[LAST]"
' this downloads and unzips the last attachment from the [http://mods.oni2.net/node/38 onisplit page]
  CreateCluster
output_dir = "F:\test\"
' get onisplit link ################################################
  set xmlhttp = createobject ("msxml2.xmlhttp.3.0")
xmlhttp.open "get", "http://mods.oni2.net/node/38/index.html", false
xmlhttp.send
<nowiki>webtext = xmlhttp.responseText
webtext = replace(replace(webtext, vbcr,""),vblf,"")
  webtext = split(webtext, "<table class=""sticky-enabled""> <thead><tr><th>Attachment</th>" )(1)
webtext = split(webtext, "</td> </tr></tbody></table>") (0)</nowiki>
link_start = instrrev(webtext, "http")
  link_end = instrrev(webtext, ".zip"" type") + 4 ' + zip
' mid (text, begin, length)
link = mid(webtext, link_start, link_end - link_start)
logmessage "last link in attachment: " & link
dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", link, False
xHttp.Send
' get file name ###################################################
hdr = xHttp.getResponseHeader("Content-Disposition")
hdr = replace (hdr, "attachment; filename=""", "")
hdr = replace (hdr, """", "")
  logmessage hdr
' save file #######################################################
filename = output_dir & hdr
with bStrm
.type = 1 '//binary
.open
.write xHttp.responseBody
.savetofile filename, 2 '//overwrite
end with
' extract file ####################################################
UnzipFile filename, output_dir
   
   
  AddToCluster "cube.polymsh.cls.Polygon, cube.poly[2-4]"
  Function UnzipFile(ZipFile, ExtractTo)
RemoveFromCluster "cube.polymsh.cls.Polygon, cube.poly[3]"
Set fso = CreateObject("Scripting.FileSystemObject")
If NOT fso.FolderExists(ExtractTo) Then
fso.CreateFolder(ExtractTo)
End If
set objShell = CreateObject("Shell.Application")
set FilesInZip=objShell.NameSpace(ZipFile).items
objShell.NameSpace(ExtractTo).CopyHere(FilesInZip)
Set fso = Nothing
Set objShell = Nothing
End Function


The cube has now a polygon cluster. More can be added. Each cluster must get its own material before a texture can be applied to the desired polygons.


[...]
' '''[42] get vertex color'''
set oGeometry = selection(0).activeprimitive.geometry
set oTriangles = oGeometry.Triangles
for each oTriangle in oTriangles
    logmessage oTriangle.Name
    for each oVertex in oTriangle.Points
        set oColor = oVertex.Color
        logmessage vbTab & "RGBA(" & _
          oColor.red & "," & _
          oColor.green & "," & _
          oColor.blue & "," & _
          oColor.alpha & ")"
    next
next




=====TRBS-fitting TRMA creation=====
' '''[43] get all used textures'''
{| width="100%"
|valign="top"|
DeleteAllUnusedMaterials
With the following code ...
DeleteUnusedImageClips
* the objects get listed one after another (TRMA-ready)
'DeleteUnusedImageSources
* the textures get listed for TRMA and TXMP creation
for each i in Application.ActiveProject.ActiveScene.ExternalFiles
* the texture sizes get checked to see if "-large" argument is necessary
if i.filetype = "Pictures" then
logmessage i
end if
next


One more idea is to add support for reflective textures.


' ''' [44] get global point position'''
set oObj = selection(0)
set oTrans = oObj.Kinematics.Local.Transform
set oPoint0 = oObj.ActivePrimitive.Geometry.Points(0)
set oPoint7 = oObj.ActivePrimitive.Geometry.Points(7)
set oPos0 = oPoint0.Position
set oPos7 = oPoint7.Position
' scaling must be frozen to 1 before we can calculate the size from local values
ResetTransform selection(0), siCtr, siScl, siXYZ
logmessage "local p0: "& oPos0.X & " " & oPos0.Y & " " & oPos0.Z
set oGlobalPos0 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos0)
logmessage "global p0: "& oGlobalPos0.X & " " & oGlobalPos0.Y & " " & oGlobalPos0.Z
logmessage "local p7: "& oPos7.X & " " & oPos7.Y & " " & oPos7.Z
set oGlobalPos7 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos7)
logmessage "global p7: "& oGlobalPos7.X & " " & oGlobalPos7.Y & " " & oGlobalPos7.Z
 
logmessage "size: " & oPos7.X - oPos0.X & " " & _
oPos7.Y - oPos0.Y & " " & _
oPos7.Z - oPos0.Z
' with a rotation of: -3,8792 16,4039 -13,5017
' INFO : local p0: -4 -4 -4
' INFO : local p7: 4 4 4
' INFO : global p0: -5,74764582364017 -3,00250371537919 -2,43916767056426 ' TRGV start point
' INFO : global p7: 5,74764582364017 3,00250371537919 2,43916767056426
' INFO : size: 8 8 8


Current status:
* http://youtu.be/RqSsXTzHTA8
* drag and drop [http://mods.oni2.net/system/files/Oni_Character_Helper_with_onisplit_dnd_updater_wip.zip add-on file] into Mod Tool to install it
* no LOD, no envmap support


|style="float:right"|
===Interactive Creative Environment (ICE)===
hexhound hierarchy
* [[wikipedia:Autodesk_Softimage#ICE_Interactive_Creative_Environment|ICE]]


[http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/hexhound_hierarchy_zpsf32e971c.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/hexhound_hierarchy_tn_zpsd82230ce.png]
'''tutorials:'''
|}
* https://www.youtube.com/watch?v=ioMQ2rBVO4g
* http://dot3d.blogspot.com/2009/10/softimage-ice-tree-building-step-by.html
* [https://www.si-community.com/community/viewtopic.php?f=41&t=1707 an ICE tutorial thread]
* [https://download.autodesk.com/global/docs/softimage2013/en_us/userguide/index.html?url=files/ICE_trees_DebuggingICETrees.htm,topicNumber=d30e269791 ICE trees Debugging]
* <nowiki>https://support.solidangle.com/display/SItoAUG/ICE</nowiki> (dead link)


if selection.count > 0 then
==Wanted knowledge==
' any part could be selected, let's find the root body part
* How to set up particles in Mod Tool? We still need an Oni particle editor!
SelectNeighborObj selection(0), 4
* How to rig models and then extract the animation data?
' get all members including the pelvis
* How to make complex new UV maps?
set bodyparts = '''selection(0).FindChildren( , , siMeshFamily)'''
for each member in bodyparts
logmessage "object name: " & member.name
if not typename(member.Material.CurrentImageClip) = "Nothing" then
logmessage "texture: " & member.Material.CurrentImageClip.source.filename.value
'logmessage "material: " & member.Material.name
'logmessage "shader: " & member.Material.shaders(0).name
logmessage "X: " & member.Material.CurrentImageClip.source.Parameters("XRes").Value
logmessage "Y: " & member.Material.CurrentImageClip.source.Parameters("YRes").Value
end if
logmessage "----------------------------------------------------------"
next
logmessage "counted body parts: " & bodyparts.count
else
logmessage "no object was selected"
end if
 
{{divhide|logmessage examples}}
' INFO  : object name: A
' INFO  : texture: C:\Users\RRM\Desktop\A.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: R
' INFO  : texture: C:\Users\RRM\Desktop\R.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: S
' INFO  : texture: C:\Users\RRM\Desktop\S.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: P
' INFO  : texture: C:\Users\RRM\Desktop\P.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: Q
' INFO  : texture: C:\Users\RRM\Desktop\Q.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: N
' INFO  : texture: C:\Users\RRM\Desktop\N.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: O
' INFO  : texture: C:\Users\RRM\Desktop\O.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: L
' INFO  : texture: C:\Users\RRM\Desktop\L.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: M
' INFO  : texture: C:\Users\RRM\Desktop\M.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: B
' INFO  : texture: C:\Users\RRM\Desktop\B.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: C
' INFO  : texture: C:\Users\RRM\Desktop\C.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: D
' INFO  : texture: C:\Users\RRM\Desktop\D.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: E
' INFO  : texture: C:\Users\RRM\Desktop\E.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: F
' INFO  : texture: C:\Users\RRM\Desktop\F.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: G
' INFO  : texture: C:\Users\RRM\Desktop\G.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: H
' INFO  : texture: C:\Users\RRM\Desktop\H.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: I
' INFO  : texture: C:\Users\RRM\Desktop\I.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: J
' INFO  : texture: C:\Users\RRM\Desktop\J.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : object name: K
' INFO  : texture: C:\Users\RRM\Desktop\K.tga
' INFO  : X: 512
' INFO  : Y: 512
' INFO  : ----------------------------------------------------------
' INFO  : counted body parts: 19
{{divhide|end}}
 
 
=====New door animations=====
Final door animation depends on:
* BINACJBODOOR position and rotation
* OBAN positions and rotations
* ZAxisUp (Does this flag do something else?)
 
 
'''Theory of the (Oni to ModTool) import from scratch:'''
 
* import BINACJBODOOR
* get door classes
* extract M3GM from DOOR, etc.
 
* import M3GM
* move center to lower bounding box Z
* move object to Z = 0
* import OBAN
* apply rotations and positions to object
 
* create null object
* make null a parent of door object
* rotate null in X = -90 (to make Z axis pointing up)
* add BINACJBODOOR rotation and position to null object
 
The current version of onisplit (v0.9.68.0) imports the doors from AKEV without animations.
 
Those door have there centers at the lower bounding box Z, got corrected from their ZAxisUp, and have (BINACJBODOOR) rotation and position.
 
Writing an own combined import would not only take more time, also it wouldn't be so fast and efficient. So, I will go with the existing import but then do some changes on the doors (appling null object, custom properties, etc.)
 
The door objects can be identified by their naming. Each name contains the BINACJBODOOR ID, e.g. <DOOR Id="7294">.
 
There could be a loop scanning all objects for those numbers.
 
 
=====Hierarchy builder for characters=====
Based on old knowledge. See [[Mod_Tool#exchange_of_meshes_in_hierarchies|HERE]] for an easy manual way to change the meshes in hierarchies.
 
 
 
'''(old notes in the following)'''
 
{| border=0 cellpadding=0 style="float: right;"
| hierarchy breaker beta<br>[http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/hierarchy_breaker_beta.png http://i305.photobucket.com/albums/nn207/unknownfuture/Oni_Galore_Images/3D_modding/hierarchy_breaker_beta_tn.png]
|}
 
(Automatic rigging.) Maybe null objects could stand for the centers of body parts. Then one click and the real centers gets positioned. The [[User:Paradox-01/for_WIP_pages#Mod_Tool:_putting_a_character_together_while_preserving_body_part_positions|rotations are known]] and the names would lead to the correct hierarchy.
 
In order to make edits there is also a need of destroying the hierarchy again while preserving the rotation and position of the meshes.
 
 
code pieces
 
 
hierarchy breaker
* [https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/hierarchy_breaker_beta.txt hierarchy_breaker_beta.txt] (works only with strict correct names)
 
 
hierarchy builder
* [https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/delete_keyframes.txt delete_keyframes.txt] (deletes keys, saves and reloads character)
* [...]
 
 
'''Keyframe affects hierarchy building'''
 
situation: "cube" and "cube1" were created
 
* case 1:
** apply 45° y rotation to cube
** make cube1 a child of cube
** cube1 will not inherit the 45°
 
* case 2:
** apply 45° y rotation to cube
** make rotation keyframe for cube1
** make cube1 a child of cube
** cube1 inherit the 45°
 
* case 3:
** apply 45° y rotation to cube
** make rotation keyframe for cube1
** remove that keyframe again
** make cube1 a child of cube
** cube1 inherit the 45°
 
* case 4:
** apply 45° y rotation to cube
** make rotation keyframe for cube1
** remove that keyframe again
** save both cube to a dae file
** make new scene and load dae
** make cube1 a child of cube
** cube1 will not inherit the 45°
 
Case 1 might be a bug (case 4 is a variant of it) but it simplify the hierarchy creation.
 
 
While doing those test you can observe red letters next to the meshes in the Schematic window.
: A means the presence of keyframes. Removing the keyframe doesn't remove the A. Object must be saved and reloaded.
: D means that mesh center was changed by rotation or translation (center mode) or by clicking Transform > Freeze(...). D can be removed by clicking the normal Freeze button. Yea, whatever...
: C can be seen next to camera objects, present when Camera_Interest is horizontal attached to it.


[[Category:Windows-only modding tools]]
[[Category:Modding tutorials]][[Category:Outdated modding tools]][[Category:Windows-only modding tools]]

Latest revision as of 22:57, 21 July 2024

Autodesk's Mod Tool used to be the main choice for modders when working on level and character models (at least in Windows, since it's Windows-only). It was a free and accessible program, but has long since been discontinued. Nowadays, modders are encouraged to use Blender instead.

sub pages
Mod_Tool/Scripting
Mod_Tool/Rigging
Mod_Tool/OniTools_addon
Mod_Tool/Oni_level_rebuilder

Installation

Full name: "Autodesk Softimage Mod Tool". Usually we just call it "Mod Tool" or "XSI", the old name of the program.

Download

  • Get it from Mod DB.
  • It's no longer possible to download Softimage from Autodesk because the software was discontinued by Autodesk in favor of their major software packages Maya and 3ds Max.

Newer Windows installations usually require .NET 2.0 to be installed before the XSI installer can fully run.

Downloading and installing .Net 2.0 manually from the internet might not work.
Therefore use the Windows search and look for "features". (Enable/disable Windows features.)
From the Windows feature page install ".NET framework 3.5 (includes .NET 2.0 and 3.0)".

There are various ways to extend Mod Tool's range of functions: scripts (.vbs, .js), toolbars (.xsitb) and addons (.xsiaddon). They can be created on one's own and also shared with other users.

Extensions

links


how to bind scripts to buttons (embedded code or .vbs / .js)

  • You can enable/disable the Main Shelf over "View" > "Optional Panels" > "Main Shelf". Then click the "Custom" tab.
  • A) Right-click the free gray space, click on "Customize Toolbar...". Select "Toolbar Widgets" > "Script Button".
  • Enter your code and make sure you selected the correct Scripting Language. Click okay when you are done.
  • B) You can also drag'n'drop your script file directly onto the free gray space.
  • A new window pops up, check the settings and click okay when done.
  • Right-click the Main Shelf and use the "Save" option. ("Save As" let you save the Main Shelf as toolbar file.)


how to install toolbars (.xsitb)

  • creation: goto "View" > "New custom toolbar"
  • Now you can add buttons like in Main Shelf. Remember to save your changes. (Right-click, "Save As".)
  • installation: Drag'n'drop the xsitb file onto the viewport (it's simply the window where you can see 3D objects).
  • access: goto "View" > "Toolbars"
  • deletion: goto "View" > "Manage..." Check a toolbar and click Delete button.
  • The file will remain with a new file suffix ".bak" You can restore this backup if you remove the ".bak" from the name.


how to install add-ons (.xsiaddon)

  • A) drag'n'drop the file into the so-called viewport (it's simply the window where you can see 3D objects), then restart Mod Tool
  • B) if drag and drop doesn't work, goto "File" > "Add-On" > "Install"
  • Then click on the "..." button to search for the file. When you found it, click the "Install" button.


building add-ons (http://softimage.wiki.softimage.com/sdkdocs/cus_addons_WalkthroughBuildinganAddon.htm, dead link)

  • Building an add-on file needs more effort than the other 'extensions' but for the end user it's best because it can hold all other files: toolbars, plugins (scripts), etc.
    • The user can install a newer version of the addon by repeating the installation with the new file: it's unnecessary to remove the old version.
  • File > Add-On > Package... doesn't work with ModTool "Add-Ons cannot be created with Softimage Demo Version."
  • Images and any other files can be put into any folder of the add-on.
    • Let's say you have an addon called "my_stuff", a subfolder "images" and an image called "test.png". The image can be accessed via:
XSIUtils.ResolvePath("$XSI_USERHOME/") & "Addons\my_stuff\images\test.png"
  • The problem with embedded code: commands like CreatePrim can call an property page (PPG).
    • Those popups can be disabled. Search for "disabling PPG popups" on this page.
  • Putting scripts simply into folders doesn't work, they must be "plugins", the file extension can be the same e.g. "vbs".
    • A script function can run on another computer if it is found as command, and plugin register new commands.
    • E.g. you can use those commands in your toolbar's script buttons.
  • Example of turning a function into a command:


function with arguments as it is

' this function can only be used inside the script where it was defined

function simple_math (a, b, operator)
	if operator = "+" then
 		simple_math = a + b
	elseif operator = "-" then
		simple_math = a - b
	elseif operator = "/" then
		simple_math = a / b
	elseif operator = "x" then
		simple_math = a * b
	else
		logmessage "The used operator is not allowed. Choose one of the following:" & vbCrlf & "+ - / x"
	end if
end function

' example of usage
result = simple_math(5, 3, "+")
logmessage result


the same function as command

' the command simple_math can now be used in any script

function XSILoadPlugin( in_reg )
	in_reg.Author = "your_name"
	in_reg.Name = "simple_mathPlugin"
	in_reg.Email = ""
	in_reg.URL = ""
	in_reg.Major = 1
	in_reg.Minor = 0

	in_reg.RegisterCommand "simple_math","simple_math"
	'RegistrationInsertionPoint - do not remove this line

	XSILoadPlugin = true
end function

function XSIUnloadPlugin( in_reg )
	dim strPluginName
	strPluginName = in_reg.Name
	Application.LogMessage strPluginName & " has been unloaded.",siVerbose
	XSIUnloadPlugin = true
end function

function simple_math_Init( in_ctxt )
	dim oCmd
	set oCmd = in_ctxt.Source
	oCmd.Description = ""
	oCmd.ReturnValue = true

	dim oArgs
	set oArgs = oCmd.Arguments
	oArgs.Add "a",siArgumentInput,"0"
	oArgs.Add "b",siArgumentInput,"0"
	oArgs.Add "operator",siArgumentInput,"0"
	simple_math_Init = true
end function

function simple_math_Execute( a, b, operator )
	' convert to double
	a = cdbl(a)
	b = cdbl(b)
	
	if operator = "+" then
		simple_math_Execute = a + b
	elseif operator = "-" then
		simple_math_Execute = a - b
	elseif operator = "/" then
		simple_math_Execute = a / b
	elseif operator = "x" then
		simple_math_Execute = a * b
	else
		logmessage "The used operator is not allowed. Choose one of the following:" & vbCrlf & "+ - / x"
	end if
end function

Selected wisdom

We've also a tutorial thread on OCF.

Hotkey list can be found here.


Visibility

  • show/hide grid: [G]
  • display polygon normals: click the eye symbol, select "Polygon Normals" (now you know the polygons up and down)
  • display triangle count: click the eye symbole, select "Selection Info"


Camera navigation

  • reset view: [R]
  • zoom camera: [Scrolling]
  • move camera: [S] + [LClick] + [MoveMouse]
  • rotate camera: [S] + [RClick] + [MoveMouse]
  • pan camera: [S] + [MClick] + [MoveMouse]
  • move camera mode: [Z]
  • rotate camera mode: [O]
  • pan camera mode: [P]
  • use camera mode move/rotate/pan with: [LClick] + [MoveMouse]


Mesh

  • scale mode: [X]
  • rotate mode: [C]
  • translate mode [V]
  • manipulate tool: [B] (a gray box appears with gray balls at the corners)
    • drag inside the object to move it
    • drag an edge to rotate it
    • drag a ball to scale it
  • object selection mode: [Space]
  • extracting a polygon from an object: select polygon, right-click, "Extract Polygons (Delete)"
  • copying an polygon from an object: select polygon, right-click, "Extract Polygons (Keep)"
  • merging two objects: select object A, then goto Model > Modify > Poly. Mesh > Boolean > Union, select object B
(this makes a copy of object B and attaches it to A, the merged objects (A) must be freezed before the original object B can be removed)
  • filling a hole: select all edges surrounding the hole and choose one of the following methods
    • A) goto Model > Modify > Poly. Mesh > Weld Boundary Points/Edges
    • B) goto Model > Modify > Poly. Mesh > Bridge Boundary Points/Edges (if blue lines appear try to disable the checkbox "Angle > 90")
  • merging two objects at blue lines: merge the objects at first, then proceed with the point above "filling a hole"
  • merging two objects with UVs: Create > Poly. Mesh > Merge
    • extracted Oni characters have their materials and hence textures directly grouped under the mesh (see Explorer)
    • when adding (merging) a new part to the existing mesh, the materials/textures will be grouped under (Explorer again) mesh > Polygon Mesh > Clusters > ...
    • Samer reported that saving the file via fbx solves a problem when trying to convert TRBS
    • one would need to move the content by hand in the explorer to bypass the fbx
  • hiding some objects/polygons: select object/polygon and hit [H], use [Control] + [H] to unhide all
  • mirroring an object: select object, Modify > Poly. Mesh > Symmetrize Polygons
  • working with symmetry mode (look below at "Low poly modeling" section for other possibilities with symmetry)
    • select object, Property > Symmetry Map (YZ/XZ/XY) (press [W] to preview symmetry plane)
    • click on Sym buttom (in Transform area) to enable/disable symmetry mode
    • changing the Symmetry plane: Explorer [8] > unfold object > Polygon Mesh > Cluster > delete SymmetryMapCls; then add a new symmetry plane


Low poly modeling

  • select edge loop: while being in selection mode Edge, press [Alt] + [MClick]
  • modify point/edge/polygon: [M]
    • press left mouse button and hold it, move mouse to translate point/edge/polygon freely
if axes symbol is active you can left-click a component and the translate axes pop up
if magnet symbol is active the translation will be limited by the surrounded components
if point symbol is active then only points can be selected, moving one point onto another will weld them
  • By the way, combining edge loop with component selection [M] and active magnet gives an interesting effect: a line of edges will mimic the shape of their neighbor edges as closer the selected edges comes to the neighbors.
  • getting images into Mod Tool that serve as modeling reference:
    • click where you can set the view mode (e.g. to "Wireframe"), select "Rotoscope"
    • in the appearing window at "Image Placement" choose "Fixed" so that the image will be zoomed when you zoom on the object
    • choose an image by clicking the button "New"


symmetry in modeling:

You could make A) only one half of an object or B) selected polygons and then mirror and merge the parts.

  • A) select object, [Alt] + [RClick] object, Symmetrize Polygons
  • B) select polygons, [RClick] one of them, Symmetrize Polygons


Or you start with a half object and create a mirrored clone. You can then work on the original mesh. (In Schematics the cloned mesh will have a red "Cl" and a trapezoid shape as symbol.)

  • select object then goto Edit > Duplicate/Instantiate > Clone Single
  • scale X or one of the other axes to -1
  • make any modification on the original mesh, the clone will adept them
  • eventually merge cloned and original mesh, freeze them to make changes final


Exchange meshes in hierarchies

Let's say you loaded an Oni character with his 19 body parts into the scene, something is wrong and you want to fix it.

To destroy the hierarchy you must be sure the model is not animated. If there are keyframes the model will be one big mess after breaking the hierarchical links.

So, save the model with the settings "Selection Only [X]" and "Animation [ ]". Then reload the model.

Next, disable the red "Auto" button under "Animation" (if enabled). Keyframes affects also the creation of hierarchies, that's why we don't want new keyframes.

Now you can safely exchange, move and rotate meshes and their centers, and eventually rebuild the hierarchy.


Material and texture

how to apply a new main texture
how to apply a new polygon cluster texture
  • objects use the scene material by default, objects need their own material if they should have individual textures
  • selecting a material and surface: Model > Material > Phong (simply close the windows that pops up)
  • connecting the texture with the material:
    • [7] to open Render Tree
    • in first column: "Texture"
    • in second column: drag'n'drop "Image" into the free space
    • click and hold the red point of "Image", then move it to "Phong" and release the mouse click
    • choose "diffuse" as illumination mode (for instance "ambient" won't work - the texture would be visible in Mod Tool but Onisplit can't use it)
  • now "Image" can be double-clicked (the Texture Editor will pop up), in the section "Image" click on "New" to choose an image
  • to change textures don't drag and drop textures directly one meshes, this creates textures layers which are ignored by OniSplit


Projection and UV

getting started

  • in section "Texture Projection" click on "New", choose a projection similar to the mesh shape (planar / cylindrical / cubic / spherical)
  • select the object
  • Texture editor: [Alt]+[7] (open it while being in selection mode "Object" or "Polygon", otherwise the UVs aren't shown)

modify single/all UVs

  • rotate 90°: [R]
  • rotate: [C]
  • scale: [X]
  • scale height only: [X], then [Shift] + [MClick] + [MoveMouse]
  • scale length only: [X], then [Shift] + [RClick] + [MoveMouse]
  • move: [V]
  • tearing single UV parts: [Control] + [T], then move [V]
  • heal (re-connect) selected UV parts: click on the plus symbol (the parts should be very near to each other, best if the points are on same spot)
  • snap function for move tool: Snap > Enable Snapping; check "Points" (if not already active)
  • relax (smooth warped UVs): [Control] + [R] (usually pressing several times that combination)
  • a new projection can also be applied to currently selected UV parts
  • freeze object to bake projections (green lines in the 3D view will disappear)
  • getting a picture of the UV: apply first a black texture then enter the Texture Editor [Alt] + [7], Edit > Stamp UV Mesh [Shift] + [S], save the texture

Things to test:


Rendering

Rendering doesn't work well on my PCs.--paradox-01 (talk) 01:09, 9 March 2016 (CET)

Especially PNG, it works one time. But as soon as I import another dae the next render will fail.


So, for creating a object library we might use blender. Nice thing is, it can also render in the background when called from CMD. This means you could keep working in XSI and render images in blender at the same time.


example of a batch file:

cd C:\Program Files\Blender Foundation\Blender
blender -b --python C:\Users\Paradox-01\Desktop\blender-out\pythonScript.py


example of a render script:

import bpy

# delete blender default cube object
bpy.ops.object.mode_set(mode='OBJECT')
bpy.ops.object.select_by_type(type='MESH')
bpy.ops.object.delete(use_global=False)

# load new object
bpy.ops.wm.collada_import(filepath = "C:/Oni/r.dae")

# render settings
bpy.data.scenes['Scene'].render.resolution_x = 900
bpy.data.scenes['Scene'].render.resolution_y = 900
# default resolution percentage seems to be 50 
bpy.data.scenes['Scene'].render.resolution_percentage = 100
bpy.data.scenes['Scene'].render.use_antialiasing = True
# 5 | 8 | 11 | 16
bpy.data.scenes['Scene'].render.antialiasing_samples = "8"

#TGA IRIS JPEG MOVIE IRIZ RAWTGA
#AVIRAW AVIJPEG PNG BMP FRAMESERVER
#no gif ? could be created by more code, maybe merging single frames of jpgs
bpy.data.scenes['Scene'].render.image_settings.file_format = 'JPEG'
bpy.data.scenes['Scene'].render.image_settings.quality = 100

# file suffix is overwritten by file_formate
#bpy.data.scenes['Scene'].render.filepath = 'C:/Oni/renders/test.jpg'
bpy.data.scenes['Scene'].render.filepath = 'C:/Oni/renders/test'

# start render
bpy.ops.render.render( write_still=True )


Animating

an animation that got scaled down from 116 to 58 frames
  • preparing ModTool to save animations Oni can use: File > Preferences... > Preferences > Tools > Output Format
    • Frame Step: 1
    • Frame Format: Custom framerate
    • Frame Rate: 60
  • for real-time playback click on "Playback" option button and then "Real-Time Playback"
  • opening the Animation Editor : [0]
  • opening the DopeSheet: from any other Animation Editor, Editor > DopeSheet
  • making an animation shorter or longer: in DopeSheet click Region button (arrow with rectangle), click and hold to select keyframes, at selection edge a) [shift] plus left-click and move mouse or b) middle-click and move mouse


Issues when saving and sharing animation files (*.dae, *.exp)

That what you currently see in Mod Tool and that what you get when saving data can be different.

1) *.dae files might save data differently than expected.

Rotation flips might appear.

2) *.exp and *.dae save data differently.

  • .exp files store the frames as they are.
  • .dae file store their actions (rotations/translation/...) in seconds and eventually depend on what framerate a Modder uses.


Following scenario: a modder wants to share an animation as *.exp and another modder wants to use that animation.

Modder A uses 30 fps in his settings and exports an *.exp that is 58 frame long.

Modder B uses 60 fps in his setting and imports the *.exp, to his surprise the animation is 116 frames long. What now?

If Modder B saves the *.exp under a new name would not help, the frames will be still the same.

Instead Modder B could scale the animation. The way to do so is described in the Animating section, "making an animation shorter or longer".

Modder B scales the animation and saves it as dae.

Now he wants to see if it worked. He opens a new scene [Strg]+[N] and loads the dae.


Summary of issue-holding aspects

  • Frames Per Second (FPS)

When you share *.dae files with other Modders be sure that the output formate is 60 fps. Different framerates among modders can cause confusion.

Dae file store their actions (rotation/translation/...) in seconds.

An example:

<float_array count="10" id="pelvis_rotation_x_ANGLE-anim-input-array">
    0.000000 0.166833 0.333667 0.500500 0.600600 0.934267 1.267933 1.601600 1.768433 1.935267
</float_array>

action at [s] * framerate [f/s] = frame [f]

In this example the last rotation occurs at 1.935267s.

Modder A uses 30 fps, his last frame is 58.
Modder B uses 60 fps, his last frame is 116.


  • Euler rotations

you will get BEZIER (cubic) interpolation instead of LINEAR if you use

"Convert Quarternion Rotation to Euler"
"Spline Interpolation" in the animation (fcurve) editor

Those smooth the rotations.

[...]


  • Quaternion rotations

Don't use this.

[...]


  • Make Rotation Keys Continuous

[...]


Animation mixer

This could help with creating cutscenes and complex animations (e.g. run cycles for FILM files).

Open the animation mixer with [Alt]+[0].

Further information over HERE.

Here are a few videos:


Sounds

Within the animation mixer also sounds can be added.

Further information over HERE.

This could be used to synchronize sounds effect with physical actions in the scene.


Scripting

  • open the Script Editor: [Alt] + [4]
  • run the current code in the Script Editor window: [F5]
  • clear the log: Edit > Clear History Log
  • Actions from button embedded code will be logged.
  • Actions from code files that are linked in a button won't be logged. (This results in a performance boost.)
  • Logged stuff will be rewritten if you change the script language. (File > Preferences...)
  • You can also change the language by right-click the white script box and click on "Set to JScript" or "Set to VBScript".
  • Python can be added as script language if you install it on your PC.
  • Right-clicking the white script box gives you also access to a few code piece, e.g. "Syntax Help" > "If..Else" or "Catch Error".
  • Mark code you want to disable ("Comment Out") or enable ("Comment Remove").
  • You can save your code to a file via "File" > "Save As..." or "Save Selection"


Links


Notes about polygon extraction

  • a grid of 9 polygons (ID 0 to 8)
  • you extract the middle polygon (ID 4)
  • grid's polygon of ID 8 will change to ID 4
  • if you now extract 5th, the 7th gets the ID
  • now the ID order is: 0, 1, 2, 3, 6, 5, 4
  • this means that an automatic extraction of several polygons requires two changes in the code
  • 1) the ID array of selected polygons must be sorted
  • 2) the extraction must start with the last ID to avoid ID shift during the process (see code pieces)


Some basics of VBScript

  • In VBS you declare variables only as a variant e.g. "dim PolygonCount". Mod Tool decides what type it becomes when it uses the variable the first time.
  • You can put "option explicit" at the start of your script. Then you will be forced to declare all variables with "dim". That looks quite unnecessary but help to find mistakes for example if you mistyped a variable somewhere in a long script.
  • The commands are not case-sensitive, e.g. you can write "LogMessage" or "logmessage".
  • Vbs functions can be found HERE.
' while it is possible to use + in strings,
' you should actually use & only for strings
' and + only for numbers

logmessage "How about ... " + "a riddle?"
logmessage "Where can you find following phrase in Oni? " & "The day is mine !!"
logmessage "Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to " & cstr(2 + 0 + 3 + 2) & "."

' INFO : How about ... a riddle?
' INFO : Where can you find following phrase in Oni? The day is mine !!
' INFO : Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to 7.


More tips

  • You can replace each "logmessage" with a variable. In that case don't forget the = sign.
  • Selection(0) means that from all selected objects the first one is taken.
  • Use a loop to get all objects.
for each n in selection
	logmessage n
next
  • In order to shorten the code you might want to set script objects.
The command to create a standard cube is CreatePrim "Cube", "MeshSurface"
If you use set name = the cube or other object will be available under that name.
Then you can use any valid method or property with that name.
Example:
set c = CreatePrim ("Cube", "MeshSurface")

logmessage selection(0).name
logmessage selection(0).length.value
logmessage c.name
logmessage c.length.value

' INFO : cube
' INFO : 8
' INFO : cube
' INFO : 8


Some ModTool commands in vbs language that might be interesting for Oni related stuff

LogMessage [string | var | object] A command to log information. It requires a string or a variable. Example:
LogMessage "The object's name is: " & selection(0).Name
logmessage selection.count Logs the number of selected objects.
logmessage selection(0).Name Logs the object name.
logmessage selection(0).Materials(0).Name Logs the material name.
logmessage selection(0).Materials(0).Library.name Logs the material library name.
logmessage selection(0).Materials(0).shaders(0).name Logs the material shader (e.g. phong).
logmessage selection(0).Materials(0).CurrentImageClip.source.filename.value Logs absolute path of the texture. Output example:
C:\Users\RRM\Desktop\TV_tex.png
logmessage selection(0).Materials(0).CurrentImageClip.source.Parameters("XRes").Value
logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
Logs the horizontal (X) and vertical (Y) pixel length of the texture. Output example:
512
logmessage selection(0).sclx.value
logmessage selection(0).scly.value
logmessage selection(0).sclz.value
Logs object's scaling (X, Y, Z).
logmessage selection(0).rotx.value
logmessage selection(0).roty.value
logmessage selection(0).rotz.value
Logs object's rotation (X, Y, Z).
logmessage selection(0).posx.value
logmessage selection(0).posy.value
logmessage selection(0).posz.value
Logs object's position (X, Y, Z).
logmessage selection(0).rotorder.value Logs object's rotation order. Oni characters have ZYX as rotation order.
0 = XYZ
1 = XZY
2 = YXZ
3 = YZX
4 = ZXY
5 = ZYX
SelectObj [string | var] select one or more objects. Comma serves as separator. Example:
SelectObj "grid,grid1"
ToggleSelection [string | var] Adds one or more objects from current selection. If the objects are already selected then they become subtracted from the selection. Example:
ToggleSelection "grid2,grid3"
SelectAllUsingFilter [SelFilter], [CheckComponentVisibility], [AffectSelectionList], [CheckObjectSelectability] Selects everything that matches the filter and other options. Example:
SelectAllUsingFilter "object", siCheckComponentVisibility
selects all roots, lights, cameras, and geometry (meshes)
SelectAllUsingFilter "geometry", siCheckComponentVisibility
selects all geometry (meshes)
DeselectAll deselects everything
DeleteObj [string | var] Deletes one or more objects. Comma used as separator. Example:
DeleteObj "grid"
DeleteObj "grid,grid1"
FreezeObj Makes all changes final. But saving those changes is still necessary. (Click on key symbol so it gets red.)
Translate [InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Snap], [SnapReference], [SnapFilter], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents] see link
Rotate [InputObjs], [X], [Y], [Z], [Delta], [RefMode], [Center], [AxesFilter], [Reference], [SplitLocalComponents], [PropTagOnly], [Pivot], [PivotX], [PivotY], [PivotZ], [ConstructionMode], [SlideComponents] see link


Code pieces (VB Script)

Remember that you can use the search function of your web browser.

If you see absolute paths adapt them so that the code works on your system too.

(Here's another page for math related code pieces.)

[1] creating and extending an array [2] check selection mode [3] getting the IDs of selected polygons plus sorting them
[4] converting euler rotations (in degrees) to quaternions [5] converting quaternions to euler rotations (in degrees) [6] get and set values of the playcontrol (timeline)
[7] get keyframes [8] message box [9] input box
[10] getting the desktop path [11] selecting a folder [12] selecting a file of a specific type
[13] reading out environment variables [14] check if file exist [15] check if folder exist + create folder
[16] write xml file [17] read xml file [18] read xml file (attribute/content-based)
[19] create txt file + write lines [20] read txt file [21] check onisplit version
[22] call CMD, e.g. to use onisplit [23] building forms in ModTool [24] UserDataBlob
[25] getting the position of points (with selection mode point) [26] getting the position of points (with selection mode object) [27] getting and setting the position of points (without selection)
[28] getting the rotation and position of selected objects [29] getting the rotation and position of not selected objects [30] decrypting merged integer flags
[31] disabling PPG popups [32] disabling logmessages [33] getting bounding box values
[34] open an explorer window [35] working with hierarchies [36] drag and drop (DnD) support for specific oni files
[37] dae export [38] fbx export [39] import an image clip only once
[40] layers [41] onisplit update [42] get vertex color
[43] get all used textures [44] get global point position


' [1] creating and extending an array


' creating an array
' () sets the number of array items
redim nums (3)
for i = 0 to 3
	nums(i) = (i)
next

logmessage "output all items"
for each n in nums
	logmessage n
next
logmessage "first array item holds: " & lbound(nums)
logmessage "last array item holds: " & ubound(nums)
logmessage "-------------------------------------"


' extending an array
' use "preserve" to keep the old content
' find next higher value
plus_one = ubound(nums) + 1
redim preserve nums (plus_one)
nums(plus_one) = 4

for each n in nums
	logmessage n
next
logmessage "first array item holds: " & lbound(nums)
logmessage "last array item holds: " & ubound(nums)


' INFO : output all items
' INFO : 0
' INFO : 1
' INFO : 2
' INFO : 3
' INFO : first array item holds: 0
' INFO : last array item holds: 3
' INFO : -------------------------------------
' INFO : 0
' INFO : 1
' INFO : 2
' INFO : 3
' INFO : 4
' INFO : first array item holds: 0
' INFO : last array item holds: 4


' [2] check selection mode 

checkfilter
'use sub to make use of the exit command

sub checkfilter
	Select Case Selection.Filter.Name
	'caution: case-sensitive

	Case "object"
		logmessage "object mode"
	Case "Edge"
		logmessage "edge mode"
	Case "Vertex"
		logmessage "point mode"
	Case "Polygon"
		logmessage "polygon mode"
	Case Else
		logmessage "unknown mode"
		exit sub
	End Select
end sub


' [3] getting the IDs of selected polygons plus sorting them

poly_count = Selection(0).SubComponent.ComponentCollection.count
logmessage "poly_count: " & poly_count
redim selected (poly_count)
logmessage "Show ID as it is."
for i=0 to poly_count - 1
	' build ID array
	selected(i) = Selection(0).SubComponent.ComponentCollection(i).index
	logmessage selected(i)
next

' change sort order by replacing ">" with "<"
For i = 0 To poly_count - 1
	For j = 0 To poly_count - 1
		If selected(i) > selected(j) Then
			tmp = selected(i)
			selected(i) = selected(j)
			selected(j) = tmp
		End If
	Next
Next
logmessage "Show sorted ID."

for i=0 to poly_count - 1
	logmessage selected(i)
next


' [4] converting euler rotations (in degrees) to quaternions

dim x, y, z, dRotation, qRotation
x = 90
y = 0
z = 0

set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z)) 
set qRotation = XSIMath.CreateQuaternion() 

	dRotation.GetQuaternion (qRotation) 
	LogMessage qRotation.W
	LogMessage qRotation.X
	LogMessage qRotation.Y
	LogMessage qRotation.Z
	' INFO : 0,707106781186548
	' INFO : 0,707106781186547
	' INFO : 0
	' INFO : 0


' [5] converting quaternions to euler rotations (in degrees)

dim qW, qX, qY, qZ, qRotation, x, y, z

	qW = 0.707106781186548
	qX = 0.707106781186547
	qY = 0
	qZ = 0

set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)

	qRotation.GetXYZAngleValues x, y, z
	logmessage XSIMath.RadiansToDegrees(x)
	logmessage XSIMath.RadiansToDegrees(y)
	logmessage XSIMath.RadiansToDegrees(z)
	' INFO : 89,9999999999999
	' INFO : 0
	' INFO : 0


' [6] get and set values of the playcontrol (timeline)

' timeline variables (tl)
dim tlStart, tlEnd, tlCurrent

' get values
tlStart = GetValue ("PlayControl.In")
tlEnd = GetValue ("PlayControl.Out")
tlCurrent = GetValue ("PlayControl.Current")

' set values
SetValue ("PlayControl.In"), 7
SetValue ("PlayControl.Out"), 70
SetValue ("PlayControl.Current"), 14


' [7] get keyframes

' keyframe counting code like "selection(0).rotx.Source.Keys.count" will produce an error if no keys exist
' that's why we need to catch possible errors for each counting

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "X rotation keys: " & selection(0).rotx.Source.Keys.count
	for each k in selection(0).rotx.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no X rotation keys"
end if
on error goto 0

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "Y rotation keys: " & selection(0).roty.Source.Keys.count
	for each k in selection(0).roty.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no Y rotation keys"
end if
on error goto 0

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "Z rotation keys: " & selection(0).rotz.Source.Keys.count
	for each k in selection(0).rotz.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no Z rotation keys"
end if
on error goto 0

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "X position keys: " & selection(0).posx.Source.Keys.count
	for each k in selection(0).posx.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no X position keys"
end if
on error goto 0

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "Y position keys: " & selection(0).posy.Source.Keys.count
	for each k in selection(0).posy.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no Y position keys"
end if
on error goto 0

on error resume next
	logmessage "--------------------------------------------------"
	logmessage "Z position keys: " & selection(0).posz.Source.Keys.count
	for each k in selection(0).posz.Source.Keys
		logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
	next
if err <> 0 then
	logmessage "no Z position keys"
end if
on error goto 0


' output example (0 rot keys, 2 pos keys):

' INFO : --------------------------------------------------
' INFO : no X rotation keys
' INFO : --------------------------------------------------
' INFO : no Y rotation keys
' INFO : --------------------------------------------------
' INFO : no Z rotation keys
' INFO : --------------------------------------------------
' INFO : X position keys: 2
' INFO : index: 0 // time: 2 // value: 5,14534318031942
' INFO : index: 1 // time: 6 // value: 8,2411504340802
' INFO : --------------------------------------------------
' INFO : Y position keys: 2
' INFO : index: 0 // time: 2 // value: 0,365325291829147
' INFO : index: 1 // time: 6 // value: 1,11923927115289
' INFO : --------------------------------------------------
' INFO : Z position keys: 2
' INFO : index: 0 // time: 2 // value: 1,96103417177471
' INFO : index: 1 // time: 6 // value: 1,88564277384233


' [8] message box

msgbox "message", , "title"


' [9] input box

logmessage inputbox ("message", "title" , "pre-entered content")


' [10] getting the desktop path

' this can be useful for default locations like when selecting a folder
DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
logmessage DesktopPath


' [11] selecting a folder

' the default path is in this case my desktop
' to make it work on other systems, you would need the code of "getting the desktop path" section and replace my path with the DesktopPath
logmessage XSIUIToolkit.PickFolder("C:\Users\RMM\Desktop\", "title" )


' [12] selecting a file of a specific type

set oFileBrowser = XSIUIToolkit.FileBrowser
oFileBrowser.DialogTitle = "Select an image file (png/tga/jpg)" 
' (default folder)
oFileBrowser.InitialDirectory = "c:\"
oFileBrowser.Filter = "JPEG (*.jpg)|*.jpg|PNG (*.png)|*.png| Targa (*.tga)|*.tga||"
oFileBrowser.ShowOpen
If oFileBrowser.FilePathName <> "" Then
	logmessage "User selected " & oFileBrowser.FilePathName
Else
	logmessage "User pressed cancel"
End If


' [13] reading out environment variables

' some infos about env vars: (1), (2), (3)
' those variables are stored inside the setenv.bat, two disadvantages:
' adding or editing those vars appears to not work in vbs or I just did it wrong
' anyway, new vars can only be read out after app restart
' therefore let's concentrate on reading out existing ones and then how to create our own via txt files (example: https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/Oni_env_vars.txt Oni_env_vars.txt (dead link))
' code for reading out:
logmessage XSIUtils.ResolvePath("$SI_HOME/")


' [14] check if file exist

Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists ("C:\folder\file.txt") then
	logmessage "File exists."
else
	logmessage "File doesn't exist."
end if


' [15] check if folder exist + create folder

dim strDirectory
strDirectory = "C:\test"

Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FolderExists(strDirectory) Then
   Set objFolder = objFSO.GetFolder(strDirectory)
   logmessage strDirectory & " already exists"
Else
   Set objFolder = objFSO.CreateFolder(strDirectory)
   logmessage "created folder " & strDirectory
End If

' ### or simply:
dir = "C:\test"
XSIUtils.EnsureFolderExists (dir)


' [16] write xml file

FolderName = CreateObject("WScript.Shell").SpecialFolders("Desktop")
FileName = "test"
FilePath = FolderName & "\OBAN" & FileName & ".xml"

Set oFS = CreateObject("Scripting.FileSystemObject")
Set objXMLFile = oFS.OpenTextFile(FilePath, 2, True, 0)

' quote sign in a string needs two quote signs
objXMLFile.WriteLine "<?xml version=""1.0"" encoding=""utf-8""?>"
objXMLFile.WriteLine "<Oni>"
objXMLFile.WriteLine "..."
objXMLFile.Close


' [17] read xml file

Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )
xmlDoc.Async = "False"
path = "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level19_Final\BINACJBOWeapon.xml"
xmlDoc.Load( path )

Set colPosition = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Position" )
Set colRotation = xmlDoc.selectNodes( "Oni/Objects/WEAP/Header/Rotation" )
Set colClass =    xmlDoc.selectNodes( "Oni/Objects/WEAP/OSD/Class" )

loop_count = xmlDoc.selectNodes( "Oni/Objects/WEAP").length

logmessage "found " & loop_count & " weapons:"
logmessage "================"
for i=0 to loop_count - 1

	'split string into pices as array, space is used as seperator by default
	'element 0 = X; element 1 = Y; element 2 = Z
	pos = split(colPosition.item(i).text)
	rot = split(colRotation.item(i).text)
	
	' Mod Tool wants comma instead of point for decimal seperator
	pos(0) = replace (pos(0), ".", ",")
	pos(1) = replace (pos(1), ".", ",")
	pos(2) = replace (pos(2), ".", ",")
	
	rot(0) = replace (rot(0), ".", ",")
	rot(1) = replace (rot(1), ".", ",")
	rot(2) = replace (rot(2), ".", ",")

	logmessage colClass.item(i).text
	logmessage pos(0) & " " & pos(1) & " " & pos(2)
	logmessage rot(0) & " " & rot(1) & " " & rot(2)
	logmessage "----------------"
next


' INFO : found 2 weapons:
' INFO : ================
' INFO : w5_sbg
' INFO : 23,16747 84,8193359 757,1958
' INFO : 0 0 0
' INFO : ----------------
' INFO : w4_psm
' INFO : 18,1105652 84,8193359 749,121
' INFO : 0 0 0
' INFO : ----------------


' [18] read xml file (attribute/content-based)

Set xmlDoc = CreateObject( "Microsoft.XMLDOM" )

' tells the program to wait until the file was loaded conpletely
xmlDoc.Async = "False"
xmlDoc.Load( "C:\Users\RRM\Oni\AE\AEInstaller\vanilla\level0_Final\BINAEINOimpact_effects.xml" )


' example 1 (we use this one)
' nodes only with a certain attribute and conent will be collected

' content-based search: node [child_node = 'content']
' attribute-based search: node [@attribute_name = 'attribute_value']

' if things get too long an underscore _ can be used to make a linebreak
set w1_tap = xmlDoc.selectNodes _
	( "Oni/ImpactEffects/Impact[@Name = 'w1_tap']/Material[@Name = 'Unbreak_Glass']/ImpactEffect [Component = 'Damage']" )


' example 2
' use "or" for a search of various material
'set w1_tap = xmlDoc.selectNodes _
'	( "Oni/ImpactEffects/Impact/Material[@Name = 'Default' or @Name = 'Character']/ImpactEffect" )


' example 3
' use (|) for a search of various content, e.g. <ImpactEffect><Modifier>Light plus <ImpactEffect><Modifier>Medium
'set w1_tap = xmlDoc.selectNodes _
'	( "Oni/ImpactEffects/Impact/Material/(ImpactEffect [Modifier = 'Light'] | ImpactEffect [Modifier = 'Medium'])" )



for each element in w1_tap
	logmessage "Impact: " & element.parentNode.parentNode.getAttribute("Name")
	logmessage "Material: " & element.parentNode.getAttribute("Name")
	
	' outputs "tag name: tag content"
	logmessage element.childNodes(0).nodename & ": " & element.childNodes(0).text
	logmessage element.childNodes(1).nodename & ": " & element.childNodes(1).text
	logmessage "-----------------------------------"
	
	'check for sounds
	if element.childNodes(2).childNodes.length = 0 then
		logmessage "sound not present"
	else
		logmessage "sound is present"
		logmessage "-----------------------------------"
		logmessage element.childNodes(2).childNodes(0).nodename & ": " & element.childNodes(2).childNodes(0).text
		logmessage element.childNodes(2).childNodes(1).nodename & ": " & element.childNodes(2).childNodes(1).text
		logmessage element.childNodes(2).childNodes(2).nodename & ": " & element.childNodes(2).childNodes(2).text
		logmessage element.childNodes(2).childNodes(3).nodename & ": " & element.childNodes(2).childNodes(3).text
	end if
	logmessage "-----------------------------------"

	' check for particle
	if element.childNodes(3).childNodes.length = 0 then
 		logmessage "no particle present"
	else
		logmessage "number of particle: " & element.childNodes(3).childNodes.length
		for each particle_section in element.childNodes(3).childNodes
			logmessage "-----------------------------------"
			for each particle_tag in particle_section.childnodes
				logmessage particle_tag.nodename & ": " & particle_tag.text
			next		
		next	
	end if
	logmessage "==================================="
next


' INFO : Impact: w1_tap
' INFO : Material: Unbreak_Glass
' INFO : Component: Damage
' INFO : Modifier: Any
' INFO : -----------------------------------
' INFO : sound not present
' INFO : -----------------------------------
' INFO : number of particle: 2
' INFO : -----------------------------------
' INFO : Name: d__GLASSCRACK
' INFO : Orientation: 0
' INFO : Location: 4
' INFO : Decal1: false
' INFO : Decal2: true
' INFO : -----------------------------------
' INFO : Name: w1_tap_x03
' INFO : Orientation: 0
' INFO : Location: 1
' INFO : Offset: -1
' INFO : ===================================


' [] remove xml nodes
Set objXMLDoc = CreateObject("Microsoft.XMLDOM") 
objXMLDoc.async = False 

Dim XMLFile
XMLFile = "C:\Softimage\Softimage_Mod_Tool_7.5\OniLevels\ONLV\test\ONLVAirport.xml"
objXMLDoc.load(XMLFile) 
Set nodes = objXMLDoc.selectNodes("Oni/CRSA/*")
For Each node In nodes
    node.parentNode.removeChild(node)
Next

objXMLDoc.Save(XMLFile)


' [19] create txt file + write lines

txt_location = "C:\Softimage\Softimage_Mod_Tool_7.5\test.txt"
Set fso = CreateObject ("Scripting.FileSystemObject")
Set wText = fso.CreateTextFile (txt_location, 1)
wText.WriteLine "I'm a test file."
wText.WriteLine "Yo!"
wText.Close


' [20] read txt file

Set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Softimage\Softimage_Mod_Tool_7.5\test.txt", 1)

do while not objFileToRead.AtEndOfStream
    strLine = objFileToRead.ReadLine()
    logmessage strLine
loop
' INFO : I'm a test file.
' INFO : Yo!

objFileToRead.Close
Set objFileToRead = Nothing


' [21] check onisplit version

Set objFSO = CreateObject("Scripting.FileSystemObject")
logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")

' result looks like this:
' INFO : 0.9.59.0


' [22] call CMD, e.g. to lunch onisplit or the game

' relative path

' the "GameDataFolder" isn't inside the "install" folder
' so we will use ..\ to go one folder backwards

' additional quote signs tells the program where the 
' paths strings start and end in case the path contains spaces

' if you are going to use the xml file right after its extraction (which is likely)
' then the "/wait" argument inside the onisplit_action string is important
' without it the code would continue and might try to read the not existing xml file and produce an error

onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
output_folder = """..\GameDataFolder"""
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "relative path: " & onisplit_action
' expected logmessage:
' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location


' absolute path

'adapt paths so it works on your computer
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "absolute path: " & onisplit_action
' expected logmessage:
' INFO : absolute path: cmd /C start OniSplit.exe -extract:xml "F:\Program Files (x86)\Oni\Edition\GameDataFolder" "F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location


' you can also lunch bat files

onibat = "cmd /C start run_wind.bat"
onilocation = "F:\Program Files (x86)\Oni\Edition"
XSIUtils.LaunchProcess onibat, 0, onilocation
' alternative to cmd: call onisplit via winmgmts

' slightly modified code from that site
' logmessage "onisplit finished." will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers 
' if you are going to use this method consider to extent the code to check if input file and output directory exist
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
osp_action = "-extract:xml"
osp_output = """C:\OniAE\Edition\GameDataFolder"""
osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
logmessage osp_total

strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objWMIService.Create osp_total, null, null, intProcessID
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")

' wait 3 second to find event - should be enough time for single actions on a slow computer
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")

Do Until i = 1
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
        i = 1
    End If
Loop
logmessage "onisplit finished."
' now you can work with the extracted xml file


XSI Mod Tool PPG.png
' [23] building a form (PPG)

dim oPSet, oPPGLayout, oItem, PPG_exist

' set check value to 0
PPG_exist = 0

' check if PPG exists
for each prop in ActiveProject.ActiveScene.Root.Properties
	logmessage prop.name
	if instr(1, prop.name, "my_new_PPG") > 0 then
		PPG_exist = 1
		logmessage "found " & """" & prop.name & """"
	end if
next

' create PPG if it doesn't exist
if PPG_exist = 0 then
	set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_new_PPG")
	set oPPGLayout = oPSet.PPGLayout
	
	' setup PPG parameters
	' ###################################################################################
	' create checkbox and remove key symboles by setting parameter "Animatable" to false
	oPSet.AddParameter3 "Check1", siBool, 0, , , false
	oPSet.AddParameter3 "Check2", siBool, 0, , , false
	oPSet.AddParameter3 "Check3", siBool, 1, , , false
	oPSet.AddParameter3 "Check4", siBool, 1, , , false
	oPSet.AddParameter3 "text1", siString
	' last parameter of "int1" is set to ReadOnly
	oPSet.AddParameter3 "int1", siInt2, , , , false, 1 
	oPSet.AddParameter3 "int2", siInt2, , , , false, 0
	
	' add PPG items
	' ###################################################################################
	oPPGLayout.AddItem "text1", "Hi there!"
	oPPGLayout.AddItem "int1", "number 1"
	oPPGLayout.AddItem "int2", "number 2"
	oPPGLayout.AddGroup "4 checkboxes", true
	oPPGLayout.AddRow
		oPPGLayout.AddItem "Check1", "C1"
		oPPGLayout.AddItem "Check3", "C3"
	oPPGLayout.EndRow
	oPPGLayout.AddRow
		oPPGLayout.AddItem "Check2", "C2"
		oPPGLayout.AddItem "Check4", "C4"
	oPPGLayout.EndRow
	oPPGLayout.EndGroup
	
	oPPGLayout.AddButton("log_values", "log PPG values").setAttribute siUICX, 120
	
	oPPGLayout.Logic = "sub log_values_OnClicked" & vbCrlf & _
	"		logmessage ""text 1 = "" & getvalue(""my_new_PPG.text1"")" & vbCrlf & _
	"		logmessage ""number 1 = "" & getvalue(""my_new_PPG.int1"")" & vbCrlf & _
	"		logmessage ""C1 = "" & getvalue(""my_new_PPG.Check1"")" & vbCrlf & _
	"	end sub"
	
	oPPGLayout.Language = "VBScript" 'Optional because this is the default

	' open PPG
	InspectObj oPSet		
else
	' open that PPG if it already exist
	InspectObj "my_new_PPG"
end if 
' set values from outside the PPG
setvalue "my_new_PPG.text1", "any text could stand here"
setvalue "my_new_PPG.int1", "42"
setvalue "my_new_PPG.check1", true

' get values from outside the PPG
logmessage getvalue("my_new_PPG.text1")
logmessage getvalue("my_new_PPG.int1")
logmessage getvalue("my_new_PPG.check1")


XSI Mod Tool UserDataBlob.png
' [24] UserDataBlob

' you can attach data to objects, e.g. new properties for the scene root or Oni Trigger Volumes
' the so-called "UserDataBlob" can't be saved in *.dae, so save the whole scene to keep your progress

' check if my property exist
found_my_prop = 0
set oProps = ActiveProject.ActiveScene.Root
logmessage "root name: " & ActiveProject.ActiveScene.Root
for each prop in oProps.Properties
	' remove apostroph in line beneath to log all root properties
 	'LogMessage prop.Name
	if instr(1, prop.Name, "my_prop") = 1 then
		found_my_prop = 1
	end if
next

' prop doesn't exist, create it
if found_my_prop = 0 then
	' create property (must also have content, in this case "yes")
	oProps.AddProperty( "UserDataBlob", , "my_prop" ).value = "yes"
	logmessage "created my new prop"
end if

' prop exists, change/read its value
if found_my_prop = 1 then
	' change property
	oProps.Properties( "my_prop" ).value = "no"

	' read property
	logmessage oProps.Properties( "my_prop" ).value
	' if you want to read a property from another script be sure that it was already created
	' reuse code under "check if my property exist"
end if


' [25] getting the position of points (with selection mode point)

' a point must be selected
' gets xyz position of first selected point of the first selected object
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
logmessage Selection(0).SubComponent.ComponentCollection(0).position.z


' [26] getting the position of points (with selection mode object)

' an object must be selected
' gets xyz position of point 0 of the first selected object
logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
logmessage selection(0).activeprimitive.geometry.Points(0).Position.z


' [27] getting and setting the position of points (without selection) right after object creation

' point positions are relative to the object's center
' to get the absolute point positions add center to point
' to set the absolute point positions subtract center from point

set oRoot = application.activeproject.activescene.root
set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )

' to test our code move center to somewhere else
Translate oObj, 9, 11, 13, siRelative, siGlobal, siCtr, siXYZ, , , , , , , , , , 0
SaveKey oObj & ".kine.local.posx," & oObj & ".kine.local.posy," & oObj & ".kine.local.posz", 1, , , , True

FreezeObj oObj
set oGeometry = oObj.activeprimitive.geometry
aPositions = oGeometry.Points.PositionArray
' get old position
'                                         (xyz, point)
logmessage "old point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
logmessage "old point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
logmessage "old point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")

' set new position
aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")

' update the array
oGeometry.Points.PositionArray = aPositions
' get new position
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
logmessage "new point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
logmessage "new point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")

' INFO : old point 0 posx: -4
' INFO : old point 0 posy: -4
' INFO : old point 0 posz: -4

' INFO : new point 0 posx: -7
' INFO : new point 0 posy: -7
' INFO : new point 0 posz: -7


' [28] getting the scaling, rotation and position of selected objects

logmessage "mesh name: " & selection(0)
logmessage selection(0).sclx.value
logmessage selection(0).scly.value
logmessage selection(0).sclz.value
logmessage selection(0).rotx.value
logmessage selection(0).roty.value
logmessage selection(0).rotz.value
logmessage selection(0).posx.value
logmessage selection(0).posy.value
logmessage selection(0).posz.value


' [29] getting the scaling, rotation, and position of not selected objects

' GetValue("NAME.kine.global.rotx")
' NAME must be the exact mesh name
' let's say you want the data of one character's pelvis
logmessage GetValue("pelvis.kine.global.sclx")
logmessage GetValue("pelvis.kine.global.scly")
logmessage GetValue("pelvis.kine.global.sclz")
logmessage GetValue("pelvis.kine.global.rotx")
logmessage GetValue("pelvis.kine.global.roty")
logmessage GetValue("pelvis.kine.global.rotz")
logmessage GetValue("pelvis.kine.global.posx")
logmessage GetValue("pelvis.kine.global.posy")
logmessage GetValue("pelvis.kine.global.posz")


' [30] decrypting merged integer flags

' for example the BINACJBOTrigger Volume.xml file has flags as strings for the <Flags> tag
' and integer values for the <Teams> tag whereby the values are merged to one value
' so if <Teams> tag holds "101" we have to think what flags it holds, the flags can be broken down with following code

' x: hypothetical value of <Teams>
x=101
' 101 = 64 + 32 + 4 + 1

x_255
sub x_255
	if x > 255 then
		msgbox "Flags bigger than 255 are not allowed.", ,"error"
		exit sub
	end if
	if x >= 128 and x <= 255 then
		logmessage "128"
		x = x - 128
	end if
	if x >= 64 and x < 128 then
		logmessage "64"
		x = x - 64
	end if
	if x >= 32 and x < 64 then
		logmessage "32"
		x = x - 32
	end if
	if x >= 16 and x < 32 then
		logmessage "16"
		x = x - 16
	end if
	if x >= 8 and x < 16 then
		logmessage "8"
		x = x - 8
	end if
	if x >= 4 and x < 8 then
		logmessage "4"
		x = x - 4
	end if
	if x >= 2 and x < 4 then
		logmessage "2"
		x = x - 2
	end if
	if x = 2 then
		logmessage "2"
		x = x - 2
	end if
	if x = 1 then
		logmessage "1"
		x = x - 1
	end if
	if x = 0 then
		logmessage "finished check"
	end if
end sub

' INFO : 64
' INFO : 32
' INFO : 4
' INFO : 1
' INFO : finished check


' [31] disabling PPG popups

' for example if code of a script button creates numerous objects in one go, the same number of property pages (PPG) can appear
' for user convenience those PPG popups can be disabled

' disable PPG popup
Preferences.SetPreferenceValue "Interaction.autoinspect", false

' creates the cube mesh but no PPG will show up
CreatePrim "Cube", "MeshSurface"

' enable PPG popup again
Preferences.SetPreferenceValue "Interaction.autoinspect", true


' [32] disabling logmessages

' msglogverbos is probably already set to false by default
' could be set at the start of a final script to increase performance
setvalue "preferences.scripting.msglogverbose", false
setvalue "preferences.scripting.msglog", false


' [33] getting bounding box values

' this could be useful to create a bounding box for OFGA files

' let's get the bounding box of a simple cylinder
' the output values will be absolute positions
CreatePrim "Cylinder", "MeshSurface"

dim xmin, ymin, zmin, xmax, ymax, zmax
dim list
'if you use SelectionList the objects will be treated as one single object
'set list = GetValue( "SelectionList" )
set list = GetValue( selection(0) )
GetBBox list, TRUE, xmin, ymin, zmin, xmax, ymax, zmax
LogMessage "Lower Bound: " & xmin & " / " & ymin & " / " & zmin
LogMessage "Upper Bound: " & xmax & " / " & ymax & " / " & zmax

' expected output:
' INFO : Lower Bound: -1 / -2 / -1
' INFO : Upper Bound: 1 / 2 / 1


' [34] open an explorer window

' this could be used open an output folder ...
strPath = "explorer.exe /e," & XSIUtils.ResolvePath("$SI_HOME/")
Set objShell = CreateObject("Wscript.Shell")
objShell.Run strPath

' to select a generated file inside the folder:
strPath = "explorer.exe /select," & "C:\folder\test.txt"


SelectNeighborObj obj 5
' [35] working with hierarchies 

' let's be sure we have an object selected
if selection.count > 0 and typename(selection(0)) = "X3DObject" then
 
	' the object itself
	logmessage selection(0)
	
	
	' the parent object
	logmessage selection(0).parent
	
	
	' the children
	for each n in selection(0).FindChildren( , , , 0 )
		logmessage n
	next
	' the 4the parameter of FindChildren can be set to 0 or 1
	' with 0 you get all direct children
	' with 1 you get everything located under that object no matter how deep the level


	' count all objects
	logmessage "children: " & selection(0).FindChildren( , , , 1).count
	' caution: if the 3rd parameter is set to siMeshFamily
	' then the selected object will be also counted

	' find the root object of the hierarchy, any object could be selected
	SelectNeighborObj selection(0), 4


	' go through the hierarchy in different directions
	' SelectNeighborObj [parameter1], [parameter2], [parameter3], [parameter4]
	' options of the second parameters
	' 0 go one level up
	' 1 go one level down
	' 2 go to next silbing
	' 3 go to previous silbing
	' 4 go to highest level
	' 5 go to deepest level of current selected obj
	' 5: if there are more than one children, the path goes always for the first (left) child (see image)


	' select the hierarchy as a whole 
	SelectObj "left_thigh", "TREE", true
	' first parameter could be any object of the hierarchy
	' after using this the selection of an Oni character will be "B:pelvis"
else
	msgbox "no object was selected"	
end if


[36] drag and drop support for specific oni files

function XSILoadPlugin( in_reg )
	in_reg.Author = ""
	in_reg.Name = "Oni drag and drop support"
	in_reg.Email = ""
	in_reg.URL = ""
	in_reg.Major = 1
	in_reg.Minor = 0

	in_reg.RegisterEvent "support_oni_DnD",siOnDragAndDrop
 	'RegistrationInsertionPoint - do not remove this line

	XSILoadPlugin = true
end function

function XSIUnloadPlugin( in_reg )
	dim strPluginName
	strPluginName = in_reg.Name
	Application.LogMessage strPluginName & " has been unloaded.",siVerbose
	XSIUnloadPlugin = true
end function

function support_oni_DnD_OnEvent( in_ctxt )
	Set objFSO = CreateObject("Scripting.FileSystemObject")

	FullFilePath = in_ctxt.GetAttribute("DragSource")
 	FileExt = objFSO.GetExtensionName(FullFilePath)
	FileName = objFSO.GetBaseName(FullFilePath)

 	if instr(LCase(FileExt), "oni") = 1 then
 		in_ctxt.SetAttribute "DragSourceSupported", true
 
 		' next line prevents the code from being repeated
	 	if in_ctxt.GetAttribute("DragAndDropAction") = 1 then

			logmessage "What I'm supposed to do with that oni?"
			' do more stuff here
			' ..................
			' e.g. telling onisplit to extract the oni

		end if
	end if

	support_oni_DnD_OnEvent = true
end function

Important:

Mod Tool has its own drag and drop event handler for dae and xml file.

If we succeed in modifying and shipping that new file, we could finally trigger scripts with those files and process them.

The original file can be found at:

logmessage XSIUtils.ResolvePath("$XSI_HOME/") & "Application\DSScripts\Model.vbs"
' C:\Softimage\Softimage_Mod_Tool_7.5\Application\DSScripts\Model.vbs

A backup of the original file can be found here: https://dl.dropboxusercontent.com/u/139715/OniGalore/ModToolScript/Model.vbs (dead link)

There are two issues that can be fixed by modifying this file.

  • import of xml files created by onisplit
  • import of dae and xml files with relative path from a html page

Relative file pathes will be transformed to absolute pathes whereby an unwanted "file:///" is added.

A solution to is to transform the path again.

if instr(in_Filename,"file:///") = 1 then
	in_Filename = replace(in_Filename, "file:///", "")
end if

Following function is always triggered, so we will modify it.

sub ImportDotXSIProc( in_Filename, in_Parent )
	if instr(in_Filename,"file:///") = 1 then
		in_Filename = replace(in_Filename, "file:///", "")
	end if
	Set objFSO = CreateObject("Scripting.FileSystem<Object")
	FileExt = objFSO.GetExtensionName(in_Filename)

	if application.license = "Avid 3D" then
		SIImportDotXSIFile in_Filename, in_Parent
	else
		Set oFS = CreateObject("Scripting.FileSystemObject") 
		if oFS.FileExists(in_Filename) = True then
			if FileExt = "xml" then
				' is this an Oni xml file ?
		 		Set xmlDoc = CreateObject("Microsoft.XMLDOM")
				xmlDoc.Async = "False"
				xmlDoc.Load(in_Filename)
				if xmlDoc.selectNodes("Oni").length > 0 then
					logmessage "Oni xml file detected"
					read_xml "OFGA", in_Filename
				end if
				exit sub
			end if

The lines in red has been added.


[37] dae export

set oProps = ActiveProject.ActiveScene.Root.Properties
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
	logmessage "Export settings not set"
	CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
else
	logmessage "Export settings present"
end if

' sets the extension to dae
SetValue "ExportCrosswalkOptions.Format", 1
' set export path and file name
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
' selection only
SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
' export
ExportCrosswalk "ExportCrosswalkOptions"


[38] fbx export

FBXExportLights (false)
FBXExportSelection (true)
' mark FBXExport and hit F1 to get more options
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )


[39] import an image clip only once

image_path =  "C:\Users\RRM\Desktop\white_512_512.tga"

Set FSO = CreateObject("Scripting.FileSystemObject")
image_clip = FSO.GetBaseName(image_path) & "_" & FSO.GetExtensionName(image_path)
logmessage image_clip

if not typename(ActiveProject.ActiveScene.ImageClips(image_clip)) = "Nothing" then
	logmessage "found image clip"
else
	logmessage "image clip not yet present, going to import ..."
	
	SICreateImageClip image_path
 	' or as script object:
	'set oImage = AddImageSource (image_path)
	'set oImageClip = AddImageClip (oImage)
end if


[40] layers

how to access scene layers

To access scene layers switch from Main Control Panel (MCP) to Keying Panel/Layers (KP/L) and then click "Scene".

After the user became used to layers, they can help to organize work in a scene. This is especially interesting for some scripting tasks like getting TRGV/FURN data for xml files.

' get current layer
GetCurrentLayer CurrentLayer
logmessage CurrentLayer


' create a new layer named "FurnLayer" if it doesn't already exist
' make new layer the current layer
if typename(ActiveProject.ActiveScene.Layers("FurnLayer")) = "Nothing" then
	SICreateLayer , "FurnLayer", FurnLayer
	SetCurrentLayer FurnLayer
else
	SetCurrentLayer "Layers.FurnLayer"
end if


' get objects in FurnLayer and count objects
' exit sub if there are no objects
SelectMembers "Layers.FurnLayer"
if selection.count = 0 then
	MsgBox "No OFGA data found to export.", , "Export canceled"
	exit sub
end if


' if there are Furn objects
for each obj in selection
	' do something e.g. create xml
next


' [41] onisplit update

' this downloads and unzips the last attachment from the onisplit page
output_dir = "F:\test\"

' get onisplit link ################################################
set xmlhttp = createobject ("msxml2.xmlhttp.3.0")
xmlhttp.open "get", "http://mods.oni2.net/node/38/index.html", false
xmlhttp.send

webtext = xmlhttp.responseText
 webtext = replace(replace(webtext, vbcr,""),vblf,"")
 webtext = split(webtext, "<table class=""sticky-enabled""> <thead><tr><th>Attachment</th>" )(1)
 webtext = split(webtext, "</td> </tr></tbody></table>") (0)

link_start = instrrev(webtext, "http")
link_end = instrrev(webtext, ".zip"" type") + 4 ' + zip
' mid (text, begin, length)
link = mid(webtext, link_start, link_end - link_start)
logmessage "last link in attachment: " & link

dim xHttp: Set xHttp = createobject("Microsoft.XMLHTTP")
dim bStrm: Set bStrm = createobject("Adodb.Stream")
xHttp.Open "GET", link, False
xHttp.Send

' get file name ###################################################
hdr = xHttp.getResponseHeader("Content-Disposition")
hdr = replace (hdr, "attachment; filename=""", "")
hdr = replace (hdr, """", "")
logmessage hdr

' save file #######################################################
filename = output_dir & hdr
with bStrm
	.type = 1 '//binary
	.open
	.write xHttp.responseBody
	.savetofile filename, 2 '//overwrite
end with

' extract file ####################################################

UnzipFile filename, output_dir

Function UnzipFile(ZipFile, ExtractTo)
	Set fso = CreateObject("Scripting.FileSystemObject")
	If NOT fso.FolderExists(ExtractTo) Then
		fso.CreateFolder(ExtractTo)
	End If
	set objShell = CreateObject("Shell.Application")
	set FilesInZip=objShell.NameSpace(ZipFile).items
	objShell.NameSpace(ExtractTo).CopyHere(FilesInZip)
	Set fso = Nothing
	Set objShell = Nothing
End Function


' [42] get vertex color

set oGeometry = selection(0).activeprimitive.geometry
set oTriangles = oGeometry.Triangles

for each oTriangle in oTriangles
   logmessage oTriangle.Name 

   for each oVertex in oTriangle.Points
       set oColor = oVertex.Color
       logmessage vbTab & "RGBA(" & _
          oColor.red & "," & _
          oColor.green & "," & _
          oColor.blue & "," & _
          oColor.alpha & ")"
   next
next


' [43] get all used textures

DeleteAllUnusedMaterials
DeleteUnusedImageClips
'DeleteUnusedImageSources
for each i in Application.ActiveProject.ActiveScene.ExternalFiles
	if i.filetype = "Pictures" then
		logmessage i
	end if
next


'  [44] get global point position

set oObj = selection(0)
set oTrans = oObj.Kinematics.Local.Transform 
set oPoint0 = oObj.ActivePrimitive.Geometry.Points(0)
set oPoint7 = oObj.ActivePrimitive.Geometry.Points(7)
set oPos0 = oPoint0.Position
set oPos7 = oPoint7.Position

' scaling must be frozen to 1 before we can calculate the size from local values
ResetTransform selection(0), siCtr, siScl, siXYZ

logmessage "local p0: "& oPos0.X & " " & oPos0.Y & " " & oPos0.Z
set oGlobalPos0 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos0)
logmessage "global p0: "& oGlobalPos0.X & " " & oGlobalPos0.Y & " " & oGlobalPos0.Z

logmessage "local p7: "& oPos7.X & " " & oPos7.Y & " " & oPos7.Z
set oGlobalPos7 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos7)
logmessage "global p7: "& oGlobalPos7.X & " " & oGlobalPos7.Y & " " & oGlobalPos7.Z
 
logmessage "size: " &		oPos7.X - oPos0.X & " " & _
							oPos7.Y - oPos0.Y & " " & _
							oPos7.Z - oPos0.Z
' with a rotation of: -3,8792 16,4039 -13,5017
' INFO : local p0: -4 -4 -4
' INFO : local p7: 4 4 4
' INFO : global p0: -5,74764582364017 -3,00250371537919 -2,43916767056426 ' TRGV start point
' INFO : global p7: 5,74764582364017 3,00250371537919 2,43916767056426
' INFO : size: 8 8 8


Interactive Creative Environment (ICE)

tutorials:

Wanted knowledge

  • How to set up particles in Mod Tool? We still need an Oni particle editor!
  • How to rig models and then extract the animation data?
  • How to make complex new UV maps?