trueSpace script params

function Execute(params)
{
	var controlIn1 = params.ConValue('controlIn1');
	var controlIn2 = params.ConValue('controlIn2');

	var scriptFrom = params.GetControlIn();

	System.Trace(scriptFrom);

	if(scriptFrom == "controlIn2")
		params.SetControlOut("ControlOut2")
	else
		params.SetControlOut("ControlOut1")

	// params.ConValue("controlOut1") = ...
	// params.ConValue("ControlOut2") = ...
}

params.GetControlIn() gets the name of the control input that runs the script, so can have several input controls and make code decisions based on which input was used
params.SetControlOut(str) does the opposite and lets you choose which output control will fire off

params.SetTerminationFlag() will stop all downstream execution

params.GetControlIn() does not work, so this whole idea is dead

Random trueSpace Notes

The script command Physics.Impulse has max XYZ values of 100. Higher values are ignored. This command only has a small effect on the object motion.

When working with script combined with the axis tool always flatten the axis back into the geometry.

The saturate node for materials does not load into trueSpace. It just gives an error.

RsApp.Help() command needs the xslt file found in the home directory in order to view the resulting xml files properly.

IK locks and IK handles attach to bones, not to joints.

A jscript object cannot properly read changes to a universal array connector if was changed from outside by a command script.

The params object can be used to stop down stream execution of command scripts via params.SetTerminationFlag()

When editing a morph target it seems best to set mesh editor auto triangulation setting to “None” .

Linear interpolation works best for IK skeletons to prevent sliding.

Space.Unselect() command will change 3D selection behavior. Before run 3D view select will select members of a 2D group. After run the 2D group will be selected instead of one of it’s 3D members.

Rough Node Connector Notes

OnLoadDefault – not in script

OnDefaultValue – called when connector is created, maybe can use to save copy of default value of conn to read later

OnSetValue – object node only, called before value is saved on connector. after value is set all connected connectors will be set “invalid”

OnInvalidate – do something if marked as invalid

OnGetValue – object node only, for intensive calculations, is run only when value is requested not when any input changes(OnComputeInputs)

Activity.ToggleAutoRun(source node, enable) – enable false is off, enable true is toggle on/off? for command node only

Space.AutoRunActivities() will run all nodes that are setup with autorun, setup via ToggleAutoRun command or LMB hold icon of the command node and choose “Run automatically”

RtNotifyEvents travel up encapsulation tree to the rootnode, event can be captured by encaps coding

Commands can be run simultaneous shared mode if nothing in the graph changes – no evidence of this since almost everything I write will change the graph. Need test on some pure intensive calculations see if can run parallel (simultaneous shared mode ?== parallel)

Creating Plugins with Unreal4 – 3DBuzz lesson notes

Updated notes for UE4 v4.23.0, Creating Plugins with Unreal4 videos

https://www.3dbuzz.com/forum/threads/204335-Creating-Plugins-with-Unreal-4-some-notes-for-version-4-10-1?p=1616145#post1616145

video 03

don’t create file folder structure, instead
Edit > Plugins > New Plugin
blank template …

edit CoolPlugin.uplugin and change “RunTime” to “Editor”

edit CoolPlugin.Build.cs and add private dependencies
“UnrealEd”,
“InputCore”,
“Core”,
“EditorStyle”,
“CoreUObject”

Use the files created by the New Plugins process, CoolPlugin.h and CoolPlugin.cpp instead of Module.cpp and Module.h
Skip creation of PCH file? – looks like something is built in already?

video 04

Edit > Editor Preferences, General > Miscellaneous, check Display UI Extension Points – shows that “WindowLocalTabSpawners” does not exist, so use “WindowGlobalTabSpawners” instead

replace #include “Slate.h” with

include “SlateBasics.h”

include “SlateExtras.h”

LOCTEXT_NAMESPACE already exists from the New Plugins process

change all references from “Module” to “FCoolPluginModule”

place new code inside the LOCTEXT_NAMESPACE define

video 05

place CoolWindow.h before CoolPlugin.h inside the CoolWindow.cpp file

video 06 – nothing

video 07


first error is for ATriggerBox – search unreal online docs for ATriggerBox – api says #include “Engine/TriggerBox.h”
add #include “Engine/TriggerBox.h” to the CoolPlugin.h file

error – cannot convert from FString to FText
CoolWindow.h change FString GetBoundsActorName() to FText GetBoundsActorName()
CoolWindow.cpp the same change and wrap the return values with FText::FromString(xxx)

error – iterator wont work, undefined class TActorIterator, ATriggerBox already has include so should be ok
search docs for TActorIterator says #include “EngineUtils.h”
add #include “EngineUtils.h” to the CoolPlugin.h file

error – cannot convert TIndirectArray to TArray
hover on GEngine->GetWorldContexts() shows that it returns a TIndirectArray not an TArray – so change TArray to TIndirectArray

video 08


convert FString to FText error for GetAlleySpaceText, wrap return value with FText::FromString(xxx)

video 09


error – UCubeBuilder not defined, search docs shows UBrushBuilder, with a link to UEditorBrushBuilder and now can see a UCubeBuilder link, finally UCubeBuilder says #include “Builders/CubeBuilder.h”
add #include “Builders/CubeBuilder.h” to the CoolPlugin.h file

error – GetBrush is not member of UWorld, so replace GetBrush with GetDefaultBrush

strange FText conversion error const Othertype to FText::EInitToEmptyString, error from file Attribute.h
output console error points to the .Text(FString(TEXT(“Build”)))
replace with .Text(LOCTEXT(“Build”,”Build”))

and now the whole thing works with ue 4.23.0

Question Messagebox

The trueSpace scripting question messagebox can be hidden behind the main display. This is a solution to that problem.

// https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-messagebox
function Question(strText)
{
	var nSecondsToWait = -1;
	var strTitle = "Question";
	// values are hexadecimal
	var MB_YESNO = 4;
	var MB_SYSTEMMODAL = 4096;//1000L force on top
	var MB_ICONQUESTION = 32;//20L question mark symbol

	var nType = MB_YESNO+MB_SYSTEMMODAL+MB_ICONQUESTION;
	var IDYES = 6;
	var IDNO = 7;

	var shell = new ActiveXObject("WScript.shell");
	var button = shell.Popup (strText, nSecondsToWait, strTitle, nType);

	if(button == IDYES) return true;
	return false;
}

if(!Question("Special mesh processing\nDo you wish to continue?")) {
params.ConValue("abort") = 1;
return;
}

Misc trueSpace script

// https://rosettacode.org/wiki/Map_range
function MapRange(val,a1,a2,b1,b2)
{
	var val2 = Math.min(val,a2);
	s = Math.max(val2, a1);
	return b1 + (s-a1)*(b2-b1)/(a2-a1)
}

map range a1,a2 to b1,b2

RsAnim.PutFrame places a single item at a certain frame without affecting other items in the scene, useful for some “set driven key” type behaviors.

LE.ConfirmEncapsulation(”, true, ”) – first arg nodes(guessing semi colon delimited list) or blank for selection, second arg true=3d false=2d, third arg name for new node

Workspace Shell Tool

Impractical process to simulate the shell tool scaffolding action of modelspace.

Workspace Scaffolding
  1. Select all faces of the mesh
  2. Run trueBevel script with a small amount
  3. Run fix geometry tools script and choose zero area triangles to select the narrow faces
  4. Invert the selection and delete
  5. Select all faces – the fix geometry changes the selection mode so be sure face selection is active
  6. copy selection – not the copy found the bottom of the interface
  7. flip faces
  8. invert selection
  9. static sweep script with average normals unchecked
  10. scale up or move
  11. select all and merge vertices coincidence with a small value

Truespace Script Custom Event

OnCustomEvent

For some special situations you may need to define custom communication between your objects.
Use RsApp.SendCustomEvent method to send an event and implement OnCustomEvent to handle the
event.

function OnCustomEvent(params)
{
	var eventData = params.Param('vtEventData');
	System.Trace(eventData)
}
RsApp.SendCustomEvent(Space.CurrentScene() + "/target", "hello")

It should be possible to send an event to one object and have it relay to all items in a scene making interface type behaviors – todo test this theory

Custom events seem to run in the scope of the calling function shown by running System.Trace(System.ThisName()); or System.Trace(System.ThisOwner();.

It’s possible to pass Command script access to object nodes by passing things like Node and RsTime in the event arguments.

RsApp.SendCustomEvent(Space.CurrentScene() + "/target", {Node:Node, RsTime:RsTime})

Random Truespace Dev Notes

Timeline can drive scripts by adding keyframes to an object at start and end with values equal to the first and last frames. Watchdog is used to read the keyed values. Same can be done to read the modelspace timeline.

Bitmap Pixel Format

   BFMT_UNKNOWN    = 0;
   BFMT_A8R8G8B8    = 1;
   BFMT_A16B16G16R16    = 2;
   BFMT_A32fB32fG32fR32f    = 3;
   BFMT_R8    = 4;
   BFMT_R16    = 5;
   BFMT_R32f    = 6;
   BFMT_FORCE_DWORD    = -1;

Bitmap File Format

BFILEFMT_BMP	= 0,
BFILEFMT_JPG	= 1,
BFILEFMT_TGA	= 2,
BFILEFMT_PNG	= 3,
BFILEFMT_DDS	= 4,
BFILEFMT_DIB	= 6,
BFILEFMT_HDR	= 7,
BFILEFMT_PFM	= 8,
BFILEFMT_FORCE_DWORD	= 2147483647

bitmap format R8 may be invalid for exported images

popup panel – can get a panel frame by dragging an item out of the panel stack view. Set the RootNode connector value to the parent of the object that will be displayed. Set the Panel Node connector of the Panel Node to the node that will be floating. Copy into Windows Manager Space and run 2 mystery commands then set the panel aspect.

	WindowsManager.CloseWindow("Project/Windows Manager Space/TestPanelFrame")
	var rpf = Space.CurrentScene() + "/TestPanelFrame";

	Node.Value(rpf, "RootNode") = Space.CurrentScene();
	Node.Value(rpf + "/Panel Node", "PanelEditorNode") = Space.CurrentScene() + "/YafaRay IES Spot";
	Node.Copy(rpf, "/Project/Windows Manager Space");
	WindowsManager.Activate();
	WindowsManager.UpdateWindowsStructure();
	//UserInterface.SetAspect(Space.CurrentScene() + "/YafaRay IES Spot", 0);//icon
	//UserInterface.SetAspect(Space.CurrentScene() + "/YafaRay IES Spot", 1);//???
	//UserInterface.SetAspect(Space.CurrentScene() + "/YafaRay IES Spot", 2);//Exp
	//UserInterface.SetAspect(Space.CurrentScene() + "/YafaRay IES Spot", 3);//Default
	UserInterface.SetAspect(Space.CurrentScene() + "/YafaRay IES Spot", 4);//color

some file notes – appending writes to the end of a file. TristateTrue will write utf-8 files compatible with the xml files in trueSpace.

	var filename = params.ConValue('filename');
	var forReading = 1, forWriting = 2, forAppending = 8;
	var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0;
	var fso = new ActiveXObject("Scripting.FileSystemObject");
	var connToFile = file.OpenAsTextStream( forReading , TristateUseDefault);
var curLine = connToFile.ReadLine();
connToFile.Close();
fso.FileExists
fso.CopyFile(System.GetMainDir() + "\\Scripts\\reset.js", System.GetMainDir() + "\\Scripts\\resetORIGINAL.js", true);
	var f = fso.GetFile(System.GetMainDir() + "\\Scripts\\reset.js");
	var connToFile = f.OpenAsTextStream( forReading, TristateUseDefault );
	var filecontents = connToFile.ReadAll();
	connToFile.Close();
connToFile.WriteLine
fso.CopyFolder(YafaRay4tSv090 + '\\YafaRay4tS Setup', tS + 'Rs Main Libraries\\', true)
fso.DeleteFile

sample write utf8 xml file

function WriteRenderPath(renderLibrary)
{
	var forReading = 1, forWriting = 2, forAppending = 8;
	var TristateUseDefault = -2, TristateTrue = -1, TristateFalse = 0; 

	var fso = new ActiveXObject("Scripting.FileSystemObject");

	var f = fso.GetFile(System.GetMainDir() + "\\Scripts\\D3DView.RenderToFile.Settings.xml");

	if(!f) {
		System.Alert("d3dview render to file xml not found.");
		return;
	}

	lines = [];

	connToFile = f.OpenAsTextStream( forReading, TristateTrue );//unicode

	while (!connToFile.AtEndOfStream) {

		lines.push(connToFile.ReadLine());
	}

	connToFile.Close();

	connToFile = f.OpenAsTextStream( forWriting, TristateTrue );//unicode

	re = /FILENAME="[\w|\:|\\|\.|\s]+"/;

	for(var i=0;i<lines.length;i++) {
		curline = lines[i];
		//System.Trace(curline);
		matchArray = curline.match(re);

		if(matchArray) {
			var currentRenderFile = matchArray[0].split('"')[1];
			var basename = fso.GetBaseName(currentRenderFile);
			var ext = fso.GetExtensionName(currentRenderFile);
			curline = curline.replace(re,'FILENAME="' + renderLibrary + '\\' + basename + '.' + ext + '"')
			//System.Trace(curline);
		}
		connToFile.WriteLine(curline);
	}
	connToFile.Close();
}
RsFileIO.SaveObject(tS + '\\Scripts\\preobjects\\Yafaray Folder.rsobj', System.ThisOwner() + "/Yafaray Folder")
RsFileIO.LoadObject(tS + '\\Rs Main Libraries\\YafaRay4tS Setup\\YafaRay Camera.RsObj', "/Preferences/Cameras")
always close a window not delete it
WindowsManager.CloseWindow("/Project/Windows Manager Space/YafaRay Toolbar Frame")
after copy to windows manager space
		WindowsManager.Activate()
		WindowsManager.UpdateWindowsStructure()
open view in panel stack – 6 is the aspect
UserInterface.OpenToolPanelViewEx2("", "", Space.CurrentScene() + "/YafaRay4tS", 6, 1, 0);
Activity.Run - runs after the script returns
ScriptObject.Execute - stops current script while executing

keyframe range

keyframe range
	//RsTime.EvalAnimStart and RsTime.EvalAnimEnd seem to ignore all arguments
	// and only operate on the entire scene animation

//alters the play range anim pref values"
		try {
			animStart = RsTime.EvalAnimStart('');
			animEnd = RsTime.EvalAnimEnd('');
play range
	var playRangeStart = RsAnimPref.PlayRangeStart;
	var playRangeEnd = RsAnimPref.PlayRangeEnd;

timeline range
Node.Value("%THIS_NAME%" , "Start_Frame") = Node.Value("AnimMng", "AnimStart");
Node.Value("%THIS_NAME%", "Number_Start") = Node.Value("AnimMng", "AnimStart");
Node.Value("%THIS_NAME%", "End_Frame") = Node.Value("AnimMng", "AnimEnd");
status line
	if(Node.Exists("/Status Line")) {
		if(!Node.ConExists("Status Line", "Model"))
			Node.ConCreate("Status Line", "Model", "string", 4);
		Node.Value("/Status Line","Model") = "Rendering...";
	}
folder enumeration and reg exp matching
var folder = fso.GetFolder(tspath); 
	var folderEnum = new Enumerator(folder.SubFolders); 
 
	var regexp = /YafaRay/i

	for(;!folderEnum.atEnd(); folderEnum.moveNext()) {
		var cur = folderEnum.item();
		var curArray = (cur+"").split("\\");
		var s = "" + curArray[curArray.length-1];
		if(s.match(regexp)) {
			temppath = fso.BuildPath(cur,"bin");
			temppath = fso.BuildPath(temppath,"yafaray-xml.exe");
			if(fso.FileExists(temppath)) {
				yafpath = temppath;
				break;
			}
		}
	}
temp path
	var WshShell = new ActiveXObject("WScript.Shell");
    
	WshSysEnv = WshShell.Environment("PROCESS");
	temppath = WshSysEnv("TEMP");
time stamp
var theDate = new Date();
	var y = theDate.getYear();
	var mo = theDate.getMonth() + 1;
	var d = theDate.getDate();
	var h = theDate.getHours();
	var mi = theDate.getMinutes();
	var s = theDate.getSeconds();

	if(mo < 10) mo = "0" + mo;
	if(d < 10) d = "0" + d;
	if(h < 10) h = "0" + h;
	if(mi < 10) mi = "0" + mi;
	if(s < 10) s = "0" + s;

	var timestamp = "_" + y + mo + d + h + mi + s;
string enum
var recentPlaces = System.CreateDO("Common Data Package/String Enum Data");

	//try catch only way to check for uninitialized value
	try {
		recentPlaces = Node.Value(owner, "RecentPlaces");
	} catch(e) {
		recentPlaces.Clear();
		Node.Value(owner, "RecentPlaces") = recentPlaces;
	}

	if(recentPlaces.GetSize() == 0) return;

	thedata = recentPlaces.GetSelectedString();

	var newRecentPlaces = System.CreateDO("Common Data Package/String Enum Data");

	newRecentPlaces.SetStringAt(0,libraryPlace);
get frame rate
frameRateEnum = System.CreateDO('Common Data Package/String Enum Data');
	
	frameRateEnum = Node.Value("Preferences/AnimPref","Frame Rate");

	frameRateString = frameRateEnum.GetSelectedString();

	frameRate = 30;
	if(frameRateString=="15 fps")
		frameRate = 15;
	if(frameRateString=="Film 24 fps")
		frameRate = 24;
	if(frameRateString=="PAL 25 fps")
		frameRate = 25;
	if(frameRateString=="30 fps")
		frameRate = 30;
	if(frameRateString=="60 fps")
		frameRate = 60;
sel = Node.Selection(); //list of selected nodes
	numsel = Node.SelectionLength(sel); // number of selected nodes
	theParent = Node.SelectionGetAt(sel, numsel-1); //last selected item will be the parent
	var mybitmap = System.CreateDO("Common Data Package/Bitmap Data");

	BFMT_A8R8G8B8	= 1;

	mybitmap.Create(256,256,BFMT_A8R8G8B8);
	var color = System.CreateDO('Common Data Package/Color Data');
			color.SetColor(val,val,val,1);
			mybitmap.SetPixel(x+126,y+126,color);
if(!DebugViewFound("Project/Windows Manager Space"))
        CmdPrompt.DebugView('Windows Manager Space', 0);
}

function NodeSubObject(root, index)
{
    return root + "/" + Node.SubObject(root, index);
}

function DebugViewFound()
{
    var WMS = "Project/Windows Manager Space";
    var numwindows = Node.SubObjectCount(WMS);

    for(var winIndex=0; winIndex < numwindows; winIndex++)
    {
        var currentWindow = NodeSubObject(WMS, winIndex);
        if(Node.SubObjectCount(currentWindow) < 1)
            continue;

        var shortname = Node.ShortName(NodeSubObject(currentWindow, 0));
        if( shortname == "LogOutput")
            return true;
    }
    return false;
}

Widgets.Rotate == rotate about origin (0,0,0) then offset back to original position

Node.Selection() – returns a selection list with a semicolon delimination, but has leading spaces in each nodes path so needs to be adjusted to work with Space.Select

sel = Node.Selection();
selArray = sel.split(";");
re = /^\s/; // whitespace in first character position
sel = selArray[0].replace(re,"");
if(selArray.length > 1)
        for(i=1;i<selArray.length;i++)
                sel = sel + ";" + selArray[i].replace(re,"");

Node.Select – select one node

Space.Unselect()

%THIS_OWNER_NAME%

%THIS_BUTTON_ITEM% – enables a toolbar button to see it’s full path

LE. – (L E dot) undocumented LE commands

Activity.Run(scene + “/second”);//runs after the calling script is done

ScriptObject.Execute(scene + “/second”);//runs immediately pausing the calling script

If you add a new connector to a script used inside the Widgets it won’t work right away. Restarting truespace will unstick it.

//functions for generating random guid for marker display
function S4() {
return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
}
function guid() {
return (“{“ + S4()+S4()+“-“+S4()+“-“+S4()+“-“+S4()+“-“+S4()+S4()+S4() + “}”);
}
WshShell = new ActiveXObject(“WScript.Shell”);
WshShell.Run(“calc”);

matrix multiplication

   myMatrix.LoadIdentity();
   myMatrix.SetPitch(-90);// myMatrix = -90 deg pitch => last rotation
   myMatrix.Mult(rollMatrix);// rollMatrix (this is the second rotation) * myMatrix => myMatrix
   myMatrix.Mult(yawMatrix);// yawMatrix (this is the first rotation) * myMatrix => myMatrix
   final result in myMatrix is yaw then roll then pitch
Local matrix from world matrices
theMatrix = Node.Value(bindgroup + "/" + subNode, "WldMatrix");
parentMatrix = Node.Value(bindParent, "WldMatrix");
parInvert = parentMatrix.Invert();
theMatrix.Mult(parInvert);
//now theMatrix contains local instead of world

Space.Select() – select multiple nodes from text string separated by semi colons, node paths in the list must not have leading spaces

sel = Node.Selection();
selArray = sel.split(";");
re = /^\s/; // whitespace in first character position
sel = selArray[0].replace(re,"");
if(selArray.length > 1)
        for(i=1;i<selArray.length;i++)
                sel = sel + ";" + selArray[i].replace(re,"");
Node.Select – select one node
Space.Unselect()
	var strEnum = System.CreateDO("Common Data Package/String Enum Data");
	try {
		strEnum = Node.Value("/Offline renderers", "Renderer");
	} catch(e) {
		strEnum.Clear();
	}
	var selString = strEnum.GetSelectedString();
	System.Alert(selString);

Node.Selection() – returns a selection list with a semicolon delimination, but has leading spaces in each nodes path so needs to be adjusted to work with Space.Select

Node.LookupParentValue