Difference between revisions of "IC Python API:Saving JSON"
Chuck (RL) (Talk | contribs) (Created page with "{{TOC}} {{Parent|IC_Python_API:RL_Python_Samples|RL Python Samples}}") |
Chuck (RL) (Talk | contribs) m (→Saving the JSON File) |
||
(9 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
{{TOC}} | {{TOC}} | ||
{{Parent|IC_Python_API:RL_Python_Samples|RL Python Samples}} | {{Parent|IC_Python_API:RL_Python_Samples|RL Python Samples}} | ||
+ | {{Sibling|IC_Python_API:Loading_JSON|Loading JSON}} | ||
+ | |||
+ | This article will go over the handling of JSON formatted data for iClone. This is done by saving transformational data for all the props in the current iClone scene to an external '''.json''' file. For this lesson, you'll need to create an iClone scene containing at least one prop. | ||
+ | |||
+ | == Necessary Modules == | ||
+ | |||
+ | Besides the fundamental Reallusion Python module, we'll also need '''Pyside2''' to build the user interface, '''os''' to read/write to file, and '''json''' to interpret/convert JSON data. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | import RLPy | ||
+ | import os | ||
+ | import json | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | {{clear}} | ||
+ | |||
+ | == Global Variables == | ||
+ | |||
+ | Now we'll need some global variables for inputting object transformation data, creating an "exclusion" list for objects we don't want, and listing all the props in the scene; among other things. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | data = {"props": []} # Empty array to store object data | ||
+ | time = RLPy.RTime(0) | ||
+ | decimal_places = 3 # How accurately do we want to store the transform data? | ||
+ | exclusions = ["Shadow Catcher "] # Create a list objects to ignore when saving | ||
+ | all_props = RLPy.RScene.FindObjects(RLPy.EObjectType_Prop) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Notice that we have an optional '''decimal_places''' variable to control the accuracy of the stored data. | ||
+ | |||
+ | == Recording Transform Data == | ||
+ | |||
+ | [[File:Ic_python_api_saving_json_01.png|right]] | ||
+ | |||
+ | Since we have a list of all the props in the scene from the previous step, we'll need to iterate over each and every one and make an entry into the JSON '''data''' global variable. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | for prop in all_props: | ||
+ | if prop.GetName() in exclusions: | ||
+ | continue | ||
+ | control = prop.GetControl("Transform") | ||
+ | data_block = control.GetDataBlock() | ||
+ | data["props"].append( | ||
+ | {"Name": prop.GetName(), | ||
+ | "Position": { | ||
+ | "x": round(data_block.GetData("Position/PositionX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Position/PositionY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Position/PositionZ", time).ToFloat(), decimal_places) | ||
+ | }, | ||
+ | "Rotation": { | ||
+ | "x": round(data_block.GetData("Rotation/RotationX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Rotation/RotationY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Rotation/RotationZ", time).ToFloat(), decimal_places) | ||
+ | }, | ||
+ | "Scale": { | ||
+ | "x": round(data_block.GetData("Scale/ScaleX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Scale/ScaleY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Scale/ScaleZ", time).ToFloat(), decimal_places) | ||
+ | } | ||
+ | } | ||
+ | ) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Keep in mind that scale data shown in iClone's '''Modify''' panel is percentage based but is read and written into the '''data block''' in floating point values. This is to say a scale of 100 in the '''Modify''' panel is 1.0 in the scale value of the '''data block'''. | ||
+ | |||
+ | == JSON Encoding and Decoding == | ||
+ | |||
+ | [[File:Ic_python_api_saving_json_02.png|right]] | ||
+ | |||
+ | The primary encoding interface for '''json''' is '''dump''' and '''dumps'''. The difference being that '''dump''' is suitable for writing data to file/socket, while '''dumps''' is for string operations such as printing and parsing. | ||
+ | |||
+ | === Saving the JSON File === | ||
+ | |||
+ | We'll need to write the transformation data to a file located in the same directory as the script itself, called '''all_prop_transforms.json'''. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | # Prepare the filepath and save the json file | ||
+ | file_name = "all_prop_transformations.json" | ||
+ | |||
+ | local_path = os.path.dirname(os.path.realpath(__file__)) | ||
+ | json_file_path = os.path.join(local_path, file_name) | ||
+ | |||
+ | with open(json_file_path, "w") as json_file: | ||
+ | json.dump(data, json_file) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | You can find the JSON file in the same directory as the script file ('''all_prop_transforms.json'''). | ||
+ | |||
+ | === Reading Back the JSON File === | ||
+ | |||
+ | Finally, we will load the same data from file and use '''json.dumps''' to pretty print the content. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | # Read back the data and pretty print it in the console | ||
+ | with open(json_file_path, "r") as json_file: | ||
+ | data_in = json.load(json_file) | ||
+ | print(json.dumps(data_in, indent=4, sort_keys=True)) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | '''Prettyprint''' (or '''pretty-print''') is the application of any of various stylistic formatting conventions to text files, such as source code, markup, and similar kinds of content. These formatting conventions can adjust positioning and spacing (indent style), add color and contrast (syntax highlighting), adjust size, and make similar modifications intended to make the content easier for people to view, read, and understand. | ||
+ | |||
+ | == Everything Put Together == | ||
+ | |||
+ | [[File:Ic_python_api_saving_json_03.png|The contents within the '''all_prop_transforms.json''' file is not formatted for easy viewing.|frame]] | ||
+ | |||
+ | You can copy and paste the following code into a PY file and load it into iClone via '''Script > Load Python'''. Make sure to have the scene prepared beforehand by populating it with props in various states of transformations. | ||
+ | |||
+ | <syntaxhighlight lang="Python"> | ||
+ | import RLPy | ||
+ | import os | ||
+ | import json | ||
+ | |||
+ | data = {"props": []} # Empty array to store object data | ||
+ | time = RLPy.RTime(0) | ||
+ | decimal_places = 3 # How accurately do we want to store the transform data? | ||
+ | exclusions = ["Shadow Catcher "] # Create a list objects to ignore when saving | ||
+ | all_props = RLPy.RScene.FindObjects(RLPy.EObjectType_Prop) | ||
+ | |||
+ | for prop in all_props: | ||
+ | if prop.GetName() in exclusions: | ||
+ | continue | ||
+ | control = prop.GetControl("Transform") | ||
+ | data_block = control.GetDataBlock() | ||
+ | data["props"].append( | ||
+ | {"Name": prop.GetName(), | ||
+ | "Position": { | ||
+ | "x": round(data_block.GetData("Position/PositionX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Position/PositionY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Position/PositionZ", time).ToFloat(), decimal_places) | ||
+ | }, | ||
+ | "Rotation": { | ||
+ | "x": round(data_block.GetData("Rotation/RotationX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Rotation/RotationY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Rotation/RotationZ", time).ToFloat(), decimal_places) | ||
+ | }, | ||
+ | "Scale": { | ||
+ | "x": round(data_block.GetData("Scale/ScaleX", time).ToFloat(), decimal_places), | ||
+ | "y": round(data_block.GetData("Scale/ScaleY", time).ToFloat(), decimal_places), | ||
+ | "z": round(data_block.GetData("Scale/ScaleZ", time).ToFloat(), decimal_places) | ||
+ | } | ||
+ | } | ||
+ | ) | ||
+ | |||
+ | # Prepare the filepath and save the json file | ||
+ | file_name = "all_prop_transformations.json" | ||
+ | |||
+ | local_path = os.path.dirname(os.path.realpath(__file__)) | ||
+ | json_file_path = os.path.join(local_path, file_name) | ||
+ | |||
+ | with open(json_file_path, "w") as json_file: | ||
+ | json.dump(data, json_file) | ||
+ | |||
+ | # Read back the data and pretty print it in the console | ||
+ | with open(json_file_path, "r") as json_file: | ||
+ | data_in = json.load(json_file) | ||
+ | print(json.dumps(data_in, indent=4, sort_keys=True)) | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | == APIs Used == | ||
+ | |||
+ | You can research the following references for the APIs deployed in this code. | ||
+ | |||
+ | <div style="column-count:4; -moz-column-count:4; -webkit-column-count:4"> | ||
+ | * [[ IC_Python_API:RLPy_RTime | RLPy.RTime() ]] | ||
+ | * [[ IC_Python_API:RLPy_RScene#FindObjects | RLPy.RScene.FindObjects() ]] | ||
+ | </div> |
Latest revision as of 22:06, 8 March 2020
- Main article: RL Python Samples.
This article will go over the handling of JSON formatted data for iClone. This is done by saving transformational data for all the props in the current iClone scene to an external .json file. For this lesson, you'll need to create an iClone scene containing at least one prop.
Necessary Modules
Besides the fundamental Reallusion Python module, we'll also need Pyside2 to build the user interface, os to read/write to file, and json to interpret/convert JSON data.
import RLPy
import os
import json
Global Variables
Now we'll need some global variables for inputting object transformation data, creating an "exclusion" list for objects we don't want, and listing all the props in the scene; among other things.
data = {"props": []} # Empty array to store object data
time = RLPy.RTime(0)
decimal_places = 3 # How accurately do we want to store the transform data?
exclusions = ["Shadow Catcher "] # Create a list objects to ignore when saving
all_props = RLPy.RScene.FindObjects(RLPy.EObjectType_Prop)
Notice that we have an optional decimal_places variable to control the accuracy of the stored data.
Recording Transform Data
Since we have a list of all the props in the scene from the previous step, we'll need to iterate over each and every one and make an entry into the JSON data global variable.
for prop in all_props:
if prop.GetName() in exclusions:
continue
control = prop.GetControl("Transform")
data_block = control.GetDataBlock()
data["props"].append(
{"Name": prop.GetName(),
"Position": {
"x": round(data_block.GetData("Position/PositionX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Position/PositionY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Position/PositionZ", time).ToFloat(), decimal_places)
},
"Rotation": {
"x": round(data_block.GetData("Rotation/RotationX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Rotation/RotationY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Rotation/RotationZ", time).ToFloat(), decimal_places)
},
"Scale": {
"x": round(data_block.GetData("Scale/ScaleX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Scale/ScaleY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Scale/ScaleZ", time).ToFloat(), decimal_places)
}
}
)
Keep in mind that scale data shown in iClone's Modify panel is percentage based but is read and written into the data block in floating point values. This is to say a scale of 100 in the Modify panel is 1.0 in the scale value of the data block.
JSON Encoding and Decoding
The primary encoding interface for json is dump and dumps. The difference being that dump is suitable for writing data to file/socket, while dumps is for string operations such as printing and parsing.
Saving the JSON File
We'll need to write the transformation data to a file located in the same directory as the script itself, called all_prop_transforms.json.
# Prepare the filepath and save the json file
file_name = "all_prop_transformations.json"
local_path = os.path.dirname(os.path.realpath(__file__))
json_file_path = os.path.join(local_path, file_name)
with open(json_file_path, "w") as json_file:
json.dump(data, json_file)
You can find the JSON file in the same directory as the script file (all_prop_transforms.json).
Reading Back the JSON File
Finally, we will load the same data from file and use json.dumps to pretty print the content.
# Read back the data and pretty print it in the console
with open(json_file_path, "r") as json_file:
data_in = json.load(json_file)
print(json.dumps(data_in, indent=4, sort_keys=True))
Prettyprint (or pretty-print) is the application of any of various stylistic formatting conventions to text files, such as source code, markup, and similar kinds of content. These formatting conventions can adjust positioning and spacing (indent style), add color and contrast (syntax highlighting), adjust size, and make similar modifications intended to make the content easier for people to view, read, and understand.
Everything Put Together
You can copy and paste the following code into a PY file and load it into iClone via Script > Load Python. Make sure to have the scene prepared beforehand by populating it with props in various states of transformations.
import RLPy
import os
import json
data = {"props": []} # Empty array to store object data
time = RLPy.RTime(0)
decimal_places = 3 # How accurately do we want to store the transform data?
exclusions = ["Shadow Catcher "] # Create a list objects to ignore when saving
all_props = RLPy.RScene.FindObjects(RLPy.EObjectType_Prop)
for prop in all_props:
if prop.GetName() in exclusions:
continue
control = prop.GetControl("Transform")
data_block = control.GetDataBlock()
data["props"].append(
{"Name": prop.GetName(),
"Position": {
"x": round(data_block.GetData("Position/PositionX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Position/PositionY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Position/PositionZ", time).ToFloat(), decimal_places)
},
"Rotation": {
"x": round(data_block.GetData("Rotation/RotationX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Rotation/RotationY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Rotation/RotationZ", time).ToFloat(), decimal_places)
},
"Scale": {
"x": round(data_block.GetData("Scale/ScaleX", time).ToFloat(), decimal_places),
"y": round(data_block.GetData("Scale/ScaleY", time).ToFloat(), decimal_places),
"z": round(data_block.GetData("Scale/ScaleZ", time).ToFloat(), decimal_places)
}
}
)
# Prepare the filepath and save the json file
file_name = "all_prop_transformations.json"
local_path = os.path.dirname(os.path.realpath(__file__))
json_file_path = os.path.join(local_path, file_name)
with open(json_file_path, "w") as json_file:
json.dump(data, json_file)
# Read back the data and pretty print it in the console
with open(json_file_path, "r") as json_file:
data_in = json.load(json_file)
print(json.dumps(data_in, indent=4, sort_keys=True))
APIs Used
You can research the following references for the APIs deployed in this code.