Mod Tool: Difference between revisions

Update notice w/ summary
mNo edit summary
(Update notice w/ summary)
Tags: Mobile edit Mobile web edit
 
(95 intermediate revisions by 3 users not shown)
Line 1: Line 1:
==General information==
{{update|This article contains links to the now defunct Oni Central Forum. Sections: Selected Wisdom}}
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).
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.
 
{| class="wikitable" style="float:right;"
Beside the free version, there's also the retail and student version "Autodesk Softimage [year]"
|width="200px"|
: Those versions are also available as 64-bit versions.
;sub pages
: If you are a student you can get Autodesk Software for free if your school is a partner of Autodesk.
: [[Mod_Tool/Scripting]]
:: In that case you can register with your email address given by your school.
: [[Mod_Tool/Rigging]]
 
: [[Mod_Tool/OniTools_addon]]
But in the very most cases "Mod Tool" isn't really in disadvantage towards the retail version when it comes to modding Oni.
: [[Mod_Tool/Oni_level_rebuilder]]
|}


There's also the possibility to get files that extent Mod Tool's range of functions.
==Installation==
: Those are scripts (.vbs, .js), toolbars (.xsitb) and addons (.xsiaddon).
Full name: "Autodesk Softimage Mod Tool". Usually we just call it "Mod Tool" or "XSI", the old name of the program.
: They can be create on one's own and also shared with other users.


'''Download'''
* '''Get it from [https://www.moddb.com/downloads/autodesk-softimage-mod-tool-75 Mod DB].'''
* 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.


'''Wanted knowledge:'''
'''Newer Windows installations usually require .NET 2.0 to be installed before the XSI installer can fully run.'''
* How to setup particle in Mod Tool? We still need an Oni particle editor!
: Downloading and installing .Net 2.0 manually from the internet might not work.
* How to rig models and then extract the animation data?
: '''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==
===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 58:




'''[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 150:
  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 192:
** 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 248:


===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 259:
** 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 282:
* 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.


''Rotation flips'' might appear.


===Scripting===
2) *.exp and *.dae save data differently.
* open the Script Editor: [Alt] + [4]
 
* run the current code in the Script Editor window: [F5]
*.exp files store the frames as they are.
* 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"


*.dae file store their actions (rotations/translation/...) in seconds and eventually depend on what framerate a Modder uses.


'''Links'''
* [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]-->


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


'''Notes about polygon extraction'''
Modder A uses 30 fps in his settings and exports an *.exp that is 58 frame long.
* 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 B uses 60 fps in his setting and imports the *.exp, to his surprise the animation is 116 frames long. What now?


'''Some basics of VBScript'''
If Modder B saves the *.exp under a new name would not help, the frames will be still the same.
* 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".
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".
* Vbs functions can be found [http://www.w3schools.com/vbscript/vbscript_ref_functions.asp HERE.]


' while it is possible to use + in strings,
Modder B scales the animation and saves it as dae.
' you should actually use & only for strings
 
' and + only for numbers
Now he wants to see if it worked. He opens a new scene [Strg]+[N] and loads the dae.
 
logmessage "How about ... " + "a riddle?"
 
logmessage "Where can you find following phrase in Oni? " & "The day is mine !!"
'''Summary of issue-holding aspects'''
logmessage "Did you know that Bungie likes to hide sevens? The digits of the year 2032 add up to " & cstr(2 + 0 + 3 + 2) & "."
 
* '''Frames Per Second (FPS)'''
' INFO : How about ... a riddle?
When you share *.dae files with other Modders be sure that the output formate is 60 fps. Different framerates among modders can cause confusion.
' 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.


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


'''More tips'''
An example:


* You can replace each "logmessage" with a variable. In that case don't forget the = sign.
<float_array count="10" id="pelvis_rotation_x_ANGLE-anim-input-array">
* Selection(0) means that from all selected objects the first one is taken.
    0.000000 0.166833 0.333667 0.500500 0.600600 0.934267 1.267933 1.601600 1.768433 1.935267
* Use a loop to get all objects.
</float_array>


for each n in selection
action at [s] * framerate [f/s] = frame [f]
logmessage n
next


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




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


{| class="wikitable" width="100%"
Don't use this.
| 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
 
|-
 
| logmessage selection.count
* '''Make Rotation Keys Continuous'''
| Logs the number of selected objects.
 
|-
[...]
| logmessage selection(0).Name
 
| Logs the object name.
 
|-
====Animation mixer====
| logmessage selection(0).Material.Name
This could help with creating cutscenes and complex animations (e.g. run cycles for FILM files).
| Logs the material name.
 
|-
Open the animation mixer with [Alt]+[0].
|valign=top| logmessage selection(0).Material.CurrentImageClip.source.filename.value
 
| Logs absolute path of the texture. Output example:
Further information over [http://web.archive.org/web/20170103065301/http://softimage.wiki.softimage.com/xsidocs/nla_mixer.htm#Rdw10200 HERE.]
: ''C:\Users\RRM\Desktop\TV_tex.png''
 
|-
Here are a few videos:
|valign=top| logmessage selection(0).Material.CurrentImageClip.source.Parameters("XRes").Value<br>logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
* '''https://www.youtube.com/watch?v=-xC31Q7zpM0'''
| Logs the horizontal (X) and vertical (Y) pixel length of the texture. Output example:
* https://www.youtube.com/watch?v=njLrAIpDOFU
: ''512''
* https://www.youtube.com/watch?v=PRDZaEo5CGo
|-
 
| 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).
=====Sounds=====
|-
Within the animation mixer also sounds can be added.
| 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).
Further information over [http://web.archive.org/web/20170102120711/http://softimage.wiki.softimage.com/xsidocs/audio_LoadingAudioFilesintheAnimationMixer.htm#Rdy36984 HERE.]
|-
 
| logmessage selection(0).posx.value<br>logmessage selection(0).posy.value<br>logmessage selection(0).posz.value
This could be used to synchronize sounds effect with physical actions in the scene.
|valign=top| Logs object's position (X, Y, Z).
 
|-
 
|valign=top| logmessage selection(0).rotorder.value
===Scripting===
| Logs object's rotation order. Oni characters have ZYX as rotation order.
* open the Script Editor: [Alt] + [4]
:0 = XYZ
* run the current code in the Script Editor window: [F5]
:1 = XZY
* clear the log: Edit > Clear History Log
:2 = YXZ
* Actions from button embedded code will be logged.
:3 = YZX
* Actions from code files that are linked in a button won't be logged. (This results in a performance boost.)
:4 = ZXY
* Logged stuff will be rewritten if you change the script language. (File > Preferences...)  
:5 = ZYX
* 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.
|valign=top| SelectObj <nowiki>[string | var]</nowiki>
* Right-clicking the white script box gives you also access to a few code piece, e.g. "Syntax Help" > "If..Else" or "Catch Error".
| select one or more objects. Comma serves as separator. Example:
* Mark code you want to disable ("Comment Out") or enable ("Comment Remove").
: SelectObj "grid,grid1"
* You can save your code to a file via "File" > "Save As..." or "Save Selection"
|-
 
|valign=top| ToggleSelection <nowiki>[string | var]</nowiki>
 
| Adds one or more objects from current selection. If the objects are already selected then they become subtracted from the selection. Example:
'''Links'''
: ToggleSelection "grid2,grid3"
* [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]
|-
* '''[http://web.archive.org/web/20170616035120/http://softimage.wiki.softimage.com/sdkdocs/scriptsdb/scriptsdb/scrdb_vbscript.htm many vbscript examples]'''
|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>
* '''[https://ss64.com/vb/ vbs commands]'''
|Selects everything that matches the filter and other options. Example:
* [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 "object", siCheckComponentVisibility
* [https://web.archive.org/web/20150504221146/http://activexperts.com/activmonitor/windowsmanagement/adminscripts/other/textfiles/ objFSO/objWSHShell: Scripts to manage Text Files]
:: selects all roots, lights, cameras, and geometry (meshes)
* [http://web.archive.org/web/20080905102848/http://www.kxcad.net/softimage_xsi/Softimage_XSI_Documentation/script_basics_IncludingExternalScripts.htm using external scripts]
: SelectAllUsingFilter "geometry", siCheckComponentVisibility
* [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 ?)-->]
:: selects all geometry (meshes)
<!--* [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]-->
|-
 
| DeselectAll
 
| deselects everything
'''Notes about polygon extraction'''
|-
* a grid of 9 polygons (ID 0 to 8)
|valign=top| DeleteObj <nowiki>[string | var]</nowiki>
* you extract the middle polygon (ID 4)
| Deletes one or more objects. Comma used as separator. Example:
* grid's polygon of ID 8 will change to ID 4
: DeleteObj "grid"
* if you now extract 5th, the 7th gets the ID
: DeleteObj "grid,grid1"
* 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
| FreezeObj
* 1) the ID array of selected polygons must be sorted
| Makes all changes final. But saving those changes is still necessary. (Click on key symbol so it gets red.)
* 2) the extraction must start with the last ID to avoid ID shift during the process (see code pieces)
|-
| [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>
| 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.]
 
' 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.


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


{| class="wikitable" width="100%"
'''More tips'''
|width=33%| [1] creating and extending an array
 
|width=33%| [2] check selection mode
* You can replace each "logmessage" with a variable. In that case don't forget the = sign.
| [3] getting the IDs of selected polygons plus sorting them
* Selection(0) means that from all selected objects the first one is taken.
|-
* Use a loop to get all objects.
| [4] converting euler rotations (in degrees) to quaternions
 
| [5] converting quaternions to euler rotations (in degrees)
for each n in selection
| [6] get and set values of the playcontrol (timeline)
logmessage n
|-
next
| [7] get keyframes
 
| [8] message box
* In order to shorten the code you might want to set script objects.
| [9] input box
: 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''.
| [10] getting the desktop path
: Then you can use any valid method or property with that ''name''.
| [11] selecting a folder
: Example:
| [12] selecting a file of a specific type
|-
set c = CreatePrim ("Cube", "MeshSurface")
| [13] reading out environment variables
| [14] check if file exist
logmessage selection(0).name
| [15] check if folder exist + create folder
logmessage selection(0).length.value
|-
logmessage c.name
| [16] write xml file
logmessage c.length.value
| [17] read xml file
| [18] read xml file (attribute/content-based)
' INFO : cube
|-
' INFO : 8
| [19] create txt file + write lines
' INFO : cube
| [20] read txt file
' INFO : 8
| [21] check onisplit version
 
 
'''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.


([[Mod_Tool/Scripting|Here]]'s another page for math related code pieces.)


' '''[2] check selection mode '''
{| class="wikitable" width="100%"
|width=33%| [1] creating and extending an array
checkfilter
|width=33%| [2] check selection mode
'use sub to make use of the exit command
| [3] getting the IDs of selected polygons plus sorting them
|-
sub checkfilter
| [4] converting euler rotations (in degrees) to quaternions
Select Case Selection.Filter.Name
| [5] converting quaternions to euler rotations (in degrees)
'caution: case-sensitive
| [6] get and set values of the playcontrol (timeline)
|-
Case "object"
| [7] get keyframes
logmessage "object mode"
| [8] message box
Case "Edge"
| [9] input box
logmessage "edge mode"
|-
Case "Vertex"
| [10] getting the desktop path
logmessage "point mode"
| [11] selecting a folder
Case "Polygon"
| [12] selecting a file of a specific type
logmessage "polygon mode"
|-
Case Else
| [13] reading out environment variables
logmessage "unknown mode"
| [14] check if file exist
exit sub
| [15] check if folder exist + create folder
End Select
|-
end sub
| [16] write xml file
 
| [17] read xml file
 
| [18] read xml file (attribute/content-based)
' '''[3] getting the IDs of selected polygons plus sorting them'''
|-
| [19] create txt file + write lines
poly_count = Selection(0).SubComponent.ComponentCollection.count
| [20] read txt file
logmessage "poly_count: " & poly_count
| [21] check onisplit version
redim selected (poly_count)
|-
logmessage "Show ID as it is."
| [22] call CMD, e.g. to use onisplit
for i=0 to poly_count - 1
| [23] building forms in ModTool
' build ID array
| [24] UserDataBlob
selected(i) = Selection(0).SubComponent.ComponentCollection(i).index
|-
logmessage selected(i)
| [25] getting the position of points (with selection mode point)
next
| [26] getting the position of points (with selection mode object)
| [27] getting and setting the position of points (without selection)
' change sort order by replacing ">" with "<"
|-
For i = 0 To poly_count - 1
| [28] getting the rotation and position of selected objects
For j = 0 To poly_count - 1
| [29] getting the rotation and position of not selected objects
If selected(i) > selected(j) Then
| [30] decrypting merged integer flags
tmp = selected(i)
|-
selected(i) = selected(j)
| [31] disabling PPG popups
selected(j) = tmp
| [32] disabling logmessages
End If
| [33] getting bounding box values
Next
|-
Next
| [34] open an explorer window
logmessage "Show sorted ID."
| [35] working with hierarchies
| [36] drag and drop (DnD) support for specific oni files
for i=0 to poly_count - 1
|-
logmessage selected(i)
| [37] dae export
next
| [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
|
|}




  ' '''[4] converting euler rotations (in degrees) to quaternions'''
  ' '''[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
   
   
dRotation.GetQuaternion (qRotation)
logmessage "output all items"
LogMessage qRotation.W
for each n in nums
  LogMessage qRotation.X
  logmessage n
LogMessage qRotation.Y
next
LogMessage qRotation.Z
logmessage "first array item holds: " & lbound(nums)
' INFO : 0,707106781186548
logmessage "last array item holds: " & ubound(nums)
' INFO : 0,707106781186547
  logmessage "-------------------------------------"
' INFO : 0
' INFO : 0
 
 
  ' '''[5] converting quaternions to euler rotations (in degrees)'''
   
   
dim qW, qX, qY, qZ, qRotation, x, y, z
   
   
qW = 0.707106781186548
' extending an array
qX = 0.707106781186547
' use "preserve" to keep the old content
qY = 0
' find next higher value
qZ = 0
plus_one = ubound(nums) + 1
redim preserve nums (plus_one)
nums(plus_one) = 4
   
   
  set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
  for each n in nums
logmessage n
next
logmessage "first array item holds: " & lbound(nums)
logmessage "last array item holds: " & ubound(nums)
   
   
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)
  ' INFO : output all items
  dim tlStart, tlEnd, tlCurrent
  ' 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 '''
   
   
  ' get values
  checkfilter
  tlStart = GetValue ("PlayControl.In")
  'use sub to make use of the exit command
tlEnd = GetValue ("PlayControl.Out")
tlCurrent = GetValue ("PlayControl.Current")
   
   
  ' set values
  sub checkfilter
  SetValue ("PlayControl.In"), 7
Select Case Selection.Filter.Name
SetValue ("PlayControl.Out"), 70
'caution: case-sensitive
SetValue ("PlayControl.Current"), 14
   
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




  ' '''[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)
  on error resume next
  logmessage "Show ID as it is."
  logmessage "--------------------------------------------------"
  for i=0 to poly_count - 1
  logmessage "X rotation keys: " & selection(0).rotx.Source.Keys.count
  ' build ID array
for each k in selection(0).rotx.Source.Keys
  selected(i) = Selection(0).SubComponent.ComponentCollection(i).index
logmessage "index: " & k.index & " // time: " & k.time & " // value: " & k.value
  logmessage selected(i)
next
  next
if err <> 0 then
  logmessage "no X rotation keys"
  end if
on error goto 0
   
   
  on error resume next
  ' change sort order by replacing ">" with "<"
  logmessage "--------------------------------------------------"
For i = 0 To poly_count - 1
logmessage "Y rotation keys: " & selection(0).roty.Source.Keys.count
  For j = 0 To poly_count - 1
for each k in selection(0).roty.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 Y 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 "Z rotation keys: " & selection(0).rotz.Source.Keys.count
next
for each k in selection(0).rotz.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 Z rotation keys"
  end if
on error goto 0
   
   
  on error resume next
  dim x, y, z, dRotation, qRotation
logmessage "--------------------------------------------------"
x = 90
logmessage "X position keys: " & selection(0).posx.Source.Keys.count
  y = 0
for each k in selection(0).posx.Source.Keys
  z = 0
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
  set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z))  
logmessage "--------------------------------------------------"
set qRotation = XSIMath.CreateQuaternion()
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
dRotation.GetQuaternion (qRotation)
  logmessage "--------------------------------------------------"
  LogMessage qRotation.W
  logmessage "Z position keys: " & selection(0).posz.Source.Keys.count
  LogMessage qRotation.X
  for each k in selection(0).posz.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 Z position keys"
' INFO : 0
end if
  ' INFO : 0
  on error goto 0
 
 
  ' '''[5] converting quaternions to euler rotations (in degrees)'''
   
   
dim qW, qX, qY, qZ, qRotation, x, y, z
   
   
' output example (0 rot keys, 2 pos keys):
qW = 0.707106781186548
qX = 0.707106781186547
qY = 0
qZ = 0
   
   
  ' INFO : --------------------------------------------------
  set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
' INFO : no X rotation keys
   
' INFO : --------------------------------------------------
qRotation.GetXYZAngleValues x, y, z
  ' INFO : no Y rotation keys
logmessage XSIMath.RadiansToDegrees(x)
' INFO : --------------------------------------------------
logmessage XSIMath.RadiansToDegrees(y)
' INFO : no Z rotation keys
logmessage XSIMath.RadiansToDegrees(z)
' INFO : --------------------------------------------------
' INFO : 89,9999999999999
' INFO : X position keys: 2
' INFO : 0
' INFO : index: 0 // time: 2 // value: 5,14534318031942
' INFO : 0
' 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."
  ' content-based search: node [child_node = 'content']
  else
' attribute-based search: node [@attribute_name = 'attribute_value']
logmessage "File doesn't exist."
  end if
 
 
  ' '''[15] check if folder exist + create folder'''
   
   
  ' if things get too long an underscore _ can be used to make a linebreak
  dim strDirectory
  '''set w1_tap = xmlDoc.selectNodes _'''
  strDirectory = "C:\test"
'''( "Oni/ImpactEffects/Impact[@Name = 'w1_tap']/Material[@Name = 'Unbreak_Glass']/ImpactEffect [Component = 'Damage']" )'''
   
   
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
   
   
  ' example 2
  ' ### or simply:
  ' use "or" for a search of various material
  dir = "C:\test"
  'set w1_tap = xmlDoc.selectNodes _
  XSIUtils.EnsureFolderExists (dir)
  ' ( "Oni/ImpactEffects/Impact/Material[@Name = 'Default' or @Name = 'Character']/ImpactEffect" )
 
 
  ' '''[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)
   
   
  ' 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" )
   
   
  for each element in w1_tap
  loop_count = xmlDoc.selectNodes( "Oni/Objects/WEAP").length
logmessage "Impact: " & element.parentNode.parentNode.getAttribute("Name")
logmessage "Material: " & element.parentNode.getAttribute("Name")
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)
 
 
  ' 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
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
  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")
'adapt paths so it works on your computer
logmessage "Material: " & element.parentNode.getAttribute("Name")
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
' outputs "tag name: tag content"
output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
logmessage element.childNodes(0).nodename & ": " & element.childNodes(0).text
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage element.childNodes(1).nodename & ": " & element.childNodes(1).text
logmessage "absolute path: " & onisplit_action
logmessage "-----------------------------------"
' expected 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>
'check for sounds
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
if element.childNodes(2).childNodes.length = 0 then
logmessage "sound not present"
else
' you can also lunch bat files
logmessage "sound is present"
logmessage "-----------------------------------"
onibat = "cmd /C start run_wind.bat"
logmessage element.childNodes(2).childNodes(0).nodename & ": " & element.childNodes(2).childNodes(0).text
onilocation = "F:\Program Files (x86)\Oni\Edition"
logmessage element.childNodes(2).childNodes(1).nodename & ": " & element.childNodes(2).childNodes(1).text
XSIUtils.LaunchProcess onibat, 0, onilocation
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
' alternative to cmd: call onisplit via winmgmts
end if
logmessage "-----------------------------------"
   
   
' 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]
' check for particle
' ''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 element.childNodes(3).childNodes.length = 0 then
' if you are going to use this method consider to extent the code to check if input file and output directory exist
  logmessage "no particle present"
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
else
osp_action = "-extract:xml"
logmessage "number of particle: " & element.childNodes(3).childNodes.length
osp_output = """C:\OniAE\Edition\GameDataFolder"""
for each particle_section in element.childNodes(3).childNodes
osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
logmessage "-----------------------------------"
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
for each particle_tag in particle_section.childnodes
  logmessage osp_total
logmessage particle_tag.nodename & ": " & particle_tag.text
next
next
end if
logmessage "==================================="
  next
   
   
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
  ' INFO : Impact: w1_tap
  Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
' INFO : Material: Unbreak_Glass
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")
' INFO : Component: Damage
   
' INFO : Modifier: Any
  Do Until i = 1
' INFO : -----------------------------------
    Set objLatestProcess = colMonitoredProcesses.NextEvent
' INFO : sound not present
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
  ' INFO : -----------------------------------
        i = 1
' INFO : number of particle: 2
    End If
' INFO : -----------------------------------
  Loop
' INFO : Name: d__GLASSCRACK
  logmessage "onisplit finished."
' INFO : Orientation: 0
  ' now you can work with the extracted xml file
  ' 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 : ===================================




{| border=0 cellpadding=0 style="float:right"
' '''[] remove xml nodes'''
| [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)'''
  Set objXMLDoc = CreateObject("Microsoft.XMLDOM")  
objXMLDoc.async = False
   
   
  dim oPSet, oPPGLayout, oItem, PPG_exist
  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
   
   
  ' set check value to 0
  objXMLDoc.Save(XMLFile)
  PPG_exist = 0
 
 
  ' '''[19] create txt file + write lines'''
   
   
  ' check if PPG exists
  txt_location = "C:\Softimage\Softimage_Mod_Tool_7.5\test.txt"
for each prop in ActiveProject.ActiveScene.Root.Properties
Set fso = CreateObject ("Scripting.FileSystemObject")
logmessage prop.name
Set wText = fso.CreateTextFile (txt_location, 1)
if instr(1, prop.name, "my_new_PPG") > 0 then
wText.WriteLine "I'm a test file."
PPG_exist = 1
wText.WriteLine "Yo!"
logmessage "found " & """" & prop.name & """"
wText.Close
end if
 
next
 
' '''[20] read txt file'''
Set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Softimage\Softimage_Mod_Tool_7.5\test.txt", 1)
   
   
  ' create PPG if it doesn't exist
  do while not objFileToRead.AtEndOfStream
if PPG_exist = 0 then
    strLine = objFileToRead.ReadLine()
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_new_PPG")
    logmessage strLine
set oPPGLayout = oPSet.PPGLayout
loop
' INFO : I'm a test file.
' setup PPG parameters
' INFO : Yo!
' ###################################################################################
' create checkbox and remove key symboles by setting parameter "Animatable" to false
objFileToRead.Close
oPSet.AddParameter3 "Check1", siBool, 0, , , false
Set objFileToRead = Nothing
oPSet.AddParameter3 "Check2", siBool, 0, , , false
 
oPSet.AddParameter3 "Check3", siBool, 1, , , false
 
oPSet.AddParameter3 "Check4", siBool, 1, , , false
' '''[21] check onisplit version'''
oPSet.AddParameter3 "text1", siString
' last parameter of "int1" is set to ReadOnly
Set objFSO = CreateObject("Scripting.FileSystemObject")
oPSet.AddParameter3 "int1", siInt2, , , , false, 1
logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")
oPSet.AddParameter3 "int2", siInt2, , , , false, 0
' result looks like this:
' add PPG items
' INFO : 0.9.59.0
' ###################################################################################
 
oPPGLayout.AddItem "text1", "Hi there!"
 
oPPGLayout.AddItem "int1", "number 1"
' '''[22] call CMD, e.g. to lunch onisplit or the game'''
oPPGLayout.AddItem "int2", "number 2"
oPPGLayout.AddGroup "4 checkboxes", true
' relative path
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
  ' the "GameDataFolder" isn't inside the "install" folder
InspectObj oPSet
  ' so we will use ..\ to go one folder backwards
  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
  ' additional quote signs tells the program where the  
logmessage getvalue("my_new_PPG.text1")
  ' paths strings start and end in case the path contains spaces
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
  ' if you are going to use the xml file right after its extraction (which is likely)
  ' the so-called "UserDataBlob" can't be saved in *.dae, so save the whole scene to keep your progress
  ' 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
   
   
  ' check if my property exist
  onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
  found_my_prop = 0
  input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
  set oProps = ActiveProject.ActiveScene.Root
  output_folder = """..\GameDataFolder"""
  logmessage "root name: " & ActiveProject.ActiveScene.Root
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
  for each prop in oProps.Properties
  logmessage "relative path: " & onisplit_action
' remove apostroph in line beneath to log all root properties
  ' expected logmessage:
  'LogMessage prop.Name
' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
if instr(1, prop.Name, "my_prop") = 1 then
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
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
  ' absolute path
  if found_my_prop = 1 then
' change property
'adapt paths so it works on your computer
oProps.Properties( "my_prop" ).value = "no"
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:
' <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>
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
   
   
' read property
' you can also lunch bat files
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
  onibat = "cmd /C start run_wind.bat"
' gets xyz position of first selected point of the first selected object
  onilocation = "F:\Program Files (x86)\Oni\Edition"
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
  XSIUtils.LaunchProcess onibat, 0, onilocation
  logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
  logmessage Selection(0).SubComponent.ComponentCollection(0).position.z


 
  ' alternative to cmd: call onisplit via winmgmts
  ' '''[26] getting the position of points (with selection mode object)'''
   
   
  ' an object must be selected
  ' 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]
  ' gets xyz position of point 0 of the first selected object
' ''logmessage "onisplit finished."'' will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
  ' if you are going to use this method consider to extent the code to check if input file and output directory exist
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
  osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.z
osp_action = "-extract:xml"
 
  osp_output = """C:\OniAE\Edition\GameDataFolder"""
 
  osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
  ' '''[27] getting and setting the position of points (without selection) right after object creation'''
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
  logmessage osp_total
   
   
  ' point positions are relative to the object's center
  strComputer = "."
  ' to get the absolute point positions add center to point
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
  ' to set the absolute point positions subtract center from point
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'")
   
   
  set oRoot = application.activeproject.activescene.root
  Do Until i = 1
  set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
    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
 
 
[[Image:XSI_Mod_Tool_PPG.png|thumb]]
 
' '''[23] building a form (PPG)'''
   
   
  ' to test our code move center to somewhere else
  dim oPSet, oPPGLayout, oItem, PPG_exist
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 check value to 0
set oGeometry = oObj.activeprimitive.geometry
  PPG_exist = 0
  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
  ' check if PPG exists
  aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
  for each prop in ActiveProject.ActiveScene.Root.Properties
aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
logmessage prop.name
aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
if instr(1, prop.name, "my_new_PPG") > 0 then
PPG_exist = 1
logmessage "found " & """" & prop.name & """"
end if
next
   
   
  ' update the array
  ' create PPG if it doesn't exist
  oGeometry.Points.PositionArray = aPositions
  if PPG_exist = 0 then
' get new position
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_new_PPG")
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
set oPPGLayout = oPSet.PPGLayout
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")
' setup PPG parameters
' ###################################################################################
' INFO : old point 0 posx: -4
' create checkbox and remove key symboles by setting parameter "Animatable" to false
' INFO : old point 0 posy: -4
oPSet.AddParameter3 "Check1", siBool, 0, , , false
' INFO : old point 0 posz: -4
oPSet.AddParameter3 "Check2", siBool, 0, , , false
oPSet.AddParameter3 "Check3", siBool, 1, , , false
' INFO : new point 0 posx: -7
oPSet.AddParameter3 "Check4", siBool, 1, , , false
' INFO : new point 0 posy: -7
oPSet.AddParameter3 "text1", siString
' INFO : new point 0 posz: -7
' last parameter of "int1" is set to ReadOnly
 
oPSet.AddParameter3 "int1", siInt2, , , , false, 1
 
oPSet.AddParameter3 "int2", siInt2, , , , false, 0
' '''[28] getting the scaling, rotation and position of selected objects'''
' 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
   
   
logmessage "mesh name: " & selection(0)
' open PPG
logmessage selection(0).sclx.value
InspectObj oPSet
  logmessage selection(0).scly.value
  else
logmessage selection(0).sclz.value
' open that PPG if it already exist
logmessage selection(0).rotx.value
InspectObj "my_new_PPG"
logmessage selection(0).roty.value
  end if
logmessage selection(0).rotz.value
logmessage selection(0).posx.value
logmessage selection(0).posy.value
  logmessage selection(0).posz.value


 
  ' set values from outside the PPG
  ' '''[29] getting the scaling, rotation, and position of not selected objects'''
setvalue "my_new_PPG.text1", "any text could stand here"
setvalue "my_new_PPG.int1", "42"
setvalue "my_new_PPG.check1", true
   
   
  ' GetValue("NAME.kine.global.rotx")
  ' get values from outside the PPG
' NAME must be the exact mesh name
  logmessage getvalue("my_new_PPG.text1")
' let's say you want the data of one character's pelvis
  logmessage getvalue("my_new_PPG.int1")
  logmessage GetValue("pelvis.kine.global.sclx")
  logmessage getvalue("my_new_PPG.check1")
  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")


[[Image:XSI_Mod_Tool_UserDataBlob.png|thumb]]


  ' '''[30] decrypting merged integer flags'''
  ' '''[24] UserDataBlob'''
   
   
  ' for example the [[XML:BINA/OBJC/TRGV|BINACJBOTrigger Volume.xml]] file has flags as strings for the <Flags> tag
  ' you can attach data to objects, e.g. new properties for the scene root or Oni Trigger Volumes
  ' and integer values for the <Teams> tag whereby the values are merged to one value
  ' the so-called "UserDataBlob" can't be saved in *.dae, so save the whole scene to keep your progress
' 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>
  ' check if my property exist
  x=101
  found_my_prop = 0
  ' 101 = 64 + 32 + 4 + 1
  set oProps = ActiveProject.ActiveScene.Root
   
  logmessage "root name: " & ActiveProject.ActiveScene.Root
  x_255
  for each prop in oProps.Properties
sub x_255
' remove apostroph in line beneath to log all root properties
  if x > 255 then
  'LogMessage prop.Name
msgbox "Flags bigger than 255 are not allowed.", ,"error"
  if instr(1, prop.Name, "my_prop") = 1 then
  exit sub
  found_my_prop = 1
  end if
  end if
if x >= 128 and x <= 255 then
next
logmessage "128"
x = x - 128
' prop doesn't exist, create it
  end if
if found_my_prop = 0 then
if x >= 64 and x < 128 then
' create property (must also have content, in this case "yes")
logmessage "64"
  oProps.AddProperty( "UserDataBlob", , "my_prop" ).value = "yes"
x = x - 64
  logmessage "created my new prop"
end if
end if
  if x >= 32 and x < 64 then
logmessage "32"
' prop exists, change/read its value
x = x - 32
if found_my_prop = 1 then
end if
  ' change property
if x >= 16 and x < 32 then
  oProps.Properties( "my_prop" ).value = "no"
logmessage "16"
x = x - 16
  ' read property
  end if
  logmessage oProps.Properties( "my_prop" ).value
  if x >= 8 and x < 16 then
  ' if you want to read a property from another script be sure that it was already created
logmessage "8"
  ' reuse code under "check if my property exist"
x = x - 8
end if
  end if
 
  if x >= 4 and x < 8 then
 
logmessage "4"
' '''[25] getting the position of points (with selection mode point)'''
x = x - 4
  end if
' a point must be selected
  if x >= 2 and x < 4 then
' gets xyz position of first selected point of the first selected object
logmessage "2"
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
x = x - 2
logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
end if
logmessage Selection(0).SubComponent.ComponentCollection(0).position.z
if x = 2 then
 
logmessage "2"
 
x = x - 2
  ' '''[26] getting the position of points (with selection mode object)'''
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
  ' an object must be selected
  ' INFO : 32
  ' gets xyz position of point 0 of the first selected object
  ' INFO : 4
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
  ' INFO : 1
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
  ' INFO : finished check
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.z




  ' '''[31] disabling PPG popups'''
  ' '''[27] getting and setting the position of points (without selection) right after object creation'''
   
   
  ' for example if code of a script button creates numerous objects in one go, the same number of property pages (PPG) can appear
  ' point positions are relative to the object's center
  ' for user convenience those PPG popups can be disabled
' to get the absolute point positions add center to point
  ' to set the absolute point positions subtract center from point
   
   
  ' disable PPG popup
  set oRoot = application.activeproject.activescene.root
  Preferences.SetPreferenceValue "Interaction.autoinspect", false
  set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
   
   
  ' creates the cube mesh but no PPG will show up
  ' to test our code move center to somewhere else
  CreatePrim "Cube", "MeshSurface"
  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
   
   
  ' enable PPG popup again
FreezeObj oObj
  Preferences.SetPreferenceValue "Interaction.autoinspect", true
set oGeometry = oObj.activeprimitive.geometry
 
aPositions = oGeometry.Points.PositionArray
 
  ' get old position
  ' '''[32] disabling logmessages'''
  '                                        (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")
   
   
  ' msglogverbos is probably already set to false by default
  ' set new position
  ' could be set at the start of a final script to increase performance
  aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
  setvalue "preferences.scripting.msglogverbose", false
  aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
  setvalue "preferences.scripting.msglog", false
  aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
 
 
' '''[33] getting bounding box values'''
   
   
  ' this could be useful to create a bounding box for [[OBD_talk:OFGA#XML|OFGA files]]
  ' update the array
   
  oGeometry.Points.PositionArray = aPositions
  ' let's get the bounding box of a simple cylinder
  ' get new position
  ' the output values will be absolute positions
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
  CreatePrim "Cylinder", "MeshSurface"
  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")
   
   
dim xmin, ymin, zmin, xmax, ymax, zmax
  ' INFO : old point 0 posx: -4
dim list
  ' INFO : old point 0 posy: -4
  'if you use SelectionList the objects will be treated as one single object
  ' INFO : old point 0 posz: -4
  '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 : new point 0 posx: -7
  ' INFO : Lower Bound: -1 / -2 / -1
  ' INFO : new point 0 posy: -7
  ' INFO : Upper Bound: 1 / 2 / 1
  ' INFO : new point 0 posz: -7




  ' '''[34] open an explorer window'''
  ' '''[28] getting the scaling, rotation and position of selected objects'''
   
   
  ' this could be used open an output folder ...
  logmessage "mesh name: " & selection(0)
  strPath = "[http://support.microsoft.com/kb/152457 explorer.exe /e]," & XSIUtils.ResolvePath("$SI_HOME/")
logmessage selection(0).sclx.value
  Set objShell = CreateObject("Wscript.Shell")
  logmessage selection(0).scly.value
  objShell.Run strPath
logmessage selection(0).sclz.value
   
logmessage selection(0).rotx.value
  ' to select a generated file inside the folder:
  logmessage selection(0).roty.value
  strPath = "explorer.exe /select," & "C:\folder\test.txt"
  logmessage selection(0).rotz.value
  logmessage selection(0).posx.value
  logmessage selection(0).posy.value
  logmessage selection(0).posz.value




{| border=0 cellpadding=0 style="float: right; padding-top: 10px"
  ' '''[29] getting the scaling, rotation, and position of not selected objects'''
| 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]
|}
  ' '''[35] working with hierarchies'''  
   
   
  ' let's be sure we have an object selected
' GetValue("NAME.kine.global.rotx")
  if selection.count > 0 and typename(selection(0)) = "X3DObject" then
' NAME must be the exact mesh name
 
  ' let's say you want the data of one character's pelvis
' the object itself
  logmessage GetValue("pelvis.kine.global.sclx")
logmessage selection(0)
logmessage GetValue("pelvis.kine.global.scly")
logmessage GetValue("pelvis.kine.global.sclz")
logmessage GetValue("pelvis.kine.global.rotx")
' the parent object
logmessage GetValue("pelvis.kine.global.roty")
logmessage selection(0).parent
logmessage GetValue("pelvis.kine.global.rotz")
logmessage GetValue("pelvis.kine.global.posx")
logmessage GetValue("pelvis.kine.global.posy")
' the children
logmessage GetValue("pelvis.kine.global.posz")
for each n in selection(0).FindChildren( , , , 0 )
 
logmessage n
 
next
' '''[30] decrypting merged integer flags'''
' 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
   
   
' 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
   
   
' count all objects
' x: hypothetical value of <Teams>
logmessage "children: " & selection(0).FindChildren( , , , 1).count
x=101
' caution: if the 3rd parameter is set to siMeshFamily
' 101 = 64 + 32 + 4 + 1
' then the selected object will be also counted
   
   
  ' find the root object of the hierarchy, any object could be selected
x_255
  SelectNeighborObj selection(0), 4
sub x_255
  if x > 255 then
msgbox "Flags bigger than 255 are not allowed.", ,"error"
  ' go through the hierarchy in different directions
exit sub
  ' SelectNeighborObj [parameter1], [parameter2], [parameter3], [parameter4]
end if
  ' options of the second parameters
  if x >= 128 and x <= 255 then
  ' 0 go one level up
logmessage "128"
  ' 1 go one level down
x = x - 128
  ' 2 go to next silbing
  end if
  ' 3 go to previous silbing
  if x >= 64 and x < 128 then
  ' 4 go to highest level
logmessage "64"
  ' 5 go to deepest level of current selected obj
x = x - 64
  ' 5: if there are more than one children, the path goes always for the first (left) child (see image)
  end if
  if x >= 32 and x < 64 then
logmessage "32"
  ' select the hierarchy as a whole
x = x - 32
  SelectObj "left_thigh", "TREE", true
  end if
  ' first parameter could be any object of the hierarchy
  if x >= 16 and x < 32 then
  ' after using this the selection of an Oni character will be "B:pelvis"
logmessage "16"
else
x = x - 16
  msgbox "no object was selected"
  end if
  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


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


  function XSILoadPlugin( in_reg )
  ' '''[31] disabling PPG popups'''
in_reg.Author = ""
in_reg.Name = "Oni_drag_and_drop"
in_reg.Email = ""
in_reg.URL = ""
in_reg.Major = 1
in_reg.Minor = 0
   
   
in_reg.RegisterEvent "support_oni_DnD",siOnDragAndDrop
' for example if code of a script button creates numerous objects in one go, the same number of property pages (PPG) can appear
'RegistrationInsertionPoint - do not remove this line
' for user convenience those PPG popups can be disabled
   
   
XSILoadPlugin = true
' disable PPG popup
  end function
  Preferences.SetPreferenceValue "Interaction.autoinspect", false
   
   
  function XSIUnloadPlugin( in_reg )
  ' creates the cube mesh but no PPG will show up
dim strPluginName
CreatePrim "Cube", "MeshSurface"
strPluginName = in_reg.Name
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
XSIUnloadPlugin = true
end function
   
   
  function support_oni_DnD_Init( in_ctxt )
  ' enable PPG popup again
dim action
Preferences.SetPreferenceValue "Interaction.autoinspect", true
set action = in_ctxt.GetAttribute( "DragAndDropAction" )
 
  end function
 
  ' '''[32] disabling logmessages'''
   
   
  ' Callback for the support_oni_DnD event.
  ' msglogverbos is probably already set to false by default
  function support_oni_DnD_OnEvent( in_ctxt )
  ' could be set at the start of a final script to increase performance
filename = in_ctxt.GetAttribute("DragSource")
setvalue "preferences.scripting.msglogverbose", false
' isolate file extension from full name
setvalue "preferences.scripting.msglog", false
FileExt = right(filename, len(filename) - instrrev(filename, "."))
' use LCase to recognize extension that might be in upper case, e.g. rewrites "ONI" to "oni"
if len(FileExt) = 3 and instr(LCase(FileExt), "oni") = 1 then
'enable drop if file extension is "oni"
in_ctxt.SetAttribute "DragSourceSupported", true
end if
' catch file drop event
if in_ctxt.GetAttribute("DragAndDropAction") = 1 then
' code that handels the imported file
' there could be a subtype recognition e.g. to let onisplit convert AKEV to dae files
' [...]
end if
' Return value is ignored as this event can not be aborted.
support_oni_DnD_OnEvent = true
end function




'''[37] dae export'''
' '''[33] getting bounding box values'''
 
   
  set oProps = ActiveProject.ActiveScene.Root.Properties
  ' this could be useful to create a bounding box for [[XML:OFGA|OFGA files]]
  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
  ' let's get the bounding box of a simple cylinder
  SetValue "ExportCrosswalkOptions.Format", 1
' the output values will be absolute positions
  ' set export path and file name
  CreatePrim "Cylinder", "MeshSurface"
  SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
  ' selection only
dim xmin, ymin, zmin, xmax, ymax, zmax
  SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
dim list
  ' export
'if you use SelectionList the objects will be treated as one single object
  ExportCrosswalk "ExportCrosswalkOptions"
  '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




'''[38] fbx export'''
' '''[34] open an explorer window'''
 
  FBXExportLights (false)
  ' this could be used open an output folder ...
  FBXExportSelection (true)
  strPath = "[http://web.archive.org/web/20150212074443/http://support.microsoft.com/kb/152457 explorer.exe /e]," & XSIUtils.ResolvePath("$SI_HOME/")
  ' mark FBXExport and hit F1 to get more options
  Set objShell = CreateObject("Wscript.Shell")
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )
objShell.Run strPath
' to select a generated file inside the folder:
strPath = "explorer.exe /select," & "C:\folder\test.txt"




'''[39] import an image clip only once'''
[[Image:XSI_Mod_Tool_SelectNeighborObj_obj_5.png|thumb|SelectNeighborObj obj 5]]
 
' '''[35] working with hierarchies'''  
  found_img = 0
   
  set clips = ActiveProject.ActiveScene.ImageClips
  ' let's be sure we have an object selected
  for each clip in clips
  if selection.count > 0 and typename(selection(0)) = "X3DObject" then
logmessage clip.name
 
if clip.name = "white_512_512_tga" then
' the object itself
logmessage "found img"
  logmessage selection(0)
found_img = 1
 
  end if
 
next
  ' the parent object
if found_placeholder_img = 0 then
  logmessage selection(0).parent
  ' import a new texture
 
  ' just so:
SICreateImageClip "C:\Users\RRM\Desktop\white_512_512.tga"
' the children
  ' or as script object:
for each n in selection(0).FindChildren( , , , 0 )
  'set oImage = AddImageSource ("C:\Users\RRM\Desktop\white_512_512.tga")
logmessage n
  'set oImageClip = AddImageClip (oImage)
next
end if
' 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
====Script ideas====
   
* addon to make easily use of onisplit within Mod Tool [done]
   
** supports drag and drop of onisplit
' count all objects
** onisplit's location will be saved and resaved in a txt
logmessage "children: " & selection(0).FindChildren( , , , 1).count
** updates scene property after txt changes
' caution: if the 3rd parameter is set to siMeshFamily
** loads txt on app start as scene property
' then the selected object will be also counted
** use backup onisplit from addon if user is to lazy for DnD
* import/export of flag collection
' find the root object of the hierarchy, any object could be selected
* import/export of patrol path collection
SelectNeighborObj selection(0), 4
* import/export of weapons collection
   
* import/export of powerup collection
* [...]
* autoheal discontinuities (blue lines) of overlaying edges (for repairing google sketchup objects that weren't yet textured)
* making loops from parts of animations
* copying animation data from one character to another
 
The following sections are about ideas that are checked right now for their possibility of realization or already in the making.
 
 
=====OniTools addon=====
{| 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_v1.zip OniTools.xsiaddon] into Mod Tool's viewport.
 
 
version 1
* drag'n'drop support for OniSplit update
* powerups manager, features also drag'n'drop support for BINACJBOPowerUp'''.oni'''
** 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)
<!--
pushed back
* Trigger volumes: import / add / export
* camera OBAN: import ([[wikipedia:Drag_and_drop|DnD]]) / export
-->
 
 
=====Selection tracking camera=====
* https://dl.dropbox.com/u/139715/auto_selection_tracking.txt
 
  ' enable automatic tracking
  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.
SelectNeighborObj , 2
SelectNeighborObj , 3
 
 
=====New camera animations=====
{| 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.
 
The last frame's rotation of level1_Final file OBANOutroCam02 is: -0.0158410165 0.854542 0.0261086561 -0.518483639.
 
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.
 
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.
 
'''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'')
 
Positions and rotations keyframes can be applied to the camera root object.
 
 
=====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.
 
An example:
 
set oCube = Application.ActiveProject.ActiveScene.Root.AddGeometry("Cube","MeshSurface")
SelectObj "cube", , True
SelectGeometryComponents "cube.poly[LAST]"
  CreateCluster
   
   
AddToCluster "cube.polymsh.cls.Polygon, cube.poly[2-4]"
' go through the hierarchy in different directions
RemoveFromCluster "cube.polymsh.cls.Polygon, cube.poly[3]"
' SelectNeighborObj [parameter1], [parameter2], [parameter3], [parameter4]
 
  ' options of the second parameters
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.
  ' 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
=====TRBS-fitting TRMA creation=====
' 5 go to deepest level of current selected obj
{| width="100%"
  ' 5: if there are more than one children, the path goes always for the first (left) child (see image)
|valign="top"|
With the following code ...
* the objects get listed one after another (TRMA-ready)
' select the hierarchy as a whole
* the textures get listed for TRMA and TXMP creation
SelectObj "left_thigh", "TREE", true
* the texture sizes get checked to see if "-large" argument is necessary
  ' first parameter could be any object of the hierarchy
 
  ' after using this the selection of an Oni character will be "B:pelvis"
One more idea is to add support for reflective textures.
 
 
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"|
hexhound hierarchy
 
[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]
|}
 
if selection.count > 0 then
  ' any part could be selected, let's find the root body part
  SelectNeighborObj selection(0), 4
  ' get all members including the pelvis
  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
  else
  logmessage "no object was selected"
  msgbox "no object was selected"
  end if
  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}}


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


=====New door animations=====
function XSILoadPlugin( in_reg )
Final door animation depends on:
in_reg.Author = ""
* BINACJBODOOR position and rotation
in_reg.Name = "Oni drag and drop support"
* OBAN positions and rotations
in_reg.Email = ""
* ZAxisUp (Does this flag do something else?)
in_reg.URL = ""
 
in_reg.Major = 1
 
in_reg.Minor = 0
'''Theory of the (Oni to ModTool) import from scratch:'''
 
in_reg.RegisterEvent "support_oni_DnD",siOnDragAndDrop
* import BINACJBODOOR
  'RegistrationInsertionPoint - do not remove this line
* get door classes
* extract M3GM from DOOR, etc.
XSILoadPlugin = true
 
end function
* import M3GM
* move center to lower bounding box Z
function XSIUnloadPlugin( in_reg )
* move object to Z = 0
dim strPluginName
* import OBAN
strPluginName = in_reg.Name
* apply rotations and positions to object
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
 
XSIUnloadPlugin = true
* create null object
end function
* make null a parent of door object
* rotate null in X = -90 (to make Z axis pointing up)
function support_oni_DnD_OnEvent( in_ctxt )
* add BINACJBODOOR rotation and position to null object
Set objFSO = CreateObject("Scripting.FileSystemObject")
 
The current version of onisplit (v0.9.68.0) imports the doors from AKEV without animations.
FullFilePath = in_ctxt.GetAttribute("DragSource")
 
  FileExt = objFSO.GetExtensionName(FullFilePath)
Those door have there centers at the lower bounding box Z, got corrected from their ZAxisUp, and have (BINACJBODOOR) rotation and position.
FileName = objFSO.GetBaseName(FullFilePath)
 
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.)
  if instr(LCase(FileExt), "oni") = 1 then
 
  in_ctxt.SetAttribute "DragSourceSupported", true
The door objects can be identified by their naming. Each name contains the BINACJBODOOR ID, e.g. <DOOR Id="7294">.
 
 
  ' next line prevents the code from being repeated
There could be a loop scanning all objects for those numbers.
if in_ctxt.GetAttribute("DragAndDropAction") = 1 then
 
 
logmessage "What I'm supposed to do with that oni?"
=====Hierarchy builder for characters=====
' do more stuff here
Based on old knowledge. See [[Mod_Tool#exchange_of_meshes_in_hierarchies|HERE]] for an easy manual way to change the meshes in hierarchies.
' ..................
 
' e.g. telling onisplit to extract the oni
end if
end if
support_oni_DnD_OnEvent = true
end function


'''Important:'''


'''(old notes in the following)'''
Mod Tool has its own drag and drop event handler for dae and xml file.


{| border=0 cellpadding=0 style="float: right;"
If we succeed in modifying and shipping that new file, we could finally trigger scripts with those files and process them.
| 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.
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: '''<nowiki>https://dl.dropboxusercontent.com/u/139715/OniGalore/ModToolScript/Model.vbs</nowiki> (dead link)'''


In order to make edits there is also a need of destroying the hierarchy again while preserving the rotation and position of the meshes.
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.


code pieces
A solution to is to transform the path again.


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


hierarchy breaker
Following function is always triggered, so we will modify it.
* [https://dl.dropbox.com/u/139715/OniGalore/ModToolScript/hierarchy_breaker_beta.txt hierarchy_breaker_beta.txt] (works only with strict correct names)
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>


The lines in red has been added.


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'''
'''[37] dae export'''


situation: "cube" and "cube1" were created
set oProps = ActiveProject.ActiveScene.Root.Properties
 
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
* case 1:
logmessage "Export settings not set"
** apply 45° y rotation to cube
CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
** make cube1 a child of cube
else
** cube1 will not inherit the 45°
logmessage "Export settings present"
 
end if
* case 2:
** apply 45° y rotation to cube
' sets the extension to dae
** make rotation keyframe for cube1
SetValue "ExportCrosswalkOptions.Format", 1
** make cube1 a child of cube
' set export path and file name
** cube1 inherit the 45°
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
' selection only
SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
' export
ExportCrosswalk "ExportCrosswalkOptions"


* 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:
'''[38] fbx export'''
** 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.
FBXExportLights (false)
FBXExportSelection (true)
' mark FBXExport and hit F1 to get more options
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )




While doing those test you can observe red letters next to the meshes in the Schematic window.
'''[39] import an image clip only once'''
: 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...
image_path =  "C:\Users\RRM\Desktop\white_512_512.tga"
: C can be seen next to camera objects, present when Camera_Interest is horizontal attached to it.
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'''
 
[[Image:ModTool_layers.png|thumb|200px|right|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 [http://mods.oni2.net/node/38 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
<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
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)===
* [[wikipedia:Autodesk_Softimage#ICE_Interactive_Creative_Environment|ICE]]
 
'''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)
 
==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?


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

edits