mirror of
https://github.com/barsdeveloper/ueblueprint.git
synced 2026-02-04 08:50:33 +08:00
Niagara and Metasound nodes WIP
* Keep track of entities * Fix renaming * Niagara variables wip * Several niagara decode and test * Move nodeTemplate code to dedicated file, self node added * Move node decoding functions to dedicated files * Move pin decoding logic to dedicated files * Accept space separated keys in objects * Build * Prevent a crash in case of incomplete object * Avoid creating objects unnecessarily * types formatting * Initial metasound style * Common pcg nodes colors * Fix string serialization * Metasound new styles and fixes * More metasound styles and colors * WIP * Several fixes * More tests and fixes * Clean gitignore
This commit is contained in:
81
js/decoding/nodeColor.js
Normal file
81
js/decoding/nodeColor.js
Normal file
@@ -0,0 +1,81 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import LinearColorEntity from "../entity/LinearColorEntity.js"
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeColor(entity) {
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.materialExpressionConstant2Vector:
|
||||
case Configuration.paths.materialExpressionConstant3Vector:
|
||||
case Configuration.paths.materialExpressionConstant4Vector:
|
||||
return Configuration.nodeColors.yellow
|
||||
case Configuration.paths.makeStruct:
|
||||
return Configuration.nodeColors.darkBlue
|
||||
case Configuration.paths.materialExpressionMaterialFunctionCall:
|
||||
return Configuration.nodeColors.blue
|
||||
case Configuration.paths.materialExpressionFunctionInput:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.materialExpressionTextureSample:
|
||||
return Configuration.nodeColors.darkTurquoise
|
||||
case Configuration.paths.materialExpressionTextureCoordinate:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.pcgEditorGraphNodeInput:
|
||||
case Configuration.paths.pcgEditorGraphNodeOutput:
|
||||
return Configuration.nodeColors.red
|
||||
}
|
||||
switch (entity.getClass()) {
|
||||
case Configuration.paths.callFunction:
|
||||
return entity.bIsPureFunc
|
||||
? Configuration.nodeColors.green
|
||||
: Configuration.nodeColors.blue
|
||||
case Configuration.paths.niagaraNodeFunctionCall:
|
||||
return Configuration.nodeColors.darkerBlue
|
||||
case Configuration.paths.dynamicCast:
|
||||
return Configuration.nodeColors.turquoise
|
||||
case Configuration.paths.inputDebugKey:
|
||||
case Configuration.paths.inputKey:
|
||||
return Configuration.nodeColors.red
|
||||
case Configuration.paths.createDelegate:
|
||||
case Configuration.paths.enumLiteral:
|
||||
case Configuration.paths.makeArray:
|
||||
case Configuration.paths.makeMap:
|
||||
case Configuration.paths.materialGraphNode:
|
||||
case Configuration.paths.select:
|
||||
return Configuration.nodeColors.green
|
||||
case Configuration.paths.executionSequence:
|
||||
case Configuration.paths.ifThenElse:
|
||||
case Configuration.paths.macro:
|
||||
case Configuration.paths.multiGate:
|
||||
return Configuration.nodeColors.gray
|
||||
case Configuration.paths.functionEntry:
|
||||
case Configuration.paths.functionResult:
|
||||
return Configuration.nodeColors.violet
|
||||
case Configuration.paths.timeline:
|
||||
return Configuration.nodeColors.yellow
|
||||
}
|
||||
if (entity.switchTarget()) {
|
||||
return Configuration.nodeColors.lime
|
||||
}
|
||||
if (entity.isEvent()) {
|
||||
return Configuration.nodeColors.red
|
||||
}
|
||||
if (entity.isComment()) {
|
||||
return (entity.CommentColor ? entity.CommentColor : LinearColorEntity.getWhite())
|
||||
.toDimmedColor()
|
||||
.toCSSRGBValues()
|
||||
}
|
||||
const pcgSubobject = entity.getPcgSubobject()
|
||||
if (pcgSubobject) {
|
||||
if (pcgSubobject.NodeTitleColor) {
|
||||
return pcgSubobject.NodeTitleColor.toDimmedColor(0.1).toCSSRGBValues()
|
||||
}
|
||||
switch (entity.PCGNode?.getName(true)) {
|
||||
case "Branch":
|
||||
case "Select":
|
||||
return Configuration.nodeColors.intenseGreen
|
||||
}
|
||||
}
|
||||
if (entity.bIsPureFunc) {
|
||||
return Configuration.nodeColors.green
|
||||
}
|
||||
return Configuration.nodeColors.blue
|
||||
}
|
||||
78
js/decoding/nodeIcon.js
Normal file
78
js/decoding/nodeIcon.js
Normal file
@@ -0,0 +1,78 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import SVGIcon from "../SVGIcon.js"
|
||||
import nodeTitle from "./nodeTitle.js"
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeIcon(entity) {
|
||||
if (entity.isMaterial() || entity.isPcg() || entity.isNiagara()) {
|
||||
return null
|
||||
}
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.addDelegate:
|
||||
case Configuration.paths.asyncAction:
|
||||
case Configuration.paths.callDelegate:
|
||||
case Configuration.paths.createDelegate:
|
||||
case Configuration.paths.functionEntry:
|
||||
case Configuration.paths.functionResult:
|
||||
return SVGIcon.node
|
||||
case Configuration.paths.customEvent: return SVGIcon.event
|
||||
case Configuration.paths.doN: return SVGIcon.doN
|
||||
case Configuration.paths.doOnce: return SVGIcon.doOnce
|
||||
case Configuration.paths.dynamicCast: return SVGIcon.cast
|
||||
case Configuration.paths.enumLiteral: return SVGIcon.enum
|
||||
case Configuration.paths.event: return SVGIcon.event
|
||||
case Configuration.paths.executionSequence:
|
||||
case Configuration.paths.multiGate:
|
||||
return SVGIcon.sequence
|
||||
case Configuration.paths.flipflop:
|
||||
return SVGIcon.flipflop
|
||||
case Configuration.paths.forEachElementInEnum:
|
||||
case Configuration.paths.forLoop:
|
||||
case Configuration.paths.forLoopWithBreak:
|
||||
case Configuration.paths.whileLoop:
|
||||
return SVGIcon.loop
|
||||
case Configuration.paths.forEachLoop:
|
||||
case Configuration.paths.forEachLoopWithBreak:
|
||||
return SVGIcon.forEachLoop
|
||||
case Configuration.paths.ifThenElse: return SVGIcon.branchNode
|
||||
case Configuration.paths.isValid: return SVGIcon.questionMark
|
||||
case Configuration.paths.makeArray: return SVGIcon.makeArray
|
||||
case Configuration.paths.makeMap: return SVGIcon.makeMap
|
||||
case Configuration.paths.makeSet: return SVGIcon.makeSet
|
||||
case Configuration.paths.makeStruct: return SVGIcon.makeStruct
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode: return SVGIcon.metasoundFunction
|
||||
case Configuration.paths.select: return SVGIcon.select
|
||||
case Configuration.paths.spawnActorFromClass: return SVGIcon.spawnActor
|
||||
case Configuration.paths.timeline: return SVGIcon.timer
|
||||
}
|
||||
if (entity.switchTarget()) {
|
||||
return SVGIcon.switch
|
||||
}
|
||||
if (nodeTitle(entity).startsWith("Break")) {
|
||||
return SVGIcon.breakStruct
|
||||
}
|
||||
if (entity.getClass() === Configuration.paths.macro) {
|
||||
return SVGIcon.macro
|
||||
}
|
||||
const hidValue = entity.getHIDAttribute()?.toString()
|
||||
if (hidValue) {
|
||||
if (hidValue.includes("Mouse")) {
|
||||
return SVGIcon.mouse
|
||||
} else if (hidValue.includes("Gamepad_Special")) {
|
||||
return SVGIcon.keyboard // It is called Touchpad in UE
|
||||
} else if (hidValue.includes("Gamepad") || hidValue.includes("Steam")) {
|
||||
return SVGIcon.gamepad
|
||||
} else if (hidValue.includes("Touch")) {
|
||||
return SVGIcon.touchpad
|
||||
} else {
|
||||
return SVGIcon.keyboard
|
||||
}
|
||||
}
|
||||
if (entity.getDelegatePin()) {
|
||||
return SVGIcon.event
|
||||
}
|
||||
if (entity.ObjectRef?.type === Configuration.paths.ambientSound) {
|
||||
return SVGIcon.sound
|
||||
}
|
||||
return SVGIcon.functionSymbol
|
||||
}
|
||||
123
js/decoding/nodeTemplate.js
Normal file
123
js/decoding/nodeTemplate.js
Normal file
@@ -0,0 +1,123 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import CommentNodeTemplate from "../template/node/CommentNodeTemplate.js"
|
||||
import EventNodeTemplate from "../template/node/EventNodeTemplate.js"
|
||||
import KnotNodeTemplate from "../template/node/KnotNodeTemplate.js"
|
||||
import MetasoundNodeTemplate from "../template/node/MetasoundNodeTemplate.js"
|
||||
import MetasoundOperationTemplate from "../template/node/MetasoundOperationTemplate.js"
|
||||
import NodeTemplate from "../template/node/NodeTemplate.js"
|
||||
import VariableAccessNodeTemplate from "../template/node/VariableAccessNodeTemplate.js"
|
||||
import VariableConversionNodeTemplate from "../template/node/VariableConversionNodeTemplate.js"
|
||||
import VariableOperationNodeTemplate from "../template/node/VariableOperationNodeTemplate.js"
|
||||
|
||||
/**
|
||||
* @param {ObjectEntity} nodeEntity
|
||||
* @return {new () => NodeTemplate}
|
||||
*/
|
||||
export default function nodeTemplateClass(nodeEntity) {
|
||||
if (
|
||||
nodeEntity.getClass() === Configuration.paths.callFunction
|
||||
|| nodeEntity.getClass() === Configuration.paths.commutativeAssociativeBinaryOperator
|
||||
|| nodeEntity.getClass() === Configuration.paths.callArrayFunction
|
||||
) {
|
||||
const memberParent = nodeEntity.FunctionReference?.MemberParent?.path ?? ""
|
||||
const memberName = nodeEntity.FunctionReference?.MemberName
|
||||
if (
|
||||
memberName && (
|
||||
memberParent === Configuration.paths.kismetMathLibrary
|
||||
|| memberParent === Configuration.paths.kismetArrayLibrary
|
||||
)) {
|
||||
if (memberName.startsWith("Conv_")) {
|
||||
return VariableConversionNodeTemplate
|
||||
}
|
||||
if (
|
||||
memberName.startsWith("Add_")
|
||||
|| memberName.startsWith("And_")
|
||||
|| memberName.startsWith("Boolean") // Boolean logic operations
|
||||
|| memberName.startsWith("Cross_")
|
||||
|| memberName.startsWith("Dot_")
|
||||
|| memberName.startsWith("Not_")
|
||||
|| memberName.startsWith("Or_")
|
||||
|| memberName.startsWith("Percent_")
|
||||
|| memberName.startsWith("Xor_")
|
||||
) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
switch (memberName) {
|
||||
case "Abs":
|
||||
case "Array_Add":
|
||||
case "Array_AddUnique":
|
||||
case "Array_Identical":
|
||||
case "BMax":
|
||||
case "BMin":
|
||||
case "CrossProduct2D":
|
||||
case "DotProduct2D":
|
||||
case "Exp":
|
||||
case "FMax":
|
||||
case "FMin":
|
||||
case "GetPI":
|
||||
case "Max":
|
||||
case "MaxInt64":
|
||||
case "Min":
|
||||
case "MinInt64":
|
||||
case "Sqrt":
|
||||
case "Square":
|
||||
case "Vector4_CrossProduct3":
|
||||
case "Vector4_DotProduct":
|
||||
case "Vector4_DotProduct3":
|
||||
// Trigonometry
|
||||
case "Acos":
|
||||
case "Asin":
|
||||
case "Cos":
|
||||
case "DegAcos":
|
||||
case "DegCos":
|
||||
case "DegSin":
|
||||
case "DegTan":
|
||||
case "Sin":
|
||||
case "Tan":
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
}
|
||||
if (memberParent === Configuration.paths.blueprintSetLibrary) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
if (memberParent === Configuration.paths.blueprintMapLibrary) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
}
|
||||
switch (nodeEntity.getClass()) {
|
||||
case Configuration.paths.comment:
|
||||
case Configuration.paths.materialGraphNodeComment:
|
||||
return CommentNodeTemplate
|
||||
case Configuration.paths.createDelegate:
|
||||
return NodeTemplate
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode:
|
||||
if (nodeEntity["ClassName"]?.["Name"] == "Add") {
|
||||
return MetasoundOperationTemplate
|
||||
}
|
||||
return MetasoundNodeTemplate
|
||||
case Configuration.paths.niagaraNodeOp:
|
||||
if ([
|
||||
"Boolean::LogicEq",
|
||||
"Boolean::LogicNEq",
|
||||
"Numeric::Abs",
|
||||
"Numeric::Add",
|
||||
"Numeric::Mul",
|
||||
].includes(nodeEntity.OpName)) {
|
||||
return VariableOperationNodeTemplate
|
||||
}
|
||||
break
|
||||
case Configuration.paths.promotableOperator:
|
||||
return VariableOperationNodeTemplate
|
||||
case Configuration.paths.knot:
|
||||
return KnotNodeTemplate
|
||||
case Configuration.paths.literal:
|
||||
case Configuration.paths.self:
|
||||
case Configuration.paths.variableGet:
|
||||
case Configuration.paths.variableSet:
|
||||
return VariableAccessNodeTemplate
|
||||
}
|
||||
if (nodeEntity.isEvent()) {
|
||||
return EventNodeTemplate
|
||||
}
|
||||
return NodeTemplate
|
||||
}
|
||||
404
js/decoding/nodeTitle.js
Normal file
404
js/decoding/nodeTitle.js
Normal file
@@ -0,0 +1,404 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import Utility from "../Utility.js"
|
||||
|
||||
const sequencerScriptingNameRegex = /\/Script\/SequencerScripting\.MovieSceneScripting(.+)Channel/
|
||||
const keyNameValue = {
|
||||
"A_AccentGrave": "à",
|
||||
"Add": "Num +",
|
||||
"C_Cedille": "ç",
|
||||
"Decimal": "Num .",
|
||||
"Divide": "Num /",
|
||||
"E_AccentAigu": "é",
|
||||
"E_AccentGrave": "è",
|
||||
"F1": "F1", // Otherwise F and number will be separated
|
||||
"F10": "F10",
|
||||
"F11": "F11",
|
||||
"F12": "F12",
|
||||
"F2": "F2",
|
||||
"F3": "F3",
|
||||
"F4": "F4",
|
||||
"F5": "F5",
|
||||
"F6": "F6",
|
||||
"F7": "F7",
|
||||
"F8": "F8",
|
||||
"F9": "F9",
|
||||
"Gamepad_Special_Left_X": "Touchpad Button X Axis",
|
||||
"Gamepad_Special_Left_Y": "Touchpad Button Y Axis",
|
||||
"Mouse2D": "Mouse XY 2D-Axis",
|
||||
"Multiply": "Num *",
|
||||
"Section": "§",
|
||||
"Subtract": "Num -",
|
||||
"Tilde": "`",
|
||||
}
|
||||
|
||||
function keyName(value) {
|
||||
/** @type {String} */
|
||||
let result = keyNameValue[value]
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
result = Utility.numberFromText(value)?.toString()
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
const match = value.match(/NumPad([a-zA-Z]+)/)
|
||||
if (match) {
|
||||
result = Utility.numberFromText(match[1]).toString()
|
||||
if (result) {
|
||||
return "Num " + result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeTitle(entity) {
|
||||
let input
|
||||
switch (entity.getType()) {
|
||||
case Configuration.paths.asyncAction:
|
||||
if (entity.ProxyFactoryFunctionName) {
|
||||
return Utility.formatStringName(entity.ProxyFactoryFunctionName)
|
||||
}
|
||||
case Configuration.paths.actorBoundEvent:
|
||||
case Configuration.paths.componentBoundEvent:
|
||||
return `${Utility.formatStringName(entity.DelegatePropertyName)} (${entity.ComponentPropertyName ?? "Unknown"})`
|
||||
case Configuration.paths.callDelegate:
|
||||
return `Call ${entity.DelegateReference?.MemberName ?? "None"}`
|
||||
case Configuration.paths.createDelegate:
|
||||
return "Create Event"
|
||||
case Configuration.paths.customEvent:
|
||||
if (entity.CustomFunctionName) {
|
||||
return entity.CustomFunctionName
|
||||
}
|
||||
case Configuration.paths.dynamicCast:
|
||||
if (!entity.TargetType) {
|
||||
return "Bad cast node" // Target type not found
|
||||
}
|
||||
return `Cast To ${entity.TargetType?.getName()}`
|
||||
case Configuration.paths.enumLiteral:
|
||||
return `Literal enum ${entity.Enum?.getName()}`
|
||||
case Configuration.paths.event:
|
||||
return `Event ${(entity.EventReference?.MemberName ?? "").replace(/^Receive/, "")}`
|
||||
case Configuration.paths.executionSequence:
|
||||
return "Sequence"
|
||||
case Configuration.paths.forEachElementInEnum:
|
||||
return `For Each ${entity.Enum?.getName()}`
|
||||
case Configuration.paths.forEachLoopWithBreak:
|
||||
return "For Each Loop with Break"
|
||||
case Configuration.paths.functionEntry:
|
||||
return entity.FunctionReference?.MemberName === "UserConstructionScript"
|
||||
? "Construction Script"
|
||||
: entity.FunctionReference?.MemberName
|
||||
case Configuration.paths.functionResult:
|
||||
return "Return Node"
|
||||
case Configuration.paths.ifThenElse:
|
||||
return "Branch"
|
||||
case Configuration.paths.makeStruct:
|
||||
if (entity.StructType) {
|
||||
return `Make ${entity.StructType.getName()}`
|
||||
}
|
||||
case Configuration.paths.materialExpressionComponentMask: {
|
||||
const materialObject = entity.getMaterialSubobject()
|
||||
return `Mask ( ${Configuration.rgba
|
||||
.filter(k => /** @type {MirroredEntity<any>} */(materialObject[k]).get() === true)
|
||||
.map(v => v + " ")
|
||||
.join("")})`
|
||||
}
|
||||
case Configuration.paths.materialExpressionConstant:
|
||||
input ??= [entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Value")?.DefaultValue]
|
||||
case Configuration.paths.materialExpressionConstant2Vector:
|
||||
input ??= [
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "X")?.DefaultValue,
|
||||
entity.getCustomproperties().find(pinEntity => pinEntity.PinName == "Y")?.DefaultValue,
|
||||
]
|
||||
case Configuration.paths.materialExpressionConstant3Vector:
|
||||
if (!input) {
|
||||
/** @type {VectorEntity} */
|
||||
const vector = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "Constant")
|
||||
?.DefaultValue
|
||||
input = [vector.X, vector.Y, vector.Z]
|
||||
}
|
||||
case Configuration.paths.materialExpressionConstant4Vector:
|
||||
if (!input) {
|
||||
/** @type {LinearColorEntity} */
|
||||
const vector = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "Constant")
|
||||
?.DefaultValue
|
||||
input = [vector.R, vector.G, vector.B, vector.A].map(v => v.valueOf())
|
||||
}
|
||||
if (input.length > 0) {
|
||||
return input.map(v => Utility.printExponential(v)).reduce((acc, cur) => acc + "," + cur)
|
||||
}
|
||||
break
|
||||
case Configuration.paths.materialExpressionFunctionInput: {
|
||||
const materialObject = entity.getMaterialSubobject()
|
||||
const inputName = materialObject?.InputName ?? "In"
|
||||
const inputType = materialObject?.InputType?.value.match(/^.+?_(\w+)$/)?.[1] ?? "Vector3"
|
||||
return `Input ${inputName} (${inputType})`
|
||||
}
|
||||
case Configuration.paths.materialExpressionLogarithm:
|
||||
return "Ln"
|
||||
case Configuration.paths.materialExpressionLogarithm10:
|
||||
return "Log10"
|
||||
case Configuration.paths.materialExpressionLogarithm2:
|
||||
return "Log2"
|
||||
case Configuration.paths.materialExpressionMaterialFunctionCall:
|
||||
const materialFunction = entity.getMaterialSubobject()?.MaterialFunction
|
||||
if (materialFunction) {
|
||||
return materialFunction.getName()
|
||||
}
|
||||
break
|
||||
case Configuration.paths.materialExpressionSquareRoot:
|
||||
return "Sqrt"
|
||||
case Configuration.paths.metasoundEditorGraphExternalNode: {
|
||||
const name = entity["ClassName"]?.["Name"]
|
||||
if (name) {
|
||||
switch (name) {
|
||||
case "Add": return "+"
|
||||
default: return name
|
||||
}
|
||||
}
|
||||
}
|
||||
case Configuration.paths.pcgEditorGraphNodeInput:
|
||||
return "Input"
|
||||
case Configuration.paths.pcgEditorGraphNodeOutput:
|
||||
return "Output"
|
||||
case Configuration.paths.spawnActorFromClass:
|
||||
let className = entity.getCustomproperties()
|
||||
.find(pinEntity => pinEntity.PinName == "ReturnValue")
|
||||
?.PinType
|
||||
?.PinSubCategoryObject
|
||||
?.getName()
|
||||
if (className === "Actor") {
|
||||
className = null
|
||||
}
|
||||
return `SpawnActor ${Utility.formatStringName(className ?? "NONE")}`
|
||||
case Configuration.paths.switchEnum:
|
||||
return `Switch on ${entity.Enum?.getName() ?? "Enum"}`
|
||||
case Configuration.paths.switchInteger:
|
||||
return `Switch on Int`
|
||||
case Configuration.paths.variableGet:
|
||||
return ""
|
||||
case Configuration.paths.variableSet:
|
||||
return "SET"
|
||||
}
|
||||
let switchTarget = entity.switchTarget()
|
||||
if (switchTarget) {
|
||||
if (switchTarget[0] !== "E") {
|
||||
switchTarget = Utility.formatStringName(switchTarget)
|
||||
}
|
||||
return `Switch on ${switchTarget}`
|
||||
}
|
||||
if (entity.isComment()) {
|
||||
return entity.NodeComment
|
||||
}
|
||||
const keyNameSymbol = entity.getHIDAttribute()
|
||||
if (keyNameSymbol) {
|
||||
const name = keyNameSymbol.toString()
|
||||
let title = keyName(name) ?? Utility.formatStringName(name)
|
||||
if (entity.getClass() === Configuration.paths.inputDebugKey) {
|
||||
title = "Debug Key " + title
|
||||
} else if (entity.getClass() === Configuration.paths.getInputAxisKeyValue) {
|
||||
title = "Get " + title
|
||||
}
|
||||
return title
|
||||
}
|
||||
if (entity.getClass() === Configuration.paths.macro) {
|
||||
return Utility.formatStringName(entity.MacroGraphReference?.getMacroName())
|
||||
}
|
||||
if (entity.isMaterial() && entity.getMaterialSubobject()) {
|
||||
let result = nodeTitle(entity.getMaterialSubobject())
|
||||
result = result.match(/Material Expression (.+)/)?.[1] ?? result
|
||||
return result
|
||||
}
|
||||
if (entity.isPcg() && entity.getPcgSubobject()) {
|
||||
let pcgSubobject = entity.getPcgSubobject()
|
||||
let result = pcgSubobject.NodeTitle ? pcgSubobject.NodeTitle : nodeTitle(pcgSubobject)
|
||||
return result
|
||||
}
|
||||
const subgraphObject = entity.getSubgraphObject()
|
||||
if (subgraphObject) {
|
||||
return subgraphObject.Graph.getName()
|
||||
}
|
||||
const settingsObject = entity.getSettingsObject()
|
||||
if (settingsObject) {
|
||||
if (settingsObject.ExportPath.type === Configuration.paths.pcgHiGenGridSizeSettings) {
|
||||
return `Grid Size: ${(
|
||||
settingsObject.HiGenGridSize?.toString().match(/\d+/)?.[0]?.concat("00")
|
||||
?? settingsObject.HiGenGridSize?.toString().match(/^\w+$/)?.[0]
|
||||
) ?? "256"}`
|
||||
}
|
||||
if (settingsObject.BlueprintElementInstance) {
|
||||
return Utility.formatStringName(settingsObject.BlueprintElementType.getName())
|
||||
}
|
||||
if (settingsObject.Operation) {
|
||||
const match = settingsObject.Name.match(/PCGMetadata(\w+)Settings_\d+/)
|
||||
if (match) {
|
||||
return Utility.formatStringName(match[1] + ": " + settingsObject.Operation)
|
||||
}
|
||||
}
|
||||
const settingsSubgraphObject = settingsObject.getSubgraphObject()
|
||||
if (settingsSubgraphObject && settingsSubgraphObject.Graph) {
|
||||
return settingsSubgraphObject.Graph.getName()
|
||||
}
|
||||
}
|
||||
let memberName = entity.FunctionReference?.MemberName
|
||||
if (memberName) {
|
||||
const memberParent = entity.FunctionReference.MemberParent?.path ?? ""
|
||||
switch (memberName) {
|
||||
case "AddKey":
|
||||
let result = memberParent.match(sequencerScriptingNameRegex)
|
||||
if (result) {
|
||||
return `Add Key (${Utility.formatStringName(result[1])})`
|
||||
}
|
||||
case "Concat_StrStr":
|
||||
return "Append"
|
||||
}
|
||||
const memberNameTraceLineMatch = memberName.match(Configuration.lineTracePattern)
|
||||
if (memberNameTraceLineMatch) {
|
||||
return "Line Trace"
|
||||
+ (memberNameTraceLineMatch[1] === "Multi" ? " Multi " : " ")
|
||||
+ (memberNameTraceLineMatch[2] === ""
|
||||
? "By Channel"
|
||||
: Utility.formatStringName(memberNameTraceLineMatch[2])
|
||||
)
|
||||
}
|
||||
switch (memberParent) {
|
||||
case Configuration.paths.blueprintGameplayTagLibrary:
|
||||
case Configuration.paths.kismetMathLibrary:
|
||||
case Configuration.paths.slateBlueprintLibrary:
|
||||
case Configuration.paths.timeManagementBlueprintLibrary:
|
||||
const leadingLetter = memberName.match(/[BF]([A-Z]\w+)/)
|
||||
if (leadingLetter) {
|
||||
// Some functions start with B or F (Like FCeil, FMax, BMin)
|
||||
memberName = leadingLetter[1]
|
||||
}
|
||||
switch (memberName) {
|
||||
case "Abs": return "ABS"
|
||||
case "BooleanAND": return "AND"
|
||||
case "BooleanNAND": return "NAND"
|
||||
case "BooleanOR": return "OR"
|
||||
case "Exp": return "e"
|
||||
case "LineTraceSingle": return "Line Trace By Channel"
|
||||
case "Max": return "MAX"
|
||||
case "MaxInt64": return "MAX"
|
||||
case "Min": return "MIN"
|
||||
case "MinInt64": return "MIN"
|
||||
case "Not_PreBool": return "NOT"
|
||||
case "Sin": return "SIN"
|
||||
case "Sqrt": return "SQRT"
|
||||
case "Square": return "^2"
|
||||
// Dot products not respecting MemberName pattern
|
||||
case "CrossProduct2D": return "cross"
|
||||
case "Vector4_CrossProduct3": return "cross3"
|
||||
case "DotProduct2D":
|
||||
case "Vector4_DotProduct":
|
||||
return "dot"
|
||||
case "Vector4_DotProduct3": return "dot3"
|
||||
}
|
||||
if (memberName.startsWith("Add_")) {
|
||||
return "+"
|
||||
}
|
||||
if (memberName.startsWith("And_")) {
|
||||
return "&"
|
||||
}
|
||||
if (memberName.startsWith("Conv_")) {
|
||||
return "" // Conversion nodes do not have visible names
|
||||
}
|
||||
if (memberName.startsWith("Cross_")) {
|
||||
return "cross"
|
||||
}
|
||||
if (memberName.startsWith("Divide_")) {
|
||||
return String.fromCharCode(0x00f7)
|
||||
}
|
||||
if (memberName.startsWith("Dot_")) {
|
||||
return "dot"
|
||||
}
|
||||
if (memberName.startsWith("EqualEqual_")) {
|
||||
return "=="
|
||||
}
|
||||
if (memberName.startsWith("Greater_")) {
|
||||
return ">"
|
||||
}
|
||||
if (memberName.startsWith("GreaterEqual_")) {
|
||||
return ">="
|
||||
}
|
||||
if (memberName.startsWith("Less_")) {
|
||||
return "<"
|
||||
}
|
||||
if (memberName.startsWith("LessEqual_")) {
|
||||
return "<="
|
||||
}
|
||||
if (memberName.startsWith("Multiply_")) {
|
||||
return String.fromCharCode(0x2a2f)
|
||||
}
|
||||
if (memberName.startsWith("Not_")) {
|
||||
return "~"
|
||||
}
|
||||
if (memberName.startsWith("NotEqual_")) {
|
||||
return "!="
|
||||
}
|
||||
if (memberName.startsWith("Or_")) {
|
||||
return "|"
|
||||
}
|
||||
if (memberName.startsWith("Percent_")) {
|
||||
return "%"
|
||||
}
|
||||
if (memberName.startsWith("Subtract_")) {
|
||||
return "-"
|
||||
}
|
||||
if (memberName.startsWith("Xor_")) {
|
||||
return "^"
|
||||
}
|
||||
break
|
||||
case Configuration.paths.blueprintSetLibrary:
|
||||
{
|
||||
const setOperationMatch = memberName.match(/Set_(\w+)/)
|
||||
if (setOperationMatch) {
|
||||
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
|
||||
}
|
||||
}
|
||||
break
|
||||
case Configuration.paths.blueprintMapLibrary:
|
||||
{
|
||||
const setOperationMatch = memberName.match(/Map_(\w+)/)
|
||||
if (setOperationMatch) {
|
||||
return Utility.formatStringName(setOperationMatch[1]).toUpperCase()
|
||||
}
|
||||
}
|
||||
break
|
||||
case Configuration.paths.kismetArrayLibrary:
|
||||
{
|
||||
const arrayOperationMath = memberName.match(/Array_(\w+)/)
|
||||
if (arrayOperationMath) {
|
||||
return arrayOperationMath[1].toUpperCase()
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
return Utility.formatStringName(memberName)
|
||||
}
|
||||
if (entity.OpName) {
|
||||
switch (entity.OpName) {
|
||||
case "Boolean::LogicAnd": return "Logic AND"
|
||||
case "Boolean::LogicEq": return "=="
|
||||
case "Boolean::LogicNEq": return "!="
|
||||
case "Boolean::LogicNot": return "Logic NOT"
|
||||
case "Boolean::LogicOr": return "Logic OR"
|
||||
case "Matrix::MatrixMultiply": return "Multiply (Matrix * Matrix)"
|
||||
case "Matrix::MatrixVectorMultiply": return "Multiply (Matrix * Vector4)"
|
||||
case "Numeric::Abs": return "Abs"
|
||||
case "Numeric::Add": return "+"
|
||||
case "Numeric::DistancePos": return "Distance"
|
||||
case "Numeric::Mul": return String.fromCharCode(0x2a2f)
|
||||
}
|
||||
return Utility.formatStringName(entity.OpName).replaceAll("::", " ")
|
||||
}
|
||||
if (entity.FunctionDisplayName) {
|
||||
return Utility.formatStringName(entity.FunctionDisplayName)
|
||||
}
|
||||
if (entity.ObjectRef) {
|
||||
return entity.ObjectRef.getName()
|
||||
}
|
||||
return Utility.formatStringName(entity.getNameAndCounter()[0])
|
||||
}
|
||||
148
js/decoding/nodeVariadic.js
Normal file
148
js/decoding/nodeVariadic.js
Normal file
@@ -0,0 +1,148 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import GuidEntity from "../entity/GuidEntity.js"
|
||||
import PinEntity from "../entity/PinEntity.js"
|
||||
|
||||
/** @param {PinEntity} pinEntity */
|
||||
const indexFromUpperCaseLetterName = pinEntity =>
|
||||
pinEntity.PinName.match(/^\s*([A-Z])\s*$/)?.[1]?.charCodeAt(0) - "A".charCodeAt(0)
|
||||
|
||||
/** @param {ObjectEntity} entity */
|
||||
export default function nodeVariadic(entity) {
|
||||
/** @type {() => PinEntity[]} */
|
||||
let pinEntities
|
||||
/** @type {(pinEntity: PinEntity) => Number} */
|
||||
let pinIndexFromEntity
|
||||
/** @type {(newPinIndex: Number, minIndex: Number, maxIndex: Number, newPin: PinEntity) => String} */
|
||||
let pinNameFromIndex
|
||||
const type = entity.getType()
|
||||
let name
|
||||
switch (type) {
|
||||
case Configuration.paths.commutativeAssociativeBinaryOperator:
|
||||
case Configuration.paths.promotableOperator:
|
||||
name = entity.FunctionReference?.MemberName
|
||||
switch (name) {
|
||||
default:
|
||||
if (
|
||||
!name?.startsWith("Add_")
|
||||
&& !name?.startsWith("Subtract_")
|
||||
&& !name?.startsWith("Multiply_")
|
||||
&& !name?.startsWith("Divide_")
|
||||
) {
|
||||
break
|
||||
}
|
||||
case "And_Int64Int64":
|
||||
case "And_IntInt":
|
||||
case "BMax":
|
||||
case "BMin":
|
||||
case "BooleanAND":
|
||||
case "BooleanNAND":
|
||||
case "BooleanOR":
|
||||
case "Concat_StrStr":
|
||||
case "FMax":
|
||||
case "FMin":
|
||||
case "Max":
|
||||
case "MaxInt64":
|
||||
case "Min":
|
||||
case "MinInt64":
|
||||
case "Or_Int64Int64":
|
||||
case "Or_IntInt":
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput())
|
||||
pinIndexFromEntity ??= indexFromUpperCaseLetterName
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1) => {
|
||||
const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
|
||||
entity.NumAdditionalInputs = pinEntities().length - 1
|
||||
return result
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
case Configuration.paths.multiGate:
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Out[_\s]+(\d+)\s*$/i)?.[1])
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) =>
|
||||
`Out ${index >= 0 ? index : min > 0 ? "Out 0" : max + 1}`
|
||||
break
|
||||
// case Configuration.paths.niagaraNodeOp:
|
||||
// pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isInput())
|
||||
// pinIndexFromEntity ??= indexFromUpperCaseLetterName
|
||||
// pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
// const result = String.fromCharCode(index >= 0 ? index : max + "A".charCodeAt(0) + 1)
|
||||
// entity.AddedPins ??= []
|
||||
// entity.AddedPins.push(newPin)
|
||||
// return result
|
||||
// }
|
||||
// break
|
||||
case Configuration.paths.switchInteger:
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*(\d+)\s*$/)?.[1])
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => (index < 0 ? max + 1 : index).toString()
|
||||
break
|
||||
case Configuration.paths.switchGameplayTag:
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
|
||||
entity.PinNames ??= []
|
||||
entity.PinNames.push(result)
|
||||
delete entity.PinTags[entity.PinTags.length - 1]
|
||||
entity.PinTags[entity.PinTags.length] = null
|
||||
return result
|
||||
}
|
||||
case Configuration.paths.switchName:
|
||||
case Configuration.paths.switchString:
|
||||
pinEntities ??= () => entity.getPinEntities().filter(pinEntity => pinEntity.isOutput())
|
||||
pinIndexFromEntity ??= pinEntity => Number(pinEntity.PinName.match(/^\s*Case[_\s]+(\d+)\s*$/i)?.[1])
|
||||
pinNameFromIndex ??= (index, min = -1, max = -1, newPin) => {
|
||||
const result = `Case_${index >= 0 ? index : min > 0 ? "0" : max + 1}`
|
||||
entity.PinNames ??= []
|
||||
entity.PinNames.push(result)
|
||||
return result
|
||||
}
|
||||
break
|
||||
}
|
||||
if (pinEntities) {
|
||||
return () => {
|
||||
let min = Number.MAX_SAFE_INTEGER
|
||||
let max = Number.MIN_SAFE_INTEGER
|
||||
let values = []
|
||||
const modelPin = pinEntities().reduce(
|
||||
(acc, cur) => {
|
||||
const value = pinIndexFromEntity(cur)
|
||||
if (!isNaN(value)) {
|
||||
values.push(value)
|
||||
min = Math.min(value, min)
|
||||
if (value > max) {
|
||||
max = value
|
||||
return cur
|
||||
}
|
||||
} else if (acc === undefined) {
|
||||
return cur
|
||||
}
|
||||
return acc
|
||||
},
|
||||
undefined
|
||||
)
|
||||
if (min === Number.MAX_SAFE_INTEGER || max === Number.MIN_SAFE_INTEGER) {
|
||||
min = undefined
|
||||
max = undefined
|
||||
}
|
||||
if (!modelPin) {
|
||||
return null
|
||||
}
|
||||
values.sort((a, b) => a < b ? -1 : a === b ? 0 : 1)
|
||||
let prev = values[0]
|
||||
let index = values.findIndex(
|
||||
// Search for a gap
|
||||
value => {
|
||||
const result = value - prev > 1
|
||||
prev = value
|
||||
return result
|
||||
}
|
||||
)
|
||||
const newPin = new PinEntity(modelPin)
|
||||
newPin.PinId = GuidEntity.generateGuid()
|
||||
newPin.PinName = pinNameFromIndex(index, min, max, newPin)
|
||||
newPin.PinToolTip = undefined
|
||||
entity.getCustomproperties(true).push(newPin)
|
||||
return newPin
|
||||
}
|
||||
}
|
||||
}
|
||||
68
js/decoding/pinColor.js
Normal file
68
js/decoding/pinColor.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { css } from "lit"
|
||||
import Configuration from "../Configuration.js"
|
||||
|
||||
const colors = {
|
||||
[Configuration.paths.niagaraBool]: css`146, 0, 0`,
|
||||
[Configuration.paths.niagaraDataInterfaceVolumeTexture]: css`0, 168, 242`,
|
||||
[Configuration.paths.niagaraFloat]: css`160, 250, 68`,
|
||||
[Configuration.paths.niagaraMatrix]: css`0, 88, 200`,
|
||||
[Configuration.paths.niagaraNumeric]: css`0, 88, 200`,
|
||||
[Configuration.paths.niagaraPosition]: css`251, 146, 251`,
|
||||
[Configuration.paths.quat4f]: css`0, 88, 200`,
|
||||
[Configuration.paths.rotator]: css`157, 177, 251`,
|
||||
[Configuration.paths.transform]: css`227, 103, 0`,
|
||||
[Configuration.paths.vector]: css`251, 198, 34`,
|
||||
[Configuration.paths.vector3f]: css`250, 200, 36`,
|
||||
[Configuration.paths.vector4f]: css`0, 88, 200`,
|
||||
"Any": css`132, 132, 132`,
|
||||
"Any[]": css`132, 132, 132`,
|
||||
"audio": css`252, 148, 252`,
|
||||
"blue": css`0, 0, 255`,
|
||||
"bool": css`146, 0, 0`,
|
||||
"byte": css`0, 109, 99`,
|
||||
"class": css`88, 0, 186`,
|
||||
"default": css`255, 255, 255`,
|
||||
"delegate": css`255, 56, 56`,
|
||||
"enum": css`0, 109, 99`,
|
||||
"exec": css`240, 240, 240`,
|
||||
"float": css`160, 252, 70`,
|
||||
"green": css`0, 255, 0`,
|
||||
"int": css`31, 224, 172`,
|
||||
"int32": css`30, 224, 172`,
|
||||
"int64": css`169, 223, 172`,
|
||||
"interface": css`238, 252, 168`,
|
||||
"name": css`201, 128, 251`,
|
||||
"object": css`0, 168, 242`,
|
||||
"Param": css`255, 166, 39`,
|
||||
"Param[]": css`255, 166, 39`,
|
||||
"Point": css`63, 137, 255`,
|
||||
"Point[]": css`63, 137, 255`,
|
||||
"real": css`54, 208, 0`,
|
||||
"red": css`255, 0, 0`,
|
||||
"string": css`251, 0, 208`,
|
||||
"struct": css`0, 88, 200`,
|
||||
"Surface": css`69, 196, 126`,
|
||||
"Surface[]": css`69, 196, 126`,
|
||||
"text": css`226, 121, 167`,
|
||||
"time": css`148, 252, 252`,
|
||||
"Volume": css`230, 69, 188`,
|
||||
"Volume[]": css`230, 69, 188`,
|
||||
"wildcard": css`128, 120, 120`,
|
||||
}
|
||||
|
||||
const pinColorMaterial = css`120, 120, 120`
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
export default function pinColor(entity) {
|
||||
if (entity.PinType.PinCategory == "mask") {
|
||||
const result = colors[entity.PinType.PinSubCategory]
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
} else if (entity.PinType.PinCategory == "optional") {
|
||||
return pinColorMaterial
|
||||
}
|
||||
return colors[entity.getType()]
|
||||
?? colors[entity.PinType.PinCategory.toLowerCase()]
|
||||
?? colors["default"]
|
||||
}
|
||||
51
js/decoding/pinTemplate.js
Normal file
51
js/decoding/pinTemplate.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import Configuration from "../Configuration.js"
|
||||
import BoolPinTemplate from "../template/pin/BoolPinTemplate.js"
|
||||
import EnumPinTemplate from "../template/pin/EnumPinTemplate.js"
|
||||
import ExecPinTemplate from "../template/pin/ExecPinTemplate.js"
|
||||
import Int64PinTemplate from "../template/pin/Int64PinTemplate.js"
|
||||
import IntPinTemplate from "../template/pin/IntPinTemplate.js"
|
||||
import LinearColorPinTemplate from "../template/pin/LinearColorPinTemplate.js"
|
||||
import NamePinTemplate from "../template/pin/NamePinTemplate.js"
|
||||
import PinTemplate from "../template/pin/PinTemplate.js"
|
||||
import RealPinTemplate from "../template/pin/RealPinTemplate.js"
|
||||
import ReferencePinTemplate from "../template/pin/ReferencePinTemplate.js"
|
||||
import RotatorPinTemplate from "../template/pin/RotatorPinTemplate.js"
|
||||
import StringPinTemplate from "../template/pin/StringPinTemplate.js"
|
||||
import Vector2DPinTemplate from "../template/pin/Vector2DPinTemplate.js"
|
||||
import Vector4DPinTemplate from "../template/pin/Vector4DPinTemplate.js"
|
||||
import VectorPinTemplate from "../template/pin/VectorPinTemplate.js"
|
||||
|
||||
const inputPinTemplates = {
|
||||
[Configuration.paths.linearColor]: LinearColorPinTemplate,
|
||||
[Configuration.paths.niagaraBool]: BoolPinTemplate,
|
||||
[Configuration.paths.niagaraPosition]: VectorPinTemplate,
|
||||
[Configuration.paths.rotator]: RotatorPinTemplate,
|
||||
[Configuration.paths.vector]: VectorPinTemplate,
|
||||
[Configuration.paths.vector2D]: Vector2DPinTemplate,
|
||||
[Configuration.paths.vector3f]: VectorPinTemplate,
|
||||
[Configuration.paths.vector4f]: Vector4DPinTemplate,
|
||||
"bool": BoolPinTemplate,
|
||||
"byte": IntPinTemplate,
|
||||
"enum": EnumPinTemplate,
|
||||
"int": IntPinTemplate,
|
||||
"int64": Int64PinTemplate,
|
||||
"MUTABLE_REFERENCE": ReferencePinTemplate,
|
||||
"name": NamePinTemplate,
|
||||
"rg": Vector2DPinTemplate,
|
||||
"real": RealPinTemplate,
|
||||
"string": StringPinTemplate,
|
||||
}
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
export default function pinTemplate(entity) {
|
||||
if (entity.PinType.ContainerType?.toString() === "Array") {
|
||||
return PinTemplate
|
||||
}
|
||||
if (entity.PinType.bIsReference && !entity.PinType.bIsConst) {
|
||||
return inputPinTemplates["MUTABLE_REFERENCE"]
|
||||
}
|
||||
if (entity.getType() === "exec") {
|
||||
return ExecPinTemplate
|
||||
}
|
||||
return (entity.isInput() ? inputPinTemplates[entity.getType()] : PinTemplate) ?? PinTemplate
|
||||
}
|
||||
19
js/decoding/pinTitle.js
Normal file
19
js/decoding/pinTitle.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import Utility from "../Utility.js"
|
||||
|
||||
/** @param {PinEntity} entity */
|
||||
export default function pinTitle(entity) {
|
||||
let result = entity.PinFriendlyName
|
||||
? entity.PinFriendlyName.toString()
|
||||
: Utility.formatStringName(entity.PinName ?? "")
|
||||
let match
|
||||
if (
|
||||
entity.PinToolTip
|
||||
// Match up until the first \n excluded or last character
|
||||
&& (match = entity.PinToolTip.match(/\s*(.+?(?=\n)|.+\S)\s*/))
|
||||
) {
|
||||
if (match[1].toLowerCase() === result.toLowerCase()) {
|
||||
return match[1] // In case they match, then keep the case of the PinToolTip
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
Reference in New Issue
Block a user