Mod Tool/Scripting: Difference between revisions

m
using Image: consistently to make it easier to find all image refs on a page
(how to get position of objects (moving and sorting more content from MT main page))
m (using Image: consistently to make it easier to find all image refs on a page)
 
(15 intermediate revisions by 3 users not shown)
Line 14: Line 14:


'''Links'''
'''Links'''
* [http://softimage.wiki.softimage.com/index.php?title=Scripting_Tips_and_Tricks_%28XSISDK%29 xsi wiki page about scripting]
* [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://softimage.wiki.softimage.com/sdkdocs/scriptsdb/scriptsdb/scrdb_vbscript.htm many vbscript examples]'''
* '''[http://web.archive.org/web/20170616035120/http://softimage.wiki.softimage.com/sdkdocs/scriptsdb/scriptsdb/scrdb_vbscript.htm many vbscript examples]'''
* '''[http://ss64.com/vb/ vbs commands]'''
* '''[https://ss64.com/vb/ vbs commands]'''
* [http://www.activexperts.com/activmonitor/windowsmanagement/adminscripts/filesfolders/files/ objFSO/objWSHShell: Scripts to manage Files] (replace "Wscript.Echo" with "logmessage")
* [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")
* [http://activexperts.com/activmonitor/windowsmanagement/adminscripts/other/textfiles/ objFSO/objWSHShell: Scripts to manage Text Files]
* [https://web.archive.org/web/20150504221146/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://web.archive.org/web/20080905102848/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 ?)-->]
* [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 ?)-->]
<!--* [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]-->
<!--* [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]-->


Line 83: Line 83:
  logmessage GetGlobal ("MyGloVar")
  logmessage GetGlobal ("MyGloVar")


Further information are found over [http://download.autodesk.com/global/docs/softimage2014/en_us/sdkguide/si_cmds/SetGlobal.html HERE.]
Further information are found over [https://download.autodesk.com/global/docs/softimage2014/en_us/sdkguide/si_cmds/SetGlobal.html HERE.]




Line 100: Line 100:
  logmessage XSIUtils.Environment("MyVar")
  logmessage XSIUtils.Environment("MyVar")


As the information is a string you need to convert it back to what it was meant originally e.g. with cBool and cInt. For more conversion see [http://www.w3schools.com/vbscript/vbscript_ref_functions.asp HERE]
As the information is a string you need to convert it back to what it was meant originally e.g. with cBool and cInt. For more conversion see [http://web.archive.org/web/20150707131602/http://www.w3schools.com/vbscript/vbscript_ref_functions.asp HERE]




Line 130: Line 130:




===Object and scene fundamentals===
===Events===
logmessage selection.count
 
====OnStartup====
logmessage selection(0).Name
Loading objects on startup can fail in some aspects even though you use the same function for OnActivate.
For example a console might get correct position but wrong rotation.
logmessage selection(0).Materials(0).Name
 
logmessage selection(0).Materials(0).Library.name
In that case you might want to switch to another program and then back to XSI to use the OnActivate event. Or you do something else e.g. let the user click on a button. With that everything that happened OnStartup should had enough time to process.
logmessage selection(0).Materials(0).shaders(0).name
 
logmessage selection(0).Materials(0).CurrentImageClip.source.filename.value
 
logmessage selection(0).Materials(0).CurrentImageClip.source.Parameters("XRes").Value
====OnActivate====
logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
  function siOnActivateEventTest_OnEvent( in_ctxt )
Application.LogMessage "State: " + cstr(in_ctxt.GetAttribute("State"))
  logmessage selection(0).Material.CurrentUV.name
  ' TODO: Put your code here.
  ' Return value is ignored as this event can not be aborted.
logmessage selection(0).activeprimitive.geometry.clusters(0).name
siOnActivateEventTest_OnEvent = true
' look for UV cluster names
  end function
' xsi-generated: "Texture_Coordinates_AUTO"
' onisplit-generated: "NodeProperties"
logmessage selection(0).sclx.value
logmessage selection(0).scly.value
logmessage selection(0).sclz.value
   
logmessage selection(0).rotx.value
  logmessage selection(0).roty.value
logmessage selection(0).rotz.value
  logmessage selection(0).posx.value
logmessage selection(0).posy.value
logmessage selection(0).posz.value
logmessage selection(0).rotorder.value


'''State becomes "False" when XSI loses its focus.'''


====Materials and textures====
'''State becomes "True" when XSI regains focus.'''
'''get all targets of an image clip'''


set imgClip = GetValue("Clips._marker_blackness_tga")
Exchanging data between XSI and other programs is rather difficult.
set imgClipTargets = imgClip.GetShaderParameterTargets


logmessage imgClipTargets.count
Instead, whenever "True" is detected XSI could look into an exchange folder or exchange file*.
for each t in imgClipTargets
 
logmessage t
: *OniSplit GUI could save a vbs script to file and then it gets executed by XSI
next
 
:: Application.ExecuteScript( FileName, [Language], [ProcName], [Params] )




'''Get all material libraries and materials
Full example (FillFile would have to be done by the GUI)


  for each ml in Application.ActiveProject.ActiveScene.MaterialLibraries
  Dim fso, wText, bInteractive, TestFile
logmessage ml
TestFile="C:\Oni\AE\Tools\Simple_OniSplit_GUI\XSI_exchange\test.vbs"
for each m in ml.items
logmessage m.name ' (material)
Main()
next
logmessage "--------------------------"
next
   
   
  ' INFO : Sources.Materials.DefaultLib
  '------------------------------------------------------------------------------
  ' INFO : Scene_Material
  ' NAME: FillFile
  ' INFO : sosMatBarrier
  '
  ' INFO : sosMatBlackness
  ' INPUT:
  ' INFO : sosMatDanger
  '
  ' INFO : sosMatGhost
  ' DESCRIPTION: Fill the test file
  ' INFO : sosMatImpassable
  '------------------------------------------------------------------------------
' INFO : sosMatStairs
  sub FillFile (in_file)
' INFO : --------------------------
  wText.WriteLine "Main()"
' INFO : Sources.Materials.MaterialLibrary
  wText.WriteLine ""
' INFO : AIR_STAIRWALL_LOB1
' INFO : AIR_WAITSEAT3
' INFO : AIR_WAITSEAT2
' INFO : COLLISION
' INFO : --------------------------
 
 
'''Check an object's main material for TwoSided-ness'''
 
' test and toggles an object's main material for TwoSided-ness
' this is also a prerequired test for transparency
  ' the difficulty is to get the TextureObject (often named Image)
' the magic happens at FindShaders, I often fail to find such trivial stuff
' imo the xsi is terrible incomplete/unintuitive
' e.g. look at "Find (ShaderCollection)" in the help
' it will give you information about meshes such as cubes ...
   
   
  wText.WriteLine "sub log_some_messages(an_int, a_string)"
  wText.WriteLine "logmessage ""the int : "" & an_int"
  wText.WriteLine "logmessage ""the string : "" & a_string"
  wText.WriteLine "end sub"
  wText.WriteLine ""
   
   
matLib = selection(0).Materials(0).Library.name
  wText.WriteLine "sub main()"
  wText.WriteLine "CreatePrim ""Sphere"", ""NurbsSurface"""
  wText.WriteLine "end sub"
  wText.Close
end sub
   
   
  set mat = selection(0).Material
  sub main()
materialName = mat.name
  Set fso = CreateObject("Scripting.FileSystemObject")
  Set wText = fso.CreateTextFile( TestFile, 1)
   
   
' let us see if there is an Image TextureObject
  FillFile TestFile
set shaders = mat.FindShaders(siShaderFilter)
textureObj = "Image"
   
   
'if typename(shaders(textureObj)) = "Texture" then ' if not it is Nothing
  ExecuteScript TestFile
' logmessage "material has texture object ""Image"""
'end if
   
   
Set list = CreateObject("System.Collections.ArrayList")
  dim aParams
  for each n in shaders
  aParams = Array(123456789, "toto")
list.Add n.name
  ExecuteScript TestFile,,"log_some_messages", aParams
  next
  end sub
 
====OnValueChange====
  ' this event is fired 4 times (2 local, 2 global)
' everytime when created, translated, rotated, or scaled
' we only want one global
firstValue = false
function siOnValueChangeEventTest_OnEvent( in_ctxt )
   
   
foundShaderParameterTransparency = false
  if instr(cstr(in_ctxt.GetAttribute("Object")), ".kine.global") > 0 then
'foundUniqueShaderName = false
  if firstValue = false then
shaderName = ""
  if list.Contains(textureObj) = true then
set oColorShareShader = GetValue("Sources.Materials." & matLib & "." & mat.name & "." & textureObj)
set oTargets = oColorShareShader.GetShaderParameterTargets("")
   
   
scriptObjArray = split(oTargets(0), ".")
' add more exit conditions here
'logmessage scriptObjArray(0) ' Sources (fixed name? Could be considered a folder.)
' e.g. selection mode
'logmessage scriptObjArray(1) ' Materials (fixed name? Could be considered a folder.)
' selection count
'logmessage scriptObjArray(2) ' MaterialsLib (usually each object has its own MaterialsLib)
  ' if obj is not an Oni obj or camera
'logmessage scriptObjArray(3) ' Material
'logmessage scriptObjArray(4) ' Shader e.g. Phong
shaderName = scriptObjArray(4)
for each t in oTargets
logmessage t
if t.name = "transparency" then
foundShaderParameterTransparency = true
exit for
end if
next
  end if
   
   
if foundShaderParameterTransparency = false then
logmessage "material is not TwoSided, lets reverse now"
SIConnectShaderToCnxPoint "Sources.Materials." & matLib & "." & materialName & ".Image", "Sources.Materials." & matLib & "." & materialName & "." & shaderName & ".transparency", False
else
logmessage "material is TwoSided, lets reverse now"
RemoveAllShadersFromCnxPoint "Sources.Materials." & matLib & "." & materialName & "." & shaderName & ".transparency", siShaderCnxPointBasePorts
end if
Output example:
' INFO : Sources.Materials.DefaultLib.Material.Phong.diffuse
' INFO : material is not TwoSided, lets reverse now
SIConnectShaderToCnxPoint "Sources.Materials.DefaultLib.Material.Image", "Sources.Materials.DefaultLib.Material.Phong.transparency", False
   
   
' INFO : Sources.Materials.DefaultLib.Material.Phong.diffuse
    if selection.count > 0 and not replace(cstr(in_ctxt.GetAttribute("Object")),".kine.global", "") = "Camera_Interest" then
' INFO : Sources.Materials.DefaultLib.Material.Phong.transparency
        logmessage "Object at: " & selection(0).posx.value & " " & selection(0).posy.value & " " & selection(0).posz.value
  ' INFO : material is TwoSided, lets reverse now
    end if
  RemoveAllShadersFromCnxPoint "Sources.Materials.DefaultLib.Material.Phong.transparency", siShaderCnxPointBasePorts
    if replace(cstr(in_ctxt.GetAttribute("Object")),".kine.global", "") = "Camera_Interest" then
        logmessage GetValue("Camera_Interest.kine.global.posx") & " " & _
          GetValue("Camera_Interest.kine.global.posy") & " " & _
          GetValue("Camera_Interest.kine.global.posz")
  logmessage "Obj is cam"
    end if
    firstValue = true
  else
    firstValue = false
  end if
end if
end function
 
  ' INFO : Object at: -22,0187049984705 6,63004918144234 5,08830153431981
  ' INFO : Obj is cam
 
This could be used to track camera and sound spheres positions. They values could be passed as command line argument to GUI.


====Clusters====
'does a certain cluster type exist ?
'set cls = selection(0).activeprimitive.geometry.clusters.find( siPolygonCluster )
 
' more interesting is how many of that type exist
for each n in selection(0).activeprimitive.geometry.clusters
    logmessage "Cluster " & n.name & " is of type " & n.type
next
' "poly" = polygon cluster
' "sample" = UV cluster


Output example:
===Suppress events in batch processing===
' INFO : Cluster Polygon4 is of type poly
Events that get triggered by code inside functions don't delay the function for processing the event.
' INFO : Cluster Polygon1 is of type poly
' INFO : Cluster Texture_Coordinates_AUTO is of type sample


The events are precessed after the function finished.


====Layers====
This can pose a serious problem with batch processing where you might create and select each object several times.
'''check if obj is member of layer'''
logmessage isMemberOfLayer(selection(0), "layerNameToTest")
function isMemberOfLayer(obj, layerName)
dim list
set list = selectMembers ("Layers." & layerName, 0) ' 0 = for not changing the current selection
for each o in list
if o = obj then
isMemberOfLayer = "yes"
exit for
end if
next
end function




'''Get layer name of an object'''
====Negative-example====
  logmessage RecursiveEnum (selection(0), false, false)
  function XSILoadPlugin( in_reg )
   
in_reg.Author = ""
  function RecursiveEnum( in_Comp, in_Type, in_FirstParentOnly )
in_reg.Name = "Sel Plug-in"
  dim list, elem, layerNameToCheck
in_reg.Major = 1
  set list = EnumElements( in_Comp, in_Type )
in_reg.Minor = 0
  if TypeName(list) <> "Nothing" then
in_reg.RegisterEvent "Selection",siOnSelectionChange
      for each elem in list
XSILoadPlugin = true
          if instr(elem, "Layers") = 1 and instr(elem, ".Members") > 1 then
  end function
          layerNameToCheck = replace(replace(elem,"Layers.", ""),".Members", "")
  function XSIUnloadPlugin( in_reg )
      if not layerNameToCheck = "" then
dim strPluginName
RecursiveEnum = layerNameToCheck
strPluginName = in_reg.Name
end if
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
      exit for
XSIUnloadPlugin = true
          end if
end function
      next
function Selection_OnEvent( in_ctxt )
  end if
' get select event, ignore unselect events (0)
if cstr(in_ctxt.GetAttribute("ChangeType")) = 1 then
exit function
end if
if XSIUtils.Environment("IgnoreMe") = "true" then
exit function
else
logmessage "hi !"
end if
Selection_OnEvent = true
  end function
  end function
' INFO : Layer_Default


Code to be called from somewhere else.


===Events===
XSIUtils.Environment.Setitem "IgnoreMe", "true"
SelectObj "cylinder", , True
XSIUtils.Environment.Setitem "IgnoreMe", "false"


====OnStartup====
The selection event outputs "hi !" despite "IgnoreMe" is set to "true" at the beginning.
Loading objects on startup can fail in some aspects even though you use the same function for OnActivate.
For example a console might get correct position but wrong rotation.


In that case you might want to switch to another program and then back to XSI to use the OnActivate event. Or you do something else e.g. let the user click on a button. With that everything that happened OnStartup should had enough time to process.
That's because the event becomes processed after the function finished after "IgnoreMe" was set to "false".




====OnActivate====
====Positive-example====
  function siOnActivateEventTest_OnEvent( in_ctxt )
  function XSILoadPlugin( in_reg )
  Application.LogMessage "State: " + cstr(in_ctxt.GetAttribute("State"))
in_reg.Author = ""
' TODO: Put your code here.
in_reg.Name = "Sel2 Plug-in"
' Return value is ignored as this event can not be aborted.
in_reg.Major = 1
  siOnActivateEventTest_OnEvent = true
in_reg.Minor = 0
in_reg.RegisterEvent "Selection2",siOnSelectionChange
XSILoadPlugin = true
end function
function XSIUnloadPlugin( in_reg )
dim strPluginName
strPluginName = in_reg.Name
  Application.LogMessage strPluginName & " has been unloaded.",siVerbose
XSIUnloadPlugin = true
end function
function Selection2_OnEvent( in_ctxt )
if cstr(in_ctxt.GetAttribute("ChangeType")) = 1 then
exit function
end if
IgnoreCount = GetGlobal ("IgnoreThis")
if IgnoreCount > 0 then
SetGlobal "IgnoreThis", (IgnoreCount - 1)
else
logmessage "Hi 2 !"
end if
  Selection2_OnEvent = true
  end function
  end function


'''State becomes "False" when XSI loses its focus.'''
Code to be called from somewhere else.
 
'''State becomes "True" when XSI regains focus.'''
 
Exchanging data between XSI and other programs is rather difficult.
 
Instead, whenever "True" is detected XSI could look into an exchange folder or exchange file*.


: *OniSplit GUI could save a vbs script to file and then it gets executed by XSI
Be aware of what can trigger the unwanted event and use a '''''global ignore variable'''''.


:: Application.ExecuteScript( FileName, [Language], [ProcName], [Params] )
for i=1 to 5
SetGlobal "IgnoreThis", GetGlobal ("IgnoreThis") + 1
SelectObj "cylinder", , True
next
' Don't use that variable where you want to trigger the event intentionally.
SelectObj "cylinder", , True




Full example (FillFile would have to be done by the GUI)
===Directories===
 
logmessage XSIUtils.ResolvePath("$XSI_USERHOME/")
  Dim fso, wText, bInteractive, TestFile
' directory to addons and exported resources
  TestFile="C:\Oni\AE\Tools\Simple_OniSplit_GUI\XSI_exchange\test.vbs"
  logmessage XSIUtils.ResolvePath("$XSI_HOME/")
  ' directory to a few imported core files that must be modified (Model.vbs, ModStartup.js, ...)
   
   
  Main()
  ' example:
' INFO : C:\Users\Paradox-01\Autodesk\Softimage_2015\
' INFO : C:\Program Files\Autodesk\Softimage 2015\
   
   
  '------------------------------------------------------------------------------
  ' INFO : C:\Users\Paradox-01\Autodesk\Softimage_Mod_Tool_7.5\
  ' NAME: FillFile
  ' INFO : C:\Softimage\Softimage_Mod_Tool_7.5\
  '
 
  ' INPUT:
  ' this can be useful for default locations like when selecting a folder
  '
DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
  ' DESCRIPTION: Fill the test file
  logmessage DesktopPath
  '------------------------------------------------------------------------------
 
  sub FillFile (in_file)
 
  wText.WriteLine "Main()"
===Message box===
  wText.WriteLine ""
  ' okay-only
  msgbox "message", 0, "title"
 
  ' okay and cancel
  MyVar = msgbox ("message", 1, "title")
   
   
  wText.WriteLine "sub log_some_messages(an_int, a_string)"
if MyVar = 1 then
  wText.WriteLine "logmessage ""the int : "" & an_int"
logmessage "OK button clicked"
  wText.WriteLine "logmessage ""the string : "" & a_string"
logmessage MyVar ' = 1
  wText.WriteLine "end sub"
else
  wText.WriteLine ""
logmessage "Cancel button clicked"
logmessage MyVar ' = 2
end if
 
 
===Input box===
MyVar = inputbox ("message", "title" , "pre-entered content")
   
   
  wText.WriteLine "sub main()"
if MyVar = false then
  wText.WriteLine "CreatePrim ""Sphere"", ""NurbsSurface"""
logmessage "Cancel button clicked"
  wText.WriteLine "end sub"
else
  wText.Close
logmessage "OK button clicked"
  end sub
logmessage MyVar
  end if
sub main()
 
  Set fso = CreateObject("Scripting.FileSystemObject")
 
  Set wText = fso.CreateTextFile( TestFile, 1)
===Dealing with different decimal marks===
Decimal sign is either period or comma.
  FillFile TestFile
  ExecuteScript TestFile
  dim aParams
  aParams = Array(123456789, "toto")
  ExecuteScript TestFile,,"log_some_messages", aParams
end sub


====OnValueChange====
Detect the used sign:
  ' this event is fired 4 times (2 local, 2 global)
logmessage Mid(FormatNumber(0.1, 1, true, false, -2), 2, 1)
  ' everytime when created, translated, rotated, or scaled
 
  ' we only want one global
OniSplit always uses the period sign but XSI uses the system used one, which is language-specific.
 
When xml files are loaded into XSI, the OniSplit formatted values need to be converted if necessary. Example:
 
if Mid(FormatNumber(0.1, 1, true, false, -2), 2, 1) = "," then
    posX = cdbl(replace(posX, ".", ","))
    posY = cdbl(replace(posY, ".", ","))
    posZ = cdbl(replace(posZ, ".", ","))
end if
 
Actually you only the replacement function because it will skip the operation if the sign to replace is not found.
 
When xml files are written, comma signs have to be replaced again.
 
posX = replace(posX, ",", ".")
  posY = replace(posY, ",", ".")
  posZ = replace(posZ, ",", ".")
 
 
===Check executable version===
  ' taking OniSplit as example
Set objFSO = CreateObject("Scripting.FileSystemObject")
logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")
   
   
firstValue = false
  ' result looks like this:
function siOnValueChangeEventTest_OnEvent( in_ctxt )
  ' INFO : 0.9.59.0
if instr(cstr(in_ctxt.GetAttribute("Object")), ".kine.global") > 0 then
  if firstValue = false then
  ' add more exit conditions here
  ' e.g. selection mode
' selection count
' if obj is not an Oni obj or camera
    if selection.count > 0 and not replace(cstr(in_ctxt.GetAttribute("Object")),".kine.global", "") = "Camera_Interest" then
        logmessage "Object at: " & selection(0).posx.value & " " & selection(0).posy.value & " " & selection(0).posz.value
    end if
    if replace(cstr(in_ctxt.GetAttribute("Object")),".kine.global", "") = "Camera_Interest" then
        logmessage GetValue("Camera_Interest.kine.global.posx") & " " & _
          GetValue("Camera_Interest.kine.global.posy") & " " & _
          GetValue("Camera_Interest.kine.global.posz")
  logmessage "Obj is cam"
    end if
    firstValue = true
  else
    firstValue = false
  end if
end if
end function


' INFO : Object at: -22,0187049984705 6,63004918144234 5,08830153431981
' INFO : Obj is cam


This could be used to track camera and sound spheres positions. They values could be passed as command line argument to GUI.
===Build an vbs executable===
[[Image:VbsEdit_for_scripting_and_compiling.png|thumb]]
Executable, app(lication), program. Whatever you call it, sometimes it might be necessary to compile the script into an actual program.


Even though vbs is a script language and not a programming language, it can be done.


===Suppress events in batch processing===
VbsEdit is an editor to fulfill such task with ease.
Events that get triggered by code inside functions don't delay the function for processing the event.


The events are precessed after the function finished.
: Just goto ''File > Convert into Executable''. Choose output path, 32/64-bit version and hit OK.


This can pose a serious problem with batch processing where you might create and select each object several times.


===OS bitness===
if GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'").AddressWidth = 64 then
logmessage "64"
else
logmessage "32"
end if


====Negative-example====
'''faster'''
  function XSILoadPlugin( in_reg )
  Set WshShell = CreateObject("WScript.Shell")
in_reg.Author = ""
  if instr(WshShell.RegRead("HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0\Identifier"), "64") > 0 then
in_reg.Name = "Sel Plug-in"
  logmessage "64"
in_reg.Major = 1
else
in_reg.Minor = 0
  logmessage "32"
in_reg.RegisterEvent "Selection",siOnSelectionChange
end if
XSILoadPlugin = true
  end function
function XSIUnloadPlugin( in_reg )
dim strPluginName
strPluginName = in_reg.Name
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
XSIUnloadPlugin = true
end function
function Selection_OnEvent( in_ctxt )
' get select event, ignore unselect events (0)
if cstr(in_ctxt.GetAttribute("ChangeType")) = 1 then
exit function
  end if
if XSIUtils.Environment("IgnoreMe") = "true" then
exit function
  else
logmessage "hi !"
end if
Selection_OnEvent = true
end function


Code to be called from somewhere else.


XSIUtils.Environment.Setitem "IgnoreMe", "true"
===XSI/Softimage bitness, version and license===
SelectObj "cylinder", , True
There are three possibilities to detect the program's bitness:
XSIUtils.Environment.Setitem "IgnoreMe", "false"


The selection event outputs "hi !" despite "IgnoreMe" is set to "true" at the beginning.
logmessage Platform
logmessage XSIUtils.ResolvePath("$XSI_CPU/")
logmessage XSIUtils.Is64BitOS
' output for 32-bit installation
' INFO : Win32
' INFO : nt-x86\
' INFO : False
' output for 64-bit installation
' INFO : Win64
' INFO : nt-x86-64\
' INFO : True


That's because the event becomes processed after the function finished after "IgnoreMe" was set to "false".


For program's version:
logmessage version
' examples:
' INFO : 13.0.114.0
' INFO : 7.5.203.0


====Positive-example====
For program's license:
  function XSILoadPlugin( in_reg )
logmessage license
in_reg.Author = ""
' examples:
in_reg.Name = "Sel2 Plug-in"
  ' INFO : Softimage
in_reg.Major = 1
' INFO : Mod Tool
in_reg.Minor = 0
 
in_reg.RegisterEvent "Selection2",siOnSelectionChange
DAE files saved with XSI/Softimage contain license information.
XSILoadPlugin = true
 
end function
 
function XSIUnloadPlugin( in_reg )
===Read registry===
dim strPluginName
This reads the registry with forced 64/32-bit path (RegType). In this example Oni's install location gets revealed.
strPluginName = in_reg.Name
 
Application.LogMessage strPluginName & " has been unloaded.",siVerbose
Set WshShell = CreateObject("WScript.Shell")
XSIUnloadPlugin = true
  if instr(WshShell.RegRead("HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0\Identifier"), "64") > 0 then
  end function
OS_bitness = 64
function Selection2_OnEvent( in_ctxt )
else
if cstr(in_ctxt.GetAttribute("ChangeType")) = 1 then
  OS_bitness = 32
exit function
  end if
end if
IgnoreCount = GetGlobal ("IgnoreThis")
Const HKEY_LOCAL_MACHINE = &H80000002
if IgnoreCount > 0 then
SetGlobal "IgnoreThis", (IgnoreCount - 1)
  sPath = ReadRegStr (HKEY_LOCAL_MACHINE, _
else
  "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B67333BB-1CF9-4EFD-A40B-E25B5CB4C8A7}}_is1", _
logmessage "Hi 2 !"
"InstallLocation", _
  end if
OS_bitness)
Selection2_OnEvent = true
 
  end function
  logmessage sPath
 
Code to be called from somewhere else.
 
Be aware of what can trigger the unwanted event and use a '''''global ignore variable'''''.
 
  for i=1 to 5
  SetGlobal "IgnoreThis", GetGlobal ("IgnoreThis") + 1
  SelectObj "cylinder", , True
  next
   
   
' Don't use that variable where you want to trigger the event intentionally.
SelectObj "cylinder", , True
===Directories===
logmessage XSIUtils.ResolvePath("$XSI_USERHOME/")
' directory to addons and exported resources
logmessage XSIUtils.ResolvePath("$XSI_HOME/")
' directory to a few imported core files that must be modified (Model.vbs, ModStartup.js, ...)
   
   
  ' example:
  Function ReadRegStr (RootKey, Key, Value, RegType)
' INFO : C:\Users\Paradox-01\Autodesk\Softimage_2015\
  Dim oCtx, oLocator, oReg, oInParams, oOutParams
' INFO : C:\Program Files\Autodesk\Softimage 2015\
   
   
  ' INFO : C:\Users\Paradox-01\Autodesk\Softimage_Mod_Tool_7.5\
  Set oCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
' INFO : C:\Softimage\Softimage_Mod_Tool_7.5\
  oCtx.Add "__ProviderArchitecture", RegType
 
   
  ' this can be useful for default locations like when selecting a folder
  Set oLocator = CreateObject("Wbemscripting.SWbemLocator")
DesktopPath = CreateObject("WScript.Shell").SpecialFolders("Desktop")
  Set oReg = oLocator.ConnectServer("", "root\default", "", "", , , , oCtx).Get("StdRegProv")
  logmessage DesktopPath
   
  Set oInParams = oReg.Methods_("GetStringValue").InParameters
  oInParams.hDefKey = RootKey
  oInParams.sSubKeyName = Key
  oInParams.sValueName = Value
  Set oOutParams = oReg.ExecMethod_("GetStringValue", oInParams, , oCtx)
   
  ReadRegStr = oOutParams.sValue
End Function




===Message box===
===Run other programs===
' okay-only
'''via XSI System'''
msgbox "message", 0, "title"


  ' okay and cancel
  'relative pathes don't seem to work with this method
  MyVar = msgbox ("message", 1, "title")
  OniSplitLocation = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\OniSplit.exe"
   
inputFile =  "M3GMU_security_tv_wall_0.oni"
  if MyVar = 1 then
  inputPath  = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\test a" & "\" & inputFile
logmessage "OK button clicked"
  outputPath = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\test a"
logmessage MyVar ' = 1
ApplicationParam  = "-extract:xml " & """" & outputPath & """" & " " & """" & inputPath & """"
  else
appResult = System( OniSplitLocation & " " & ApplicationParam )
logmessage "Cancel button clicked"
  Select Case appResult
logmessage MyVar ' = 2
    Case 0 LogMessage "Ok."
  end if
    Case Else LogMessage "Error."
  End Select


 
'''Via CMD'''
===Input box===
' relative path
  MyVar = inputbox ("message", "title" , "pre-entered content")
  ' the "GameDataFolder" isn't inside the "install" folder
' so we will use ..\ to go one folder backwards
   
   
  if MyVar = false then
  ' additional quote signs tells the program where the
logmessage "Cancel button clicked"
' paths strings start and end in case the path contains spaces
  else
logmessage "OK button clicked"
' if you are going to use the xml file right after its extraction (which is likely)
logmessage MyVar
' then the "/wait" argument inside the onisplit_action string is important
  end if
' without it the code would continue and might try to read the not existing xml file and produce an error
 
 
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
===dealing with different decimal marks===
input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
Decimal sign is either period or comma.
output_folder = """..\GameDataFolder"""
 
  onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
Detect the used sign:
logmessage "relative path: " & onisplit_action
  logmessage Mid(FormatNumber(0.1, 1, true, false, -2), 2, 1)
' expected logmessage:
 
  ' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
OniSplit always uses the period sign but XSI uses the system used one, which is language-specific.
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
 
When xml files are loaded into XSI, the OniSplit formatted values need to be converted if necessary. Example:
' absolute path
'adapt paths so it works on your computer
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "absolute path: " & onisplit_action
' expected logmessage:
  ' <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
' you can also lunch bat files
onibat = "cmd /C start run_wind.bat"
onilocation = "F:\Program Files (x86)\Oni\Edition"
XSIUtils.LaunchProcess onibat, 0, onilocation


if Mid(FormatNumber(0.1, 1, true, false, -2), 2, 1) = "," then
    posX = cdbl(replace(posX, ".", ","))
    posY = cdbl(replace(posY, ".", ","))
    posZ = cdbl(replace(posZ, ".", ","))
end if


Actually you only the replacement function because it will skip the operation if the sign to replace is not found.
'''Via winmgmts'''
 
When xml files are written, comma signs have to be replaced again.
 
posX = replace(posX, ",", ".")
posY = replace(posY, ",", ".")
posZ = replace(posZ, ",", ".")
 
 
===Check executable version===
' taking OniSplit as example
Set objFSO = CreateObject("Scripting.FileSystemObject")
logmessage objFSO.GetFileVersion("F:\Program Files (x86)\Oni\Edition\install\onisplit.exe")
   
   
  ' result looks like this:
  ' 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]
  ' INFO : 0.9.59.0
  ' ''logmessage "onisplit finished."'' will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers
 
' if you are going to use this method consider to extent the code to check if input file and output directory exist
 
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
===Build an vbs executable===
osp_action = "-extract:xml"
[[Image:VbsEdit_for_scripting_and_compiling.png|thumb]]
osp_output = """C:\OniAE\Edition\GameDataFolder"""
Executable, app(lication), program. Whatever you call it, sometimes it might be necessary to compile the script into an actual program.
osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
logmessage osp_total
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
objWMIService.Create osp_total, null, null, intProcessID
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
' wait 3 second to find event - should be enough time for single actions on a slow computer
Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")
Do Until i = 1
    Set objLatestProcess = colMonitoredProcesses.NextEvent
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
        i = 1
    End If
Loop
logmessage "onisplit finished."
' now you can work with the extracted xml file


Even though vbs is a script language and not a programming language, it can be done.
VbsEdit is an editor to fulfill such task with ease.
: Just goto ''File > Convert into Executable''. Choose output path, 32/64-bit version and hit OK.


===Detect a running program===
detectProgram = "Simple_OniSplit_GUI.exe"
programIsActive = false
sComputerName = "."
Set objWMIService = GetObject("winmgmts:\\" & sComputerName & "\root\cimv2")
sQuery = "SELECT * FROM Win32_Process"
Set objItems = objWMIService.ExecQuery(sQuery)
For Each objItem In objItems
    if objItem.name = detectProgram then
        programIsActive = true
        exit for
    end if
Next
logmessage programIsActive


===OS bitness===
outputs either True or False
if GetObject("winmgmts:root\cimv2:Win32_Processor='cpu0'").AddressWidth = 64 then
logmessage "64"
else
logmessage "32"
end if


'''faster'''
The code above triggers a bug. When Mod Tool gets minimized you can't bring it back to front.
Set WshShell = CreateObject("WScript.Shell")
if instr(WshShell.RegRead("HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0\Identifier"), "64") > 0 then
logmessage "64"
else
logmessage "32"
end if


ExecuteScript doesn't help.


===XSI/Softimage bitness, version and license===
===Taking a viewport screenshot===
There are three possibilities to detect the program's bitness:
SetDisplayMode "Camera", "texturedecal"
DeselectAll
cf = ActiveProject.Properties.Item("Play Control").Parameters.Item("Current").Value
set oViewportCapture = Dictionary.GetObject("ViewportCapture")
oViewportCapture.NestedObjects.Item("Start Frame").Value = cf
oViewportCapture.NestedObjects.Item("End Frame").Value = cf
oViewportCapture.NestedObjects.Item("OpenGL Anti-Aliasing").Value = 4
oViewportCapture.NestedObjects.Item("File Name").Value = "C:\Oni\AE\Tools\Simple_OniSplit_GUI\OutputFolder\test.jpg"
CaptureViewport  2, false


logmessage Platform
command-line access via:
logmessage XSIUtils.ResolvePath("$XSI_CPU/")
  C:\Softimage\Softimage_Mod_Tool_7.5\Application\bin\flip.exe
logmessage XSIUtils.Is64BitOS
' output for 32-bit installation
' INFO : Win32
  ' INFO : nt-x86\
' INFO : False
' output for 64-bit installation
' INFO : Win64
' INFO : nt-x86-64\
' INFO : True


For CMD options use "true" and Help > Command Line Options
CaptureViewport  2, true


For program's version:
It might be possible to further automate html creation with this.
logmessage version
' examples:
' INFO : 13.0.114.0
' INFO : 7.5.203.0


For program's license:
When screenshots are big enough the info text doesn't overlay object and can be cut away in further image processing.
logmessage license
' examples:
' INFO : Softimage
' INFO : Mod Tool


DAE files saved with XSI/Softimage contain license information.
==Write file==
===Export DAE===
Before you go crazy, yes, the command "CreateExportCrosswalkOptions" doesn't get logged in the script history.
 
set oProps = ActiveProject.ActiveScene.Root.Properties
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
end if
' sets the extension to dae
SetValue "ExportCrosswalkOptions.Format", 1
' set export path and file name
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
' selection only
SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
' export
ExportCrosswalk "ExportCrosswalkOptions"
 
 
===Export FBX===
FBXExportLights (false)
FBXExportSelection (true)
' mark FBXExport and hit F1 to get more options
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )




===Read registry===
===Write text file===
This reads the registry with forced 64/32-bit path (RegType). In this example Oni's install location gets revealed.
  txt_location = "C:\Softimage\Softimage_Mod_Tool_7.5\test.txt"
 
  Set fso = CreateObject ("Scripting.FileSystemObject")
  Set WshShell = CreateObject("WScript.Shell")
  Set wText = fso.CreateTextFile (txt_location, 1)
if instr(WshShell.RegRead("HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0\Identifier"), "64") > 0 then
  wText.WriteLine "I'm a test file."
OS_bitness = 64
  wText.WriteLine "Yo!"
else
  wText.Close
OS_bitness = 32
end if
Const HKEY_LOCAL_MACHINE = &H80000002
sPath = ReadRegStr (HKEY_LOCAL_MACHINE, _
"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\{B67333BB-1CF9-4EFD-A40B-E25B5CB4C8A7}}_is1", _
"InstallLocation", _
OS_bitness)
  logmessage sPath
Function ReadRegStr (RootKey, Key, Value, RegType)
  Dim oCtx, oLocator, oReg, oInParams, oOutParams
  Set oCtx = CreateObject("WbemScripting.SWbemNamedValueSet")
  oCtx.Add "__ProviderArchitecture", RegType
   
  Set oLocator = CreateObject("Wbemscripting.SWbemLocator")
  Set oReg = oLocator.ConnectServer("", "root\default", "", "", , , , oCtx).Get("StdRegProv")
   
  Set oInParams = oReg.Methods_("GetStringValue").InParameters
  oInParams.hDefKey = RootKey
  oInParams.sSubKeyName = Key
  oInParams.sValueName = Value
   
  Set oOutParams = oReg.ExecMethod_("GetStringValue", oInParams, , oCtx)
   
  ReadRegStr = oOutParams.sValue
End Function
 


===Run other programs===
'''via XSI System'''


  'relative pathes don't seem to work with this method
==Read file==
OniSplitLocation = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\OniSplit.exe"
===Import DAE===
  inputFile =  "M3GMU_security_tv_wall_0.oni"
  CopyPaste, filePath, parentObject, 2
inputPath  = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\test a" & "\" & inputFile
 
outputPath = "C:\Softimage\Softimage_Mod_Tool_7.5\OniXSI_resources\test a"
'use scene object if you don't want to group the imported object under a parent, e.g.:
ApplicationParam  = "-extract:xml " & """" & outputPath & """" & " " & """" & inputPath & """"
  CopyPaste, filePath, ActiveProject.ActiveScene, 2
appResult = System( OniSplitLocation & " " & ApplicationParam )
 
Select Case appResult
 
    Case 0 LogMessage "Ok."
===Import DAE (and get name)===
    Case Else LogMessage "Error."
Sometimes you want to get the name of the object you just imported.
End Select


'''Via CMD'''
In that case you use "ImportModel" instead of "CopyPaste"
  ' relative path
 
   
ImportModel filePath, [parentObject], , , "0"
  ' the "GameDataFolder" isn't inside the "install" folder
logmessage selection(0).parent
  ' so we will use ..\ to go one folder backwards
logmessage selection(0) 'this gets root object (file name)
   
logmessage selection(0).children(0) 'this gets first object of file
  ' additional quote signs tells the program where the
 
  ' paths strings start and end in case the path contains spaces
If you want to remove the null object and select the new parent you add these lines:
set nullObject = selection(0)
set newParent = selection(0).children(0)
CutObj newParent
  DeleteObj nullObject
  SelectObj newParent
 
 
===Read text file===
  Set objFileToRead = CreateObject("Scripting.FileSystemObject").OpenTextFile("C:\Softimage\Softimage_Mod_Tool_7.5\test.txt", 1)
  do while not objFileToRead.AtEndOfStream
    strLine = objFileToRead.ReadLine()
    logmessage strLine
  loop
  ' INFO : I'm a test file.
  ' INFO : Yo!
   
   
  ' if you are going to use the xml file right after its extraction (which is likely)
  objFileToRead.Close
  ' then the "/wait" argument inside the onisplit_action string is important
  Set objFileToRead = Nothing
  ' without it the code would continue and might try to read the not existing xml file and produce an error
 
 
===Read binary file===
  scan_AKEV_file_table
   
   
  onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
  sub scan_AKEV_file_table
input_folder = """..\GameDataFolder\level19_Final\ONLVcompound.oni"""
' ##############################################
output_folder = """..\GameDataFolder"""
OniInputFile = "H:\Oni\AE\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
' ##############################################
logmessage "relative path: " & onisplit_action
   
' expected logmessage:
Set OniInputFileStream = CreateObject("ADODB.Stream")
' INFO : relative path: cmd /C start OniSplit.exe -extract:xml "..\GameDataFolder" "..\GameDataFolder\level19_Final\ONLVcompound.oni"
OniInputFileStream.Type = 1
XSIUtils.LaunchProcess onisplit_action, 1, onisplit_location
OniInputFileStream.Open
OniInputFileStream.LoadFromFile OniInputFile
   
   
' ### read AKEV textures table offset and size
ByteNum = 4
' ##############################################
TOffset = cLng("&H" & "28")
' ##############################################
OniInputFileStream.Position = TOffset
BArr1 = OniInputFileStream.Read(ByteNum)
   
   
' absolute path
ByteNum = 4
' ##############################################
TSize = cLng ("&H" & "2C")
' ##############################################
OniInputFileStream.Position = TSize
BArr2 = OniInputFileStream.Read(ByteNum)
   
   
'adapt paths so it works on your computer
onisplit_location = "F:\Program Files (x86)\Oni\Edition\install"
input_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder\level19_Final\ONLVcompound.oni"""
output_folder = """F:\Program Files (x86)\Oni\Edition\GameDataFolder"""
onisplit_action = "cmd /C start /wait OniSplit.exe -extract:xml " & output_folder & " " & input_folder
logmessage "absolute path: " & onisplit_action
' expected logmessage:
' <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
   
   
' ### get AKEV textures table offset and size
TOffsetHex = SimpleBinaryToString(BArr1)
for i = ubound(TOffsetHex ) - 1 to 0 step -1
newhex = newhex & hex(Asc(TOffsetHex(i)))
next
logmessage newhex
logmessage "name table offset: " & cLng("&H" & newhex)
TOffsetInt = cLng("&H" & newhex)
newhex = ""
TSizeHex = SimpleBinaryToString(BArr2)
for i = ubound(TSizeHex) - 1 to 0 step -1
newhex = newhex & hex(Asc(TSizeHex(i)))
next
logmessage newhex
logmessage "name table size: " & cLng("&H" & newhex)
TSizeInt = cLng("&H" & newhex)
  logmessage "------------------------------"
 
   
   
' you can also lunch bat files
' ### read table content
ByteNum = TSizeInt
onibat = "cmd /C start run_wind.bat"
OniInputFileStream.Position = TOffsetInt
onilocation = "F:\Program Files (x86)\Oni\Edition"
BArr3 = OniInputFileStream.Read(ByteNum)
XSIUtils.LaunchProcess onibat, 0, onilocation
TContent = SimpleBinaryToString(BArr3)
 
 
' ### name grapper
'''Via winmgmts'''
NG = ""
for each n in TContent
' 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]
if not Asc(n) = 0 then
' ''logmessage "onisplit finished."'' will be executed after the conversion finished, there should be also an delay of 3 seconds to support very slow computers
NG = NG & n
' if you are going to use this method consider to extent the code to check if input file and output directory exist
else
osp_loca = "C:\OniAE\Edition\install\OniSplit.exe"
'if instr(NG, "TXMP") = 1 then
osp_action = "-extract:xml"
' write TXMP to array ?
osp_output = """C:\OniAE\Edition\GameDataFolder"""
logmessage NG
  osp_input = """C:\OniAE\Edition\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"""
'end if
osp_total = osp_loca & " " & osp_action & " " & osp_output & " " & osp_input
NG = ""
logmessage osp_total
end if
next
  end sub
   
   
  strComputer = "."
  Function SimpleBinaryToString(Binary)
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process")
ReDim tmpArr(LenB(Binary) - 1)
objWMIService.Create osp_total, null, null, intProcessID
For I = 1 To LenB(Binary)
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
S = Chr(AscB(MidB(Binary, I, 1)))
   
tmpArr(I - 1) = S
  ' wait 3 second to find event - should be enough time for single actions on a slow computer
Next
  Set colMonitoredProcesses = objWMIService.ExecNotificationQuery _
SimpleBinaryToString = tmpArr
    ("Select * From __InstanceDeletionEvent Within 3 Where TargetInstance ISA 'Win32_Process'")
  End Function
   
 
  Do Until i = 1
Output:
    Set objLatestProcess = colMonitoredProcesses.NextEvent
  ' INFO : 0E40
    If objLatestProcess.TargetInstance.ProcessID = intProcessID Then
  ' INFO : name table offset: 3648
        i = 1
' INFO : 0A4A
    End If
' INFO : name table size: 2634
  Loop
  ' INFO : ------------------------------
  logmessage "onisplit finished."
  ' INFO : AKEVEnvWarehouse
  ' now you can work with the extracted xml file
' INFO : AGDBEnvWarehouse
' INFO : TXMP_DOOR_FRAME
' INFO : TXMPNONE
' INFO : TXMPSUMI_1
  ' INFO : TXMPTC_CONTROL_01
  ' [...]
  ' INFO : TXMPWH_DCTRBND




===Detect a running program===
====AKEV AGQG====
  detectProgram = "Simple_OniSplit_GUI.exe"
read_AGQG_binary
programIsActive = false
   
sComputerName = "."
sub read_AGQG_binary
  Set objWMIService = GetObject("winmgmts:\\" & sComputerName & "\root\cimv2")
OniInputFile = "C:\Oni\AE\GameDataFolder\L3\AKEVlab.oni"
sQuery = "SELECT * FROM Win32_Process"
' ##############################################
  Set objItems = objWMIService.ExecQuery(sQuery)
   
  For Each objItem In objItems
Set OniInputFileStream = CreateObject("ADODB.Stream")
    if objItem.name = detectProgram then
OniInputFileStream.Type = 1
        programIsActive = true
OniInputFileStream.Open
        exit for
OniInputFileStream.LoadFromFile OniInputFile
    end if
   
  Next
data_table_offset = "&H20"
logmessage programIsActive
AGQG_table_offset = "&H94"
 
AGQG_table_size  = "&H9C"
outputs either True or False
   
 
The code above triggers a bug. When Mod Tool gets minimized you can't bring it back to front.
ByteNum = 4
 
OniInputFileStream.Position = clng(data_table_offset)
ExecuteScript doesn't help.
BArr0 = OniInputFileStream.Read(ByteNum)
 
   
===Taking a viewport screenshot===
ByteNum = 4
SetDisplayMode "Camera", "texturedecal"
OniInputFileStream.Position = clng(AGQG_table_offset)
  DeselectAll
BArr1 = OniInputFileStream.Read(ByteNum)
cf = ActiveProject.Properties.Item("Play Control").Parameters.Item("Current").Value
   
set oViewportCapture = Dictionary.GetObject("ViewportCapture")
ByteNum = 4
oViewportCapture.NestedObjects.Item("Start Frame").Value = cf
OniInputFileStream.Position = clng(AGQG_table_size)
oViewportCapture.NestedObjects.Item("End Frame").Value = cf
BArr2 = OniInputFileStream.Read(ByteNum)
oViewportCapture.NestedObjects.Item("OpenGL Anti-Aliasing").Value = 4
oViewportCapture.NestedObjects.Item("File Name").Value = "C:\Oni\AE\Tools\Simple_OniSplit_GUI\OutputFolder\test.jpg"
CaptureViewport  2, false
newhex = ""
 
data_table_offset_hex = SimpleBinaryToString(BArr0)
command-line access via:
for i = ubound(data_table_offset_hex) - 1 to 0 step -1
C:\Softimage\Softimage_Mod_Tool_7.5\Application\bin\flip.exe
h = hex(Asc(data_table_offset_hex(i)))
 
if len(h) = 1 then
For CMD options use "true" and Help > Command Line Options
h = "0" & h
CaptureViewport  2, true
end if
 
newhex = newhex & h
It might be possible to further automate html creation with this.
next
 
logmessage newhex
When screenshots are big enough the info text doesn't overlay object and can be cut away in further image processing.
logmessage "data table offset: " & cLng("&H" & newhex)
 
data_table_offset_int = cLng("&H" & newhex)
==Write file==
   
===Export DAE===
newhex = ""
Before you go crazy, yes, the command "CreateExportCrosswalkOptions" doesn't get logged in the script history.
AGQG_offset_hex = SimpleBinaryToString(BArr1)
 
for i = ubound(AGQG_offset_hex) - 1 to 0 step -1
set oProps = ActiveProject.ActiveScene.Root.Properties
h = hex(Asc(AGQG_offset_hex(i)))
if typename (oProps.find("ExportCrosswalkOptions")) = "Nothing" then
if len(h) = 1 then
CreateExportCrosswalkOptions , "ExportCrosswalkOptions"
h = "0" & h
  end if
end if
newhex = newhex & h
next
logmessage newhex
logmessage "AGQG table offset: " & cLng("&H" & newhex)
AGQG_offset_int = cLng("&H" & newhex)
newhex = ""
AGQG_size_hex = SimpleBinaryToString(BArr2)
for i = ubound(AGQG_size_hex) - 1 to 0 step -1
h = hex(Asc(AGQG_size_hex(i)))
if len(h) = 1 then
h = "0" & h
end if
newhex = newhex & h
next
logmessage newhex
logmessage "AGQG table size: " & cLng("&H" & newhex)
AGQG_size_int = cLng("&H" & newhex)  
  logmessage "------------------------------"
  AGQG_start = data_table_offset_int + AGQG_offset_int - 8
  AGQG_end  = data_table_offset_int + AGQG_offset_int + AGQG_size_int - 8
  logmessage "AGQG_start: " & AGQG_start
 
' AGQG array size
  ByteNum = 4
OniInputFileStream.Position = AGQG_start + 28
BArr3 = OniInputFileStream.Read(ByteNum)
TContent = SimpleBinaryToString(BArr3)
  logmessage "------------------------------"
newhex = 0
for i = ubound(TContent) to 0 step -1
h = hex(Asc(TContent(i)))
if len(h) = 1 then
h = "0" & h
end if
newhex = newhex & h
next
AGQG_array_size = clng("&H" & newhex)
logmessage "AGQG array size: " & clng("&H" & newhex)
' reduce number by header space
ByteNum = AGQG_size_int - 31
' skip bytes used by header
OniInputFileStream.Position = AGQG_start + 32
BArr4 = OniInputFileStream.Read(ByteNum)
TContent2 = SimpleBinaryToString(BArr4)
c = -1
loop_count = 1
h = ""
newhex = ""
color = 0
logmessage "----------"
logmessage "element: 1"
for i = 0 to ubound(TContent2) - 1
c = c + 1
if c = 56 then
c = 0
loop_count = loop_count + 1
logmessage "----------"
logmessage "element: " & loop_count
end if
h = hex(Asc(TContent2(i)))
if len(h) = 1 then
h = "0" & h
end if
newhex = newhex & h
   
if i mod 56 = 32 then
color = 1
end if
if i mod 56 = 48 then
color = 0
end if
if i mod 56 = 52 then
objid = 1
end if
if i mod 56 = 0 then
objid = 0
end if
   
   
' sets the extension to dae
if len(newhex) = 8 then
SetValue "ExportCrosswalkOptions.Format", 1
if color = 1 then
' set export path and file name
logmessage newhex & " (color: " & clng(("&H" & left(newhex, 2))) _
SetValue "ExportCrosswalkOptions.Filename", CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.dae"
& " " & clng("&H" & mid(newhex, 3, 2)) _
  ' selection only
& " " & clng("&H" & mid(newhex, 5, 2)) _
  SetValue "ExportCrosswalkOptions.ExportSelectionOnly", True
& " " & clng("&H" & mid(newhex, 7, 2)) & ")"
  ' export
elseif objid = 1 then
  ExportCrosswalk "ExportCrosswalkOptions"
if newhex = "FFFFFFFF" then
logmessage newhex & " (object id: -1)"
else
logmessage newhex
end if
else
logmessage newhex
end if
newhex = ""
end if
next
   
  end sub
   
  Function SimpleBinaryToString(Binary)
ReDim tmpArr(LenB(Binary) - 1)
For I = 1 To LenB(Binary)
S = Chr(AscB(MidB(Binary, I, 1)))
tmpArr(I - 1) = S
'logmessage "hex = " & hex(AscB(S))
Next
SimpleBinaryToString = tmpArr
End Function




===Export FBX===
==Modify file==
FBXExportLights (false)
===Binary===
FBXExportSelection (true)
[[Image:Before_and_after_binary_patching_with_vbs.png|thumb|400px|Before and after patching.]]
' mark FBXExport and hit F1 to get more options
Change FilePath and in case of binary patching use function "StringToByteArray".
FBXExport (CreateObject("WScript.Shell").SpecialFolders("Desktop") & "\export_test.fbx" )


 
FilePath = "C:\path_to\AKEVEnvWarehouse.oni"
==Read file==
===Import DAE===
  ' ### create stream objects
  CopyPaste, filePath, parentObject, 2
Set InputStream = CreateObject("ADODB.Stream")
 
InputStream.Type = 1
'use scene object if you don't want to group the imported object under a parent, e.g.:
  InputStream.Open
  CopyPaste, filePath, ActiveProject.ActiveScene, 2
Set OutputStream = CreateObject("ADODB.Stream")
 
OutputStream.Type = 1
 
OutputStream.Open
===Import DAE (and get name)===
Sometimes you want to get the name of the object you just imported.
' ### load input stream from file
 
  InputStream.LoadFromFile FilePath
In that case you use "ImportModel" instead of "CopyPaste"
   
 
  ' ### copy first 16 signs of input stream to output stream
  ImportModel filePath, [parentObject], , , "0"
  OutputStream.Write InputStream.Read(16)
  logmessage selection(0).parent
  logmessage selection(0) 'this gets root object (file name)
' ### apply patch
  logmessage selection(0).children(0) 'this gets first object of file
' # ASCII patching
 
patch_data = "ABCD"
If you want to remove the null object and select the new parent you add these lines:
  patch_data_length = len(patch_data)
  set nullObject = selection(0)
  '  patch_data_length = 4
  set newParent = selection(0).children(0)
InputStream.Position = InputStream.Position + patch_data_length
  CutObj newParent
OutputStream.Write CreateObject("System.Text.ASCIIEncoding").GetBytes_4(patch_data)
  DeleteObj nullObject
  ' # binary patching
SelectObj newParent
  'OutputStream.Write StringToByteArray("41424344")
 
 
===Binary===
scan_AKEV_file_table
   
   
  sub scan_AKEV_file_table
  ' ### re-add data that was cut off
' ##############################################
OutputStream.Write InputStream.Read
OniInputFile = "H:\Oni\AE\GameDataFolder\level1_Final\AKEVEnvWarehouse.oni"
   
' ##############################################
' ### unloader
   
InputStream.Close
Set OniInputFileStream = CreateObject("ADODB.Stream")
Set InputStream = Nothing
  OniInputFileStream.Type = 1
' ### modes: 2 = overwrite; 1 = dontoverwrite
OniInputFileStream.Open
' test: save to new file
  OniInputFileStream.LoadFromFile OniInputFile
' FilePath2 = "C:\path_to\AKEVEnvWarehouseB.oni"
OutputStream.SaveToFile FilePath, 2
OutputStream.Close
Set OutputStream = Nothing
 
 
==Conversions==
===String -> byte array===
function StringToByteArray(ThisString)
  for i = 1 To Len(ThisString) Step 2
str = str & Chr("&h" & Mid(ThisString, i, 2))
  next
   
   
  ' ### read AKEV textures table offset and size
  Set stream = CreateObject("ADODB.Stream")
  ByteNum = 4
  With stream
' ##############################################
.Open
TOffset = cLng("&H" & "28")
.CharSet = "Windows-1252"
' ##############################################
.Type = 2
OniInputFileStream.Position = TOffset
' ### 2 = text
BArr1 = OniInputFileStream.Read(ByteNum)
.WriteText str
.Position = 0
.Type = 1
' ### 1 = binary
StringToByteArray = .Read
.Close
End With
end function
   
   
ByteNum = 4
' ### usage
' ##############################################
ByteArray = StringToByteArray(ThisString)
TSize = cLng ("&H" & "2C")
 
' ##############################################
 
OniInputFileStream.Position = TSize
===Byte array -> string===
BArr2 = OniInputFileStream.Read(ByteNum)
Function ByteArrayToString(Binary)
  'Antonin Foller, https://www.motobit.com/
  'Optimized version of a simple BinaryToString algorithm.
' ### get AKEV textures table offset and size
 
TOffsetHex = SimpleBinaryToString(BArr1)
  Dim cl1, cl2, cl3, pl1, pl2, pl3
for i = ubound(TOffsetHex ) - 1 to 0 step -1
  Dim L
newhex = newhex & hex(Asc(TOffsetHex(i)))
  cl1 = 1
next
  cl2 = 1
logmessage newhex
  cl3 = 1
logmessage "name table offset: " & cLng("&H" & newhex)
  L = LenB(Binary)
TOffsetInt = cLng("&H" & newhex)
 
newhex = ""
  Do While cl1<=L
    pl3 = pl3 & Chr(AscB(MidB(Binary,cl1,1)))
TSizeHex = SimpleBinaryToString(BArr2)
    cl1 = cl1 + 1
for i = ubound(TSizeHex) - 1 to 0 step -1
    cl3 = cl3 + 1
newhex = newhex & hex(Asc(TSizeHex(i)))
    If cl3>300 Then
next
      pl2 = pl2 & pl3
logmessage newhex
      pl3 = ""
logmessage "name table size: " & cLng("&H" & newhex)
      cl3 = 1
TSizeInt = cLng("&H" & newhex)
      cl2 = cl2 + 1
      If cl2>200 Then
  logmessage "------------------------------"
        pl1 = pl1 & pl2
 
        pl2 = ""
        cl2 = 1
      End If
    End If
  Loop
  BinaryToString = pl1 & pl2 & pl3
End Function
   
   
' ### read table content
' ### usage
ByteNum = TSizeInt
MyString = ByteArrayToString(ByteArray)
OniInputFileStream.Position = TOffsetInt
 
  BArr3 = OniInputFileStream.Read(ByteNum)
 
  TContent = SimpleBinaryToString(BArr3)
==Math==
===Euler rotation -> matrix===
' ### name grapper
function cosn (n)
NG = ""
  cosn = cos(XSIMath.DegreesToRadians(n))
for each n in TContent
end function
if not Asc(n) = 0 then
function sinn (n)
NG = NG & n
  sinn = sin(XSIMath.DegreesToRadians(n))
else
end function
'if instr(NG, "TXMP") = 1 then
' write TXMP to array ?
' ################
logmessage NG
logmessage "input"
'end if
x = 60 : logmessage x
NG = ""
y = 60 : logmessage y
  end if
z = 60 : logmessage z
logmessage "##################"
logmessage "converted"
set RotMatX = XSIMath.CreateMatrix3(1, 0, 0, 0, cosn(x), sinn(x), 0, -sinn(x), cosn(x))
set RotMatY = XSIMath.CreateMatrix3(cosn(y), 0, -sinn(y), 0, 1, 0, sinn(y), 0, cosn(y))
set RotMatZ = XSIMath.CreateMatrix3(cosn(z), sinn(z), 0, -sinn(z), cosn(z), 0, 0, 0, 1)
RotMatZ.MulInPlace RotMatY
RotMatZ.MulInPlace RotMatX
for i=0 to 2
for j=0 to 2
  logmessage RotMatZ (i, j)
  next
  next
  end sub
  next
   
   
  Function SimpleBinaryToString(Binary)
  ' INFO : input
ReDim tmpArr(LenB(Binary) - 1)
' INFO : 60
For I = 1 To LenB(Binary)
' INFO : 60
S = Chr(AscB(MidB(Binary, I, 1)))
' INFO : 60
tmpArr(I - 1) = S
' INFO : ##################
Next
' INFO : converted
SimpleBinaryToString = tmpArr
' INFO : 0,25
  End Function
' INFO : 0,808012701892219
' INFO : 0,53349364905389
' INFO : -0,433012701892219
' INFO : -0,399519052838329
' INFO : 0,808012701892219
  ' INFO : 0,866025403784439
' INFO : -0,433012701892219
' INFO : 0,25
 


Output:
===Matrix -> Euler rotation===
  ' INFO : 0E40
  Function Atan2(y, x)
' INFO : name table offset: 3648
  If x > 0 Then
' INFO : 0A4A
    Atan2 = Atn(y / x)
' INFO : name table size: 2634
  ElseIf x < 0 Then
' INFO : ------------------------------
    Atan2 = Sgn(y) * (XSIMath.PI - Atn(Abs(y / x)))
' INFO : AKEVEnvWarehouse
  ElseIf y = 0 Then
' INFO : AGDBEnvWarehouse
    Atan2 = 0
' INFO : TXMP_DOOR_FRAME
  Else
' INFO : TXMPNONE
    Atan2 = Sgn(y) * XSIMath.PI / 2
  ' INFO : TXMPSUMI_1
  End If
  ' INFO : TXMPTC_CONTROL_01
  End Function
  ' [...]
   
  ' INFO : TXMPWH_DCTRBND
   
 
  function ToEuler(M00, M10, M20, M21, M22)
 
            a = M00
 
            b = M10
==Modify file==
            dim c, s, r
===Binary===
[[Image:Before_and_after_binary_patching_with_vbs.png|thumb|400px|Before and after patching.]]
Change FilePath and in case of binary patching use function "StringToByteArray".
 
FilePath =  "C:\path_to\AKEVEnvWarehouse.oni"
   
   
' ### create stream objects
            if b = 0 then
Set InputStream = CreateObject("ADODB.Stream")
                c = Sgn(a)
InputStream.Type = 1
                s = 0
InputStream.Open
                r = Abs(a)
Set OutputStream = CreateObject("ADODB.Stream")
OutputStream.Type = 1
OutputStream.Open
   
   
' ### load input stream from file
            elseif a = 0 then
  InputStream.LoadFromFile FilePath
                c = 0
                s = Sgn(b)
' ### copy first 16 signs of input stream to output stream
                r = Abs(b)
OutputStream.Write InputStream.Read(16)
               
            elseif Abs(b) > Abs(a) then
                t = a / b
                u = Sgn(b) * Sqr(1 + t * t)
                s = 1 / u
                c = s * t
                r = b * u
   
            else
                t = b / a
                u = Sgn(a) * Sqr(1 + t * t)
                c = 1 / u
                s = c * t
                r = a * u
   
   
' ### apply patch
    end if
' # ASCII patching
patch_data = "ABCD"
patch_data_length = len(patch_data)
'  patch_data_length = 4
InputStream.Position = InputStream.Position + patch_data_length
OutputStream.Write CreateObject("System.Text.ASCIIEncoding").GetBytes_4(patch_data)
' # binary patching
'OutputStream.Write StringToByteArray("41424344")
   
   
' ### re-add data that was cut off
            Z = -Atan2(s, c)
OutputStream.Write InputStream.Read
            Y = Atan2(M20, r)
   
            X = -Atan2(M21, M22)
' ### unloader
InputStream.Close
Set InputStream = Nothing
' ### modes: 2 = overwrite; 1 = dontoverwrite
' test: save to new file
' FilePath2 = "C:\path_to\AKEVEnvWarehouseB.oni"
OutputStream.SaveToFile FilePath, 2
OutputStream.Close
Set OutputStream = Nothing
 
 
==Conversions==
===String -> byte array===
function StringToByteArray(ThisString)
for i = 1 To Len(ThisString) Step 2
str = str & Chr("&h" & Mid(ThisString, i, 2))
next
   
   
Set stream = CreateObject("ADODB.Stream")
    X = XSIMath.RadiansToDegrees(X)
With stream
    Y = XSIMath.RadiansToDegrees(Y)
.Open
    Z = XSIMath.RadiansToDegrees(Z)
.CharSet = "Windows-1252"
.Type = 2
    ToEuler = array(X, Y, Z)
' ### 2 = text
.WriteText str
.Position = 0
.Type = 1
' ### 1 = binary
StringToByteArray = .Read
.Close
End With
  end function
  end function
   
   
  ' ### usage
  ' ################################
  ByteArray = StringToByteArray(ThisString)
  set RotMat = XSIMath.CreateMatrix3( _
 
0.25, 0.808012701892219, 0.53349364905389, _
 
-0.433012701892219, -0.399519052838329, 0.808012701892219, _
===Byte array -> string===
0.866025403784439, -0.433012701892219, 0.25 )
Function ByteArrayToString(Binary)
  'Antonin Foller, http://www.motobit.com
  'Optimized version of a simple BinaryToString algorithm.
' convert matrix to euler rotation and store values to array
 
ReXYZ = ToEuler(RotMat(0,0), RotMat(1,0), RotMat(2,0), RotMat(2,1), RotMat(2,2))
  Dim cl1, cl2, cl3, pl1, pl2, pl3
  Dim L
logmessage "reconverted"
  cl1 = 1
logmessage ReXYZ(0)
  cl2 = 1
logmessage ReXYZ(1)
  cl3 = 1
logmessage ReXYZ(2)
  L = LenB(Binary)
 
' INFO : 60
  Do While cl1<=L
' INFO : 60
    pl3 = pl3 & Chr(AscB(MidB(Binary,cl1,1)))
' INFO : 60
    cl1 = cl1 + 1
 
    cl3 = cl3 + 1
 
    If cl3>300 Then
===Euler rotation -> quaternion===
      pl2 = pl2 & pl3
dim x, y, z, dRotation, qRotation
      pl3 = ""
x = 90
      cl3 = 1
y = 0
      cl2 = cl2 + 1
z = 0
      If cl2>200 Then
        pl1 = pl1 & pl2
set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z))
        pl2 = ""
  set qRotation = XSIMath.CreateQuaternion()
        cl2 = 1
      End If
    End If
  Loop
  BinaryToString = pl1 & pl2 & pl3
  End Function
   
   
' ### usage
dRotation.GetQuaternion (qRotation)  
MyString = ByteArrayToString(ByteArray)
LogMessage qRotation.W
 
LogMessage qRotation.X
 
LogMessage qRotation.Y
==Math==
  LogMessage qRotation.Z
===Euler rotation -> matrix===
' INFO : 0,707106781186548
function cosn (n)
' INFO : 0,707106781186547
  cosn = cos(XSIMath.DegreesToRadians(n))
  ' INFO : 0
end function
' INFO : 0
function sinn (n)
  sinn = sin(XSIMath.DegreesToRadians(n))
end function
   
   
  ' ################
  ' to calculate oni quaternions from euler rotations use this setup:
  logmessage "input"
  ' LogMessage qRotation.X
  x = 60 : logmessage x
  ' LogMessage qRotation.Y
  y = 60 : logmessage y
  ' LogMessage qRotation.Z
  z = 60 : logmessage z
  ' LogMessage qRotation.W * -1
 
logmessage "##################"
 
  logmessage "converted"
===Quaternion -> Euler rotation===
  dim qW, qX, qY, qZ, qRotation, x, y, z
   
   
set RotMatX = XSIMath.CreateMatrix3(1, 0, 0, 0, cosn(x), sinn(x), 0, -sinn(x), cosn(x))
qW = 0.707106781186548
set RotMatY = XSIMath.CreateMatrix3(cosn(y), 0, -sinn(y), 0, 1, 0, sinn(y), 0, cosn(y))
qX = 0.707106781186547
set RotMatZ = XSIMath.CreateMatrix3(cosn(z), sinn(z), 0, -sinn(z), cosn(z), 0, 0, 0, 1)
qY = 0
qZ = 0
   
   
  RotMatZ.MulInPlace RotMatY
  set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
RotMatZ.MulInPlace RotMatX
   
   
for i=0 to 2
qRotation.GetXYZAngleValues x, y, z
  for j=0 to 2
logmessage XSIMath.RadiansToDegrees(x)
logmessage RotMatZ (i, j)
  logmessage XSIMath.RadiansToDegrees(y)
  next
logmessage XSIMath.RadiansToDegrees(z)
next
' INFO : 89,9999999999999
  ' INFO : 0
' INFO : 0
   
   
  ' INFO : input
  ' to calculate euler rotations from oni quaternions use this setup:
' INFO : 60
'qX = ...
' INFO : 60
'qY = ...
' INFO : 60
'qZ = ...
' INFO : ##################
'qW = ... * -1
' INFO : converted
  'set qRotation = XSIMath.CreateQuaternion (qW, qX, qY, qZ)
' INFO : 0,25
' INFO : 0,808012701892219
' INFO : 0,53349364905389
' INFO : -0,433012701892219
' INFO : -0,399519052838329
' INFO : 0,808012701892219
  ' INFO : 0,866025403784439
' INFO : -0,433012701892219
' INFO : 0,25




===Matrix -> Euler rotation===
==Check selection mode==
  Function Atan2(y, x)
  checkfilter
  If x > 0 Then
'use sub to make use of the exit command
    Atan2 = Atn(y / x)
  ElseIf x < 0 Then
sub checkfilter
    Atan2 = Sgn(y) * (XSIMath.PI - Atn(Abs(y / x)))
Select Case Selection.Filter.Name
  ElseIf y = 0 Then
'caution: case-sensitive
    Atan2 = 0
  Else
    Atan2 = Sgn(y) * XSIMath.PI / 2
  End If
End Function
   
   
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
==3D mesh==
===General mesh information===
logmessage selection.count
   
   
  function ToEuler(M00, M10, M20, M21, M22)
  logmessage selection(0).Name
            a = M00
            b = M10
            dim c, s, r
            if b = 0 then
                c = Sgn(a)
                s = 0
                r = Abs(a)
            elseif a = 0 then
                c = 0
                s = Sgn(b)
                r = Abs(b)
               
            elseif Abs(b) > Abs(a) then
                t = a / b
                u = Sgn(b) * Sqr(1 + t * t)
                s = 1 / u
                c = s * t
                r = b * u
   
   
            else
logmessage selection(0).Materials(0).Name
                t = b / a
logmessage selection(0).Materials(0).Library.name
                u = Sgn(a) * Sqr(1 + t * t)
logmessage selection(0).Materials(0).shaders(0).name
                c = 1 / u
logmessage selection(0).Materials(0).CurrentImageClip.source.filename.value
                s = c * t
logmessage selection(0).Materials(0).CurrentImageClip.source.Parameters("XRes").Value
                r = a * u
logmessage selection(0).Material.CurrentImageClip.source.Parameters("YRes").Value
   
   
    end if
logmessage selection(0).Material.CurrentUV.name
   
   
            Z = -Atan2(s, c)
logmessage selection(0).activeprimitive.geometry.clusters(0).name
            Y = Atan2(M20, r)
' look for UV cluster names
            X = -Atan2(M21, M22)
' xsi-generated: "Texture_Coordinates_AUTO"
' onisplit-generated: "NodeProperties"
   
   
    X = XSIMath.RadiansToDegrees(X)
logmessage selection(0).sclx.value
    Y = XSIMath.RadiansToDegrees(Y)
logmessage selection(0).scly.value
    Z = XSIMath.RadiansToDegrees(Z)
logmessage selection(0).sclz.value
   
   
    ToEuler = array(X, Y, Z)
logmessage selection(0).rotx.value
  end function
  logmessage selection(0).roty.value
logmessage selection(0).rotz.value
   
   
  ' ################################
  logmessage selection(0).posx.value
set RotMat = XSIMath.CreateMatrix3( _
logmessage selection(0).posy.value
0.25, 0.808012701892219, 0.53349364905389, _
logmessage selection(0).posz.value
-0.433012701892219, -0.399519052838329, 0.808012701892219, _
0.866025403784439, -0.433012701892219, 0.25 )
   
   
  logmessage selection(0).rotorder.value
' convert matrix to euler rotation and store values to array
ReXYZ = ToEuler(RotMat(0,0), RotMat(1,0), RotMat(2,0), RotMat(2,1), RotMat(2,2))
  logmessage "reconverted"
logmessage ReXYZ(0)
logmessage ReXYZ(1)
logmessage ReXYZ(2)
' INFO : 60
' INFO : 60
' INFO : 60




===Euler rotation -> quaternion===
===Materials and textures===
dim x, y, z, dRotation, qRotation
====Get all targets of an image clip====
x = 90
  set imgClip = GetValue("Clips._marker_blackness_tga")
y = 0
  set imgClipTargets = imgClip.GetShaderParameterTargets
z = 0
 
  logmessage imgClipTargets.count
  set dRotation = XSIMath.CreateRotation(XSIMath.DegreesToRadians(x), XSIMath.DegreesToRadians(y), XSIMath.DegreesToRadians(z))  
  for each t in imgClipTargets
  set qRotation = XSIMath.CreateQuaternion()
logmessage t
  next
dRotation.GetQuaternion (qRotation)
LogMessage qRotation.W
LogMessage qRotation.X
LogMessage qRotation.Y
LogMessage qRotation.Z
' INFO : 0,707106781186548
' INFO : 0,707106781186547
' INFO : 0
' INFO : 0
  ' to calculate oni quaternions from euler rotations use this setup:
' LogMessage qRotation.X
  ' LogMessage qRotation.Y
' LogMessage qRotation.Z
  ' LogMessage qRotation.W * -1




===Quaternion -> Euler rotation===
====Get all material libraries and materials====
  dim qW, qX, qY, qZ, qRotation, x, y, z
  for each ml in Application.ActiveProject.ActiveScene.MaterialLibraries
logmessage ml
for each m in ml.items
logmessage m.name ' (material)
next
logmessage "--------------------------"
next
   
   
qW = 0.707106781186548
' INFO : Sources.Materials.DefaultLib
qX = 0.707106781186547
' INFO : Scene_Material
qY = 0
  ' INFO : sosMatBarrier
qZ = 0
  ' INFO : sosMatBlackness
   
  ' INFO : sosMatDanger
  set qRotation = XSIMath.CreateQuaternion (qW, qX , qY, qZ)
' INFO : sosMatGhost
   
' INFO : sosMatImpassable
qRotation.GetXYZAngleValues x, y, z
  ' INFO : sosMatStairs
logmessage XSIMath.RadiansToDegrees(x)
  ' INFO : --------------------------
logmessage XSIMath.RadiansToDegrees(y)
' INFO : Sources.Materials.MaterialLibrary
logmessage XSIMath.RadiansToDegrees(z)
' INFO : AIR_STAIRWALL_LOB1
' INFO : 89,9999999999999
' INFO : AIR_WAITSEAT3
' INFO : 0
' INFO : AIR_WAITSEAT2
' INFO : 0
' INFO : COLLISION
   
  ' INFO : --------------------------
  ' to calculate euler rotations from oni quaternions use this setup:
'qX = ...
'qY = ...
'qZ = ...
'qW = ... * -1
  'set qRotation = XSIMath.CreateQuaternion (qW, qX, qY, qZ)




==Check selection mode==
====Check an object's main material for TwoSided-ness====
  checkfilter
  ' test and toggles an object's main material for TwoSided-ness
  'use sub to make use of the exit command
' this is also a prerequired test for transparency
   
  ' the difficulty is to get the TextureObject (often named Image)
  sub checkfilter
  ' the magic happens at FindShaders, I often fail to find such trivial stuff
Select Case Selection.Filter.Name
  ' imo the xsi is terrible incomplete/unintuitive
'caution: case-sensitive
' e.g. look at "Find (ShaderCollection)" in the help
' it will give you information about meshes such as cubes ...
   
   
Case "object"
  matLib = selection(0).Materials(0).Library.name
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
 
==3D mesh==
===General mesh information===
====Bounding box values====
' this could be useful to create a bounding box for [[OBD_talk:OFGA#XML|OFGA files]]
   
   
  ' let's get the bounding box of a simple cylinder
set mat = selection(0).Material
  ' the output values will be absolute positions
materialName = mat.name
  CreatePrim "Cylinder", "MeshSurface"
  ' let us see if there is an Image TextureObject
  set shaders = mat.FindShaders(siShaderFilter)
  textureObj = "Image"
   
   
dim xmin, ymin, zmin, xmax, ymax, zmax
  'if typename(shaders(textureObj)) = "Texture" then ' if not it is Nothing
dim list
  ' logmessage "material has texture object ""Image"""
  'if you use SelectionList the objects will be treated as one single object
  'end if
'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:
  Set list = CreateObject("System.Collections.ArrayList")
  ' INFO : Lower Bound: -1 / -2 / -1
  for each n in shaders
  ' INFO : Upper Bound: 1 / 2 / 1
list.Add n.name
 
  next
====Get the scaling, rotation and position of selected objects====
logmessage "mesh name: " & selection(0)
foundShaderParameterTransparency = false
logmessage selection(0).sclx.value
'foundUniqueShaderName = false
  logmessage selection(0).scly.value
shaderName = ""
logmessage selection(0).sclz.value
if list.Contains(textureObj) = true then
logmessage selection(0).rotx.value
set oColorShareShader = GetValue("Sources.Materials." & matLib & "." & mat.name & "." & textureObj)
logmessage selection(0).roty.value
set oTargets = oColorShareShader.GetShaderParameterTargets("")
logmessage selection(0).rotz.value
   
logmessage selection(0).posx.value
scriptObjArray = split(oTargets(0), ".")
logmessage selection(0).posy.value
'logmessage scriptObjArray(0) ' Sources (fixed name? Could be considered a folder.)
logmessage selection(0).posz.value
'logmessage scriptObjArray(1) ' Materials (fixed name? Could be considered a folder.)
 
'logmessage scriptObjArray(2) ' MaterialsLib (usually each object has its own MaterialsLib)
 
'logmessage scriptObjArray(3) ' Material
====Get scaling, rotation, and position of not selected objects====
'logmessage scriptObjArray(4) ' Shader e.g. Phong
  ' GetValue("NAME.kine.global.rotx")
shaderName = scriptObjArray(4)
  ' NAME must be the exact mesh name
  ' let's say you want the data of one character's pelvis
for each t in oTargets
logmessage GetValue("pelvis.kine.global.sclx")
logmessage t
logmessage GetValue("pelvis.kine.global.scly")
if t.name = "transparency" then
logmessage GetValue("pelvis.kine.global.sclz")
foundShaderParameterTransparency = true
logmessage GetValue("pelvis.kine.global.rotx")
exit for
  logmessage GetValue("pelvis.kine.global.roty")
end if
logmessage GetValue("pelvis.kine.global.rotz")
next
logmessage GetValue("pelvis.kine.global.posx")
  end if
logmessage GetValue("pelvis.kine.global.posy")
   
logmessage GetValue("pelvis.kine.global.posz")
  if foundShaderParameterTransparency = false then
 
logmessage "material is not TwoSided, lets reverse now"
 
SIConnectShaderToCnxPoint "Sources.Materials." & matLib & "." & materialName & ".Image", "Sources.Materials." & matLib & "." & materialName & "." & shaderName & ".transparency", False
===Points===
  else
====Get position of points (with selection mode point)====
logmessage "material is TwoSided, lets reverse now"
' a point must be selected
RemoveAllShadersFromCnxPoint "Sources.Materials." & matLib & "." & materialName & "." & shaderName & ".transparency", siShaderCnxPointBasePorts
  ' gets xyz position of first selected point of the first selected object
  end if
logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
logmessage Selection(0).SubComponent.ComponentCollection(0).position.z


====Get position of points (with selection mode object)====
Output example:
  ' an object must be selected
  ' INFO : Sources.Materials.DefaultLib.Material.Phong.diffuse
  ' gets xyz position of point 0 of the first selected object
  ' INFO : material is not TwoSided, lets reverse now
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.x
  SIConnectShaderToCnxPoint "Sources.Materials.DefaultLib.Material.Image", "Sources.Materials.DefaultLib.Material.Phong.transparency", False
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.y
  logmessage selection(0).activeprimitive.geometry.Points(0).Position.z
  ' INFO : Sources.Materials.DefaultLib.Material.Phong.diffuse
' INFO : Sources.Materials.DefaultLib.Material.Phong.transparency
' INFO : material is TwoSided, lets reverse now
  RemoveAllShadersFromCnxPoint "Sources.Materials.DefaultLib.Material.Phong.transparency", siShaderCnxPointBasePorts


====Get and set position of points (without selection) right after object creation====
 
  ' point positions are relative to the object's center
===Clusters===
  ' to get the absolute point positions add center to point
'does a certain cluster type exist ?
  ' to set the absolute point positions subtract center from point
'set cls = selection(0).activeprimitive.geometry.clusters.find( siPolygonCluster )
 
' more interesting is how many of that type exist
for each n in selection(0).activeprimitive.geometry.clusters
    logmessage "Cluster " & n.name & " is of type " & n.type
next
' "poly" = polygon cluster
' "sample" = UV cluster
 
Output example:
  ' INFO : Cluster Polygon4 is of type poly
' INFO : Cluster Polygon1 is of type poly
  ' INFO : Cluster Texture_Coordinates_AUTO is of type sample
 
 
====Bounding box values====
  ' this could be useful to create a bounding box for [[XML:OFGA|OFGA files]]
   
   
  set oRoot = application.activeproject.activescene.root
  ' let's get the bounding box of a simple cylinder
  set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
  ' the output values will be absolute positions
CreatePrim "Cylinder", "MeshSurface"
   
   
  ' to test our code move center to somewhere else
  dim xmin, ymin, zmin, xmax, ymax, zmax
Translate oObj, 9, 11, 13, siRelative, siGlobal, siCtr, siXYZ, , , , , , , , , , 0
dim list
  SaveKey oObj & ".kine.local.posx," & oObj & ".kine.local.posy," & oObj & ".kine.local.posz", 1, , , , True
'if you use SelectionList the objects will be treated as one single object
'set list = GetValue( "SelectionList" )
set list = GetValue( selection(0) )
GetBBox list, TRUE, xmin, ymin, zmin, xmax, ymax, zmax
  LogMessage "Lower Bound: " & xmin & " / " & ymin & " / " & zmin
LogMessage "Upper Bound: " & xmax & " / " & ymax & " / " & zmax
   
   
  FreezeObj oObj
  ' expected output:
  set oGeometry = oObj.activeprimitive.geometry
  ' INFO : Lower Bound: -1 / -2 / -1
  aPositions = oGeometry.Points.PositionArray
  ' INFO : Upper Bound: 1 / 2 / 1
' get old position
 
'                                        (xyz, point)
====Get the scaling, rotation and position of selected objects====
  logmessage "old point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
  logmessage "mesh name: " & selection(0)
  logmessage "old point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
  logmessage selection(0).sclx.value
  logmessage "old point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
  logmessage selection(0).scly.value
   
  logmessage selection(0).sclz.value
' set new position
  logmessage selection(0).rotx.value
aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
  logmessage selection(0).roty.value
  aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
  logmessage selection(0).rotz.value
  aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
  logmessage selection(0).posx.value
   
  logmessage selection(0).posy.value
' update the array
  logmessage selection(0).posz.value
oGeometry.Points.PositionArray = aPositions
' get new position
  logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
  logmessage "new point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
  logmessage "new point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
' INFO : old point 0 posx: -4
' INFO : old point 0 posy: -4
' INFO : old point 0 posz: -4
' INFO : new point 0 posx: -7
' INFO : new point 0 posy: -7
' INFO : new point 0 posz: -7




====Get global point position====
====Get scaling, rotation, and position of not selected objects====
  set oObj = selection(0)
  ' GetValue("NAME.kine.global.rotx")
set oTrans = oObj.Kinematics.Local.Transform
  ' NAME must be the exact mesh name
set oPoint0 = oObj.ActivePrimitive.Geometry.Points(0)
  ' let's say you want the data of one character's pelvis
set oPoint7 = oObj.ActivePrimitive.Geometry.Points(7)
  logmessage GetValue("pelvis.kine.global.sclx")
set oPos0 = oPoint0.Position
logmessage GetValue("pelvis.kine.global.scly")
set oPos7 = oPoint7.Position
  logmessage GetValue("pelvis.kine.global.sclz")
logmessage GetValue("pelvis.kine.global.rotx")
  ' scaling must be frozen to 1 before we can calculate the size from local values
  logmessage GetValue("pelvis.kine.global.roty")
ResetTransform selection(0), siCtr, siScl, siXYZ
logmessage GetValue("pelvis.kine.global.rotz")
   
  logmessage GetValue("pelvis.kine.global.posx")
  logmessage "local p0: "& oPos0.X & " " & oPos0.Y & " " & oPos0.Z
logmessage GetValue("pelvis.kine.global.posy")
set oGlobalPos0 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos0)
  logmessage GetValue("pelvis.kine.global.posz")
  logmessage "global p0: "& oGlobalPos0.X & " " & oGlobalPos0.Y & " " & oGlobalPos0.Z
 
 
  logmessage "local p7: "& oPos7.X & " " & oPos7.Y & " " & oPos7.Z
===Points===
set oGlobalPos7 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos7)
====Get position of points (with selection mode point)====
  logmessage "global p7: "& oGlobalPos7.X & " " & oGlobalPos7.Y & " " & oGlobalPos7.Z
  ' a point must be selected
 
  ' gets xyz position of first selected point of the first selected object
  logmessage "size: " & oPos7.X - oPos0.X & " " & _
  logmessage Selection(0).SubComponent.ComponentCollection(0).position.x
oPos7.Y - oPos0.Y & " " & _
  logmessage Selection(0).SubComponent.ComponentCollection(0).position.y
oPos7.Z - oPos0.Z
  logmessage Selection(0).SubComponent.ComponentCollection(0).position.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


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


===Edges===
====Get and set position of points (without selection) right after object creation====
...
  ' point positions are relative to the object's center
===Polygons===
  ' to get the absolute point positions add center to point
...
  ' to set the absolute point positions subtract center from point
 
==Property Page==
===Detect a PPG===
' this check is used to decide to either build or open a PPG
set oRoot = ActiveProject.ActiveScene.Root
if typename(oRoot.Properties("my_PPG")) = "Nothing" then
logmessage "couldn't find my_PPG"
else
logmessage "found my_PPG"
end if
 
 
===Disable PPG popups===
  ' let's say a big amount of objects will be created and each object will open a PPG
  ' in that case for user convenience those PPG popups should be disabled
 
  ' disable PPG popup
Preferences.SetPreferenceValue "Interaction.autoinspect", false
   
   
  ' creates the cube mesh but no PPG will show up
  set oRoot = application.activeproject.activescene.root
  CreatePrim "Cube", "MeshSurface"
  set oObj = oRoot.addgeometry( "Cube", "MeshSurface", "test" )
   
   
  ' enable PPG popup again
  ' to test our code move center to somewhere else
  Preferences.SetPreferenceValue "Interaction.autoinspect", true
  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
 
===Build a PPG===
  ' general PPG setup
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_PPG")
set oPPGLayout = oPSet.PPGLayout
   
   
  ' PPG content
FreezeObj oObj
  ' [...]
set oGeometry = oObj.activeprimitive.geometry
aPositions = oGeometry.Points.PositionArray
  ' get old position
  '                                         (xyz, point)
logmessage "old point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
logmessage "old point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
logmessage "old point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
   
   
  ' open PPG
  ' set new position
  InspectObj oPSet
  aPositions(0, 0) = -7 - GetValue(oObj & ".kine.global.posx")
aPositions(1, 0) = -7 - GetValue(oObj & ".kine.global.posy")
aPositions(2, 0) = -7 - GetValue(oObj & ".kine.global.posz")
' update the array
oGeometry.Points.PositionArray = aPositions
' get new position
logmessage "new point 0 posx: " & aPositions(0, 0) + GetValue(oObj & ".kine.global.posx")
logmessage "new point 0 posy: " & aPositions(1, 0) + GetValue(oObj & ".kine.global.posy")
logmessage "new point 0 posz: " & aPositions(2, 0) + GetValue(oObj & ".kine.global.posz")
' INFO : old point 0 posx: -4
' INFO : old point 0 posy: -4
' INFO : old point 0 posz: -4
' INFO : new point 0 posx: -7
' INFO : new point 0 posy: -7
' INFO : new point 0 posz: -7




===PPG content===
====Get global point position====
====Button====
set oObj = selection(0)
If you need quote signs in the Logic section you write two signs: "" (Not shown in example.)
set oTrans = oObj.Kinematics.Local.Transform
 
set oPoint0 = oObj.ActivePrimitive.Geometry.Points(0)
  oPPGLayout.AddButton("btnFuncName", "click here to trigger afunction").setAttribute siUICX, 200
  set oPoint7 = oObj.ActivePrimitive.Geometry.Points(7)
   
set oPos0 = oPoint0.Position
  oPPGLayout.Logic = " sub btnFuncName_OnClicked" & vbCrlf & _
  set oPos7 = oPoint7.Position
  " btnFuncNameReal" & vbCrlf & _
   
  " end sub"
  ' scaling must be frozen to 1 before we can calculate the size from local values
  ResetTransform selection(0), siCtr, siScl, siXYZ
   
   
  function btnFuncNameReal
  logmessage "local p0: "& oPos0.X & " " & oPos0.Y & " " & oPos0.Z
logmessage "hello world"
  set oGlobalPos0 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos0)
  end function
logmessage "global p0: "& oGlobalPos0.X & " " & oGlobalPos0.Y & " " & oGlobalPos0.Z
 
New lines in Logic need
  logmessage "local p7: "& oPos7.X & " " & oPos7.Y & " " & oPos7.Z
  & vbCrlf & _
set oGlobalPos7 = XSIMath.MapObjectPositionToWorldSpace( oTrans, oPos7)
on the earlier line.
  logmessage "global p7: "& oGlobalPos7.X & " " & oGlobalPos7.Y & " " & oGlobalPos7.Z
 
 
 
  logmessage "size: " & oPos7.X - oPos0.X & " " & _
====Text input====
oPos7.Y - oPos0.Y & " " & _
  oPSet.AddParameter3 "ParamName", siString
oPos7.Z - oPos0.Z
  oPPGLayout.AddItem "ParamName", "Caption"
' 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




====Integer input====
===Edges===
oPSet.AddParameter3 "ParamName", siInt2, , , , false, 0
...
oPPGLayout.AddItem "ParamName", "Caption"
===Polygons===
...


If you try to set a value that is below the defined minimum, you will get this error text:
' ERROR : 2006-EDIT-SetValue - Unexpected failure.
' Syntax: PPGname.data, value
' SetValue "Turret.int1", "0"
' ERROR : Überlauf: 'setvalue' - [line ''N'']
German devs? I expected "overflow", not "Überlauf".




====Droplist====
==Layers==
  oPSet.AddParameter3 "Team", siString, 0
===Check if object is member of layer===
aListTeams = Array( "Konoko", 0, _
  logmessage isMemberOfLayer(selection(0), "layerNameToTest")
"TCTF", 1, _
"Syndicate", 2, _
function isMemberOfLayer(obj, layerName)
"Neutral", 3, _
dim list
"SecurityGuard", 4, _
set list = selectMembers ("Layers." & layerName, 0) ' 0 = for not changing the current selection
"RogueKonoko", 5, _
"Switzerland (is melee-immune)", 6, _
for each o in list
"SyndicateAccessory", 7 )
if o = obj then
  oPPGLayout.AddEnumControl "Team", aListTeams, "", siControlCombo
isMemberOfLayer = "yes"
exit for
end if
next
  end function
 


===Get layer name of an object===
logmessage RecursiveEnum (selection(0), false, false)
function RecursiveEnum( in_Comp, in_Type, in_FirstParentOnly )
  dim list, elem, layerNameToCheck
  set list = EnumElements( in_Comp, in_Type )
  if TypeName(list) <> "Nothing" then
      for each elem in list
          if instr(elem, "Layers") = 1 and instr(elem, ".Members") > 1 then
          layerNameToCheck = replace(replace(elem,"Layers.", ""),".Members", "")
      if not layerNameToCheck = "" then
RecursiveEnum = layerNameToCheck
end if
      exit for
          end if
      next
  end if
end function
' INFO : Layer_Default
==Property Page==
===Detect a PPG===
' this check is used to decide to either build or open a PPG
set oRoot = ActiveProject.ActiveScene.Root
if typename(oRoot.Properties("my_PPG")) = "Nothing" then
logmessage "couldn't find my_PPG"
else
logmessage "found my_PPG"
end if
===Disable PPG popups===
' let's say a big amount of objects will be created and each object will open a PPG
' in that case for user convenience those PPG popups should be disabled
' disable PPG popup
Preferences.SetPreferenceValue "Interaction.autoinspect", false
' creates the cube mesh but no PPG will show up
CreatePrim "Cube", "MeshSurface"
' enable PPG popup again
Preferences.SetPreferenceValue "Interaction.autoinspect", true
===Build a PPG===
' general PPG setup
set oPSet = ActiveSceneRoot.AddProperty("CustomProperty", false, "my_PPG")
set oPPGLayout = oPSet.PPGLayout
' PPG content
' [...]
' open PPG
InspectObj oPSet
===PPG content===
====Button====
If you need quote signs in the Logic section you write two signs: "" (Not shown in example.)
oPPGLayout.AddButton("btnFuncName", "click here to trigger afunction").setAttribute siUICX, 200
oPPGLayout.Logic = " sub btnFuncName_OnClicked" & vbCrlf & _
" btnFuncNameReal" & vbCrlf & _
" end sub"
function btnFuncNameReal
logmessage "hello world"
end function
New lines in Logic need
& vbCrlf & _
on the earlier line.
====Text input====
oPSet.AddParameter3 "ParamName", siString
oPPGLayout.AddItem "ParamName", "Caption"
====Integer input====
oPSet.AddParameter3 "ParamName", siInt2, , , , false, 0
oPPGLayout.AddItem "ParamName", "Caption"
If you try to set a value that is below the defined minimum, you will get this error text:
' ERROR : 2006-EDIT-SetValue - Unexpected failure.
' Syntax: PPGname.data, value
' SetValue "Turret.int1", "0"
' ERROR : Überlauf: 'setvalue' - [line ''N'']
German devs? I expected "overflow", not "Überlauf".
====Droplist====
oPSet.AddParameter3 "Team", siString, 0
aListTeams = Array( "Konoko", 0, _
"TCTF", 1, _
"Syndicate", 2, _
"Neutral", 3, _
"SecurityGuard", 4, _
"RogueKonoko", 5, _
"Switzerland (is melee-immune)", 6, _
"SyndicateAccessory", 7 )
oPPGLayout.AddEnumControl "Team", aListTeams, "", siControlCombo
====Radio options====
oPSet.AddParameter3 "Team", siString, 0
aListTeams = Array( "Konoko", 0, _
"TCTF", 1, _
"Syndicate", 2, _
"Neutral", 3, _
"SecurityGuard", 4, _
"RogueKonoko", 5, _
"Switzerland (is melee-immune)", 6, _
"SyndicateAccessory", 7 )
oPPGLayout.AddEnumControl "Team", aListTeams, "", siControlRadio
====Checkbox====
' create checkbox and remove key symboles by setting parameter "Animatable" to false or 0
' CustomProperty.AddParameter3( ScriptName, ValueType, [DefaultValue], [Min], [Max], [Animatable], [ReadOnly] )
oPSet.AddParameter3 "Check1", siBool, , , , 0, 0
oPPGLayout.AddItem "Check1", "Checkbox_caption"
====Spacer====
' AddSpacer( [Width], [Height] )
oPPGLayout.AddSpacer 25
==Softimage ICE==
===Show a simple value in the viewport===
[[Image:XSI_ModTool_ICE_workaround_displaying_a_flag.png|thumb|400px]]


====Radio options====
oPSet.AddParameter3 "Team", siString, 0
aListTeams = Array( "Konoko", 0, _
"TCTF", 1, _
"Syndicate", 2, _
"Neutral", 3, _
"SecurityGuard", 4, _
"RogueKonoko", 5, _
"Switzerland (is melee-immune)", 6, _
"SyndicateAccessory", 7 )
oPPGLayout.AddEnumControl "Team", aListTeams, "", siControlRadio
====Checkbox====
' create checkbox and remove key symboles by setting parameter "Animatable" to false or 0
' CustomProperty.AddParameter3( ScriptName, ValueType, [DefaultValue], [Min], [Max], [Animatable], [ReadOnly] )
oPSet.AddParameter3 "Check1", siBool, , , , 0, 0
oPPGLayout.AddItem "Check1", "Checkbox_caption"
====Spacer====
' AddSpacer( [Width], [Height] )
oPPGLayout.AddSpacer 25
==Softimage ICE==
===Show a simple value in the viewport===
  CreatePrim "Cube", "MeshSurface"
  CreatePrim "Cube", "MeshSurface"
   
   
2,112

edits