summaryrefslogtreecommitdiffstats
path: root/libtastt.py
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2022-12-17 17:51:12 -0800
committeryum <yum.food.vr@gmail.com>2022-12-17 17:51:12 -0800
commitee8213d1d2c2008d2d996929500c9e87dac325a3 (patch)
treef5fb3da0fea10a30229c3642c4312fd457ac293a /libtastt.py
parent4d836989720523cd0363927e3e066f56b9dc445c (diff)
Finish python virtual env
GUI can now download all TaSTT dependencies and install them into a virtual environment. * Add buttons to check embedded python version & install dependencies * Add class to wrap interacting with embedded Python * Put all TaSTT python scripts into a folder
Diffstat (limited to 'libtastt.py')
-rw-r--r--libtastt.py578
1 files changed, 0 insertions, 578 deletions
diff --git a/libtastt.py b/libtastt.py
deleted file mode 100644
index bee535f..0000000
--- a/libtastt.py
+++ /dev/null
@@ -1,578 +0,0 @@
-#!/usr/bin/env python3
-
-import argparse
-import generate_utils
-import libunity
-import os
-import pickle
-import sys
-import typing
-
-# TODO(yum) we're getting the encoding scheme from here, but I think it should
-# be in a different layer.
-import osc_ctrl
-
-LETTER_ANIMATION_TEMPLATE = """
-%YAML 1.1
-%TAG !u! tag:unity3d.com,2011:
---- !u!74 &7400000
-AnimationClip:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_Name: REPLACEME_ANIMATION_NAME
- serializedVersion: 6
- m_Legacy: 0
- m_Compressed: 0
- m_UseHighQualityCurve: 1
- m_RotationCurves: []
- m_CompressedRotationCurves: []
- m_EulerCurves: []
- m_PositionCurves: []
- m_ScaleCurves: []
- m_FloatCurves:
- - curve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: REPLACEME_LETTER_VALUE
- inSlope: 0
- outSlope: 0
- tangentMode: 136
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- - serializedVersion: 3
- time: 0.016666668
- value: REPLACEME_LETTER_VALUE
- inSlope: 0
- outSlope: 0
- tangentMode: 136
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- attribute: material.REPLACEME_LETTER_PARAM
- path: TaSTT
- classID: 137
- script: {fileID: 0}
- m_PPtrCurves: []
- m_SampleRate: 60
- m_WrapMode: 0
- m_Bounds:
- m_Center: {x: 0, y: 0, z: 0}
- m_Extent: {x: 0, y: 0, z: 0}
- m_ClipBindingConstant:
- genericBindings:
- - serializedVersion: 2
- path: 2794480623
- attribute: 2284639795
- script: {fileID: 0}
- typeID: 137
- customType: 22
- isPPtrCurve: 0
- pptrCurveMapping: []
- m_AnimationClipSettings:
- serializedVersion: 2
- m_AdditiveReferencePoseClip: {fileID: 0}
- m_AdditiveReferencePoseTime: 0
- m_StartTime: 0
- m_StopTime: 0
- m_OrientationOffsetY: 0
- m_Level: 0
- m_CycleOffset: 0
- m_HasAdditiveReferencePose: 0
- m_LoopTime: 1
- m_LoopBlend: 0
- m_LoopBlendOrientation: 0
- m_LoopBlendPositionY: 0
- m_LoopBlendPositionXZ: 0
- m_KeepOriginalOrientation: 0
- m_KeepOriginalPositionY: 1
- m_KeepOriginalPositionXZ: 0
- m_HeightFromFeet: 0
- m_Mirror: 0
- m_EditorCurves:
- - curve:
- serializedVersion: 2
- m_Curve:
- - serializedVersion: 3
- time: 0
- value: REPLACEME_LETTER_VALUE
- inSlope: 0
- outSlope: 0
- tangentMode: 136
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- - serializedVersion: 3
- time: 0.016666668
- value: REPLACEME_LETTER_VALUE
- inSlope: 0
- outSlope: 0
- tangentMode: 136
- weightedMode: 0
- inWeight: 0
- outWeight: 0
- m_PreInfinity: 2
- m_PostInfinity: 2
- m_RotationOrder: 4
- attribute: material.REPLACEME_LETTER_PARAM
- path: TaSTT
- classID: 137
- script: {fileID: 0}
- m_EulerEditorCurves: []
- m_HasGenericRootTransform: 0
- m_HasMotionFloatCurves: 0
- m_Events: []
-"""
-
-ANIMATOR_TEMPLATE = """
---- !u!91 &9100000
-AnimatorController:
- m_ObjectHideFlags: 0
- m_CorrespondingSourceObject: {fileID: 0}
- m_PrefabInstance: {fileID: 0}
- m_PrefabAsset: {fileID: 0}
- m_Name: TaSTT_fx
- serializedVersion: 5
- m_AnimatorParameters: []
- m_AnimatorLayers: []
-"""
-
-# For whatever reason, running unrelated animations s.a.
-# facial expressions can have a slight effect on supposedly
-# unrelated parameters, causing letter to flip. Add a
-# little buffer to reduce the odds that this effect causes
-# a letter to change after it has been written.
-UNITY_ANIMATION_FUDGE_MARGIN = 0.1
-
-def generateClearAnimation(anim_dir, guid_map):
- print("Generating board clearing animation", file=sys.stderr)
-
- parser = libunity.UnityParser()
- parser.parse(LETTER_ANIMATION_TEMPLATE)
-
- anim_node = parser.nodes[0]
- anim_clip = anim_node.mapping['AnimationClip']
- curve_template = anim_clip.mapping['m_FloatCurves'].sequence[0]
- anim_clip.mapping['m_FloatCurves'].sequence = []
- anim_clip.mapping['m_EditorCurves'].sequence = []
-
- letter = 0
-
- for byte in range(0, generate_utils.BYTES_PER_CHAR):
- for row in range(0, generate_utils.BOARD_ROWS):
- for col in range(0, generate_utils.BOARD_COLS):
- curve = curve_template.copy()
- for keyframe in curve.mapping['curve'].mapping['m_Curve'].sequence:
- keyframe.mapping['value'] = str(letter +
- UNITY_ANIMATION_FUDGE_MARGIN)
- curve.mapping['attribute'] = "material.{}".format(generate_utils.getShaderParamByRowColByte(row, col, byte))
- curve.mapping['path'] = "World Constraint/Container/TaSTT"
- # Add curve to animation
- anim_clip.mapping['m_FloatCurves'].sequence.append(curve)
- anim_clip.mapping['m_EditorCurves'].sequence.append(curve)
- # Serialize animation to file
- anim_name = generate_utils.getClearAnimationName()
- anim_path = anim_dir + anim_name + ".anim"
- with open(anim_path, "w") as f:
- f.write(libunity.unityYamlToString([anim_node]))
- # Generate metadata
- meta = libunity.Metadata()
- with open(anim_path + ".meta", "w") as f:
- f.write(str(meta))
- # Add metadata to guid map
- guid_map[anim_path] = meta.guid
- guid_map[meta.guid] = anim_path
-
-# Generate a toggle animation for a shader parameter.
-def generateToggleAnimations(anim_dir, shader_param, guid_map):
- print("Generating shader toggle animation", file=sys.stderr)
-
- parser = libunity.UnityParser()
- parser.parse(LETTER_ANIMATION_TEMPLATE)
-
- # 0.0 represents false, 1.0 represents true. Don't forget that we add
- # `UNITY_ANIMATION_FUDGE_MARGIN` to everything.
- for shader_value in range(0, 2):
- anim_node = parser.nodes[0]
- anim_clip = anim_node.mapping['AnimationClip']
- curve_template = anim_clip.mapping['m_FloatCurves'].sequence[0]
- anim_clip.mapping['m_FloatCurves'].sequence = []
- anim_clip.mapping['m_EditorCurves'].sequence = []
-
- curve = curve_template.copy()
- for keyframe in curve.mapping['curve'].mapping['m_Curve'].sequence:
- keyframe.mapping['value'] = str(float(shader_value) +
- UNITY_ANIMATION_FUDGE_MARGIN)
- curve.mapping['attribute'] = "material.{}".format(shader_param)
- curve.mapping['path'] = "World Constraint/Container/TaSTT"
- # Add curve to animation
- anim_clip.mapping['m_FloatCurves'].sequence.append(curve)
- anim_clip.mapping['m_EditorCurves'].sequence.append(curve)
-
- # Serialize animation to file
- anim_name = generate_utils.getClearAnimationName()
- anim_suffix = "_Off"
- if shader_value == 1:
- anim_suffix = "_On"
- anim_path = anim_dir + shader_param + anim_suffix + ".anim"
- with open(anim_path, "w") as f:
- f.write(libunity.unityYamlToString([anim_node]))
- # Generate metadata
- meta = libunity.Metadata()
- with open(anim_path + ".meta", "w") as f:
- f.write(str(meta))
- # Add metadata to guid map
- guid_map[anim_path] = meta.guid
- guid_map[meta.guid] = anim_path
-
-# Generate a toggle animation for a shader parameter.
-def generateFloatAnimation(anim_name: str, anim_dir: str,
- path: str, attribute: str,
- value: float,
- guid_map: typing.Dict[str, str]) -> str:
- print("Generating float toggle animation {}/{}".format(path,attribute),
- file=sys.stderr)
-
- parser = libunity.UnityParser()
- parser.parse(LETTER_ANIMATION_TEMPLATE)
-
- # 0.0 represents false, 1.0 represents true. Don't forget that we add
- # `UNITY_ANIMATION_FUDGE_MARGIN` to everything.
- anim_node = parser.nodes[0]
- anim_clip = anim_node.mapping['AnimationClip']
- curve_template = anim_clip.mapping['m_FloatCurves'].sequence[0]
- anim_clip.mapping['m_FloatCurves'].sequence = []
- anim_clip.mapping['m_EditorCurves'].sequence = []
-
- curve = curve_template.copy()
- for keyframe in curve.mapping['curve'].mapping['m_Curve'].sequence:
- keyframe.mapping['value'] = str(value)
- curve.mapping['attribute'] = attribute
- curve.mapping['path'] = path
- # Add curve to animation
- anim_clip.mapping['m_FloatCurves'].sequence.append(curve)
- anim_clip.mapping['m_EditorCurves'].sequence.append(curve)
-
- # Serialize animation to file
- anim_path = anim_dir + anim_name + ".anim"
- with open(anim_path, "w") as f:
- f.write(libunity.unityYamlToString([anim_node]))
- # Generate metadata
- meta = libunity.Metadata()
- with open(anim_path + ".meta", "w") as f:
- f.write(str(meta))
- # Add metadata to guid map
- guid_map[anim_path] = meta.guid
- guid_map[meta.guid] = anim_path
-
- return meta.guid
-
-def generateAnimations(anim_dir, guid_map):
- generateClearAnimation(args.gen_anim_dir, guid_map)
-
- generateToggleAnimations(args.gen_anim_dir, generate_utils.getIndicator0Param(), guid_map)
- generateToggleAnimations(args.gen_anim_dir, generate_utils.getIndicator1Param(), guid_map)
-
- print("Generating letter animations", file=sys.stderr)
-
- parser = libunity.UnityParser()
- parser.parse(LETTER_ANIMATION_TEMPLATE)
-
- anim_node = parser.nodes[0]
- anim_clip = anim_node.mapping['AnimationClip']
- curve_template = anim_clip.mapping['m_FloatCurves'].sequence[0]
- anim_clip.mapping['m_FloatCurves'].sequence = []
- anim_clip.mapping['m_EditorCurves'].sequence = []
-
- # To support more languages, we use 2 bytes per character, giving us a 64K character set.
- for byte in range(0, generate_utils.BYTES_PER_CHAR):
- for row in range(0, generate_utils.BOARD_ROWS):
- print("Generating letter animations (row {}/{}) (byte {}/2)".format(row,
- generate_utils.BOARD_ROWS, byte), file=sys.stderr)
- for col in range(0, generate_utils.BOARD_COLS):
- for letter in range(0, 2):
- if letter == 1:
- letter = generate_utils.CHARS_PER_CELL - 1
-
- # Make a deep copy of the templates
- node = anim_node.copy()
- curve = curve_template.copy()
- clip = node.mapping['AnimationClip']
- # Populate animation name
- anim_name = generate_utils.getLetterAnimationName(row, col, letter, byte)
- clip.mapping['m_Name'] = anim_name
- # Populate letter value
- for keyframe in curve.mapping['curve'].mapping['m_Curve'].sequence:
- keyframe.mapping['value'] = str(letter + UNITY_ANIMATION_FUDGE_MARGIN)
- # Populate path to letter parameter
- curve.mapping['attribute'] = "material.{}".format(generate_utils.getShaderParamByRowColByte(row, col, byte))
- curve.mapping['path'] = "World Constraint/Container/TaSTT"
- # Add curve to animation
- clip.mapping['m_FloatCurves'].sequence.append(curve)
- clip.mapping['m_EditorCurves'].sequence.append(curve)
- # Serialize animation to file
- anim_path = anim_dir + anim_name + ".anim"
- with open(anim_path, "w") as f:
- f.write(libunity.unityYamlToString([node]))
- # Generate metadata
- meta = libunity.Metadata()
- with open(anim_path + ".meta", "w") as f:
- f.write(str(meta))
- # Add metadata to guid map
- guid_map[anim_path] = meta.guid
- guid_map[meta.guid] = anim_path
-
-def generateFXController(anim: libunity.UnityAnimator) -> typing.Dict[int, libunity.UnityDocument]:
- parser = libunity.UnityParser()
- parser.parse(ANIMATOR_TEMPLATE)
- anim.addNodes(parser.nodes)
-
- anim.addParameter(generate_utils.getEnableParam(), bool)
- anim.addParameter(generate_utils.getDummyParam(), bool)
- anim.addParameter(generate_utils.getHipToggleParam(), bool)
- anim.addParameter(generate_utils.getHandToggleParam(), bool)
- anim.addParameter(generate_utils.getToggleParam(), bool)
- anim.addParameter(generate_utils.getSpeechNoiseEnableParam(), bool)
- anim.addParameter(generate_utils.getClearBoardParam(), bool)
- anim.addParameter(generate_utils.getIndicator0Param(), bool)
- anim.addParameter(generate_utils.getIndicator1Param(), bool)
- anim.addParameter(generate_utils.getScaleParam(), float)
-
- layers = {}
- for byte in range(0, generate_utils.BYTES_PER_CHAR):
- layers[byte] = {}
- for i in range(0, generate_utils.NUM_LAYERS):
- anim.addParameter(generate_utils.getBlendParam(i, byte), float)
-
- layer = anim.addLayer(generate_utils.getLayerName(i, byte))
- layers[byte][i] = layer
- anim.addParameter(generate_utils.getSelectParam(), int)
-
- return layers
-
-def generateFXLayer(which_layer: int, anim: libunity.UnityAnimator, layer:
- libunity.UnityDocument, gen_anim_dir: str, byte: int):
- is_default_state = True
- default_state = anim.addAnimatorState(layer,
- generate_utils.getDefaultStateName(which_layer, byte), is_default_state)
-
- dy = 100
- active_state = anim.addAnimatorState(layer,
- generate_utils.getActiveStateName(which_layer, byte), dy = dy)
-
- active_state_transition = anim.addTransition(active_state)
- enable_param = generate_utils.getEnableParam()
- anim.addTransitionBooleanCondition(default_state, active_state_transition,
- enable_param, True)
-
- select_states = {}
- for i in range(0, generate_utils.NUM_REGIONS):
- dx = i * 200
- dy = 200
-
- # Create blend tree for this region.
- anim_lo_path = gen_anim_dir + \
- generate_utils.getAnimationNameByLayerAndIndex(
- which_layer, i, 0, byte) + \
- ".anim"
- guid_lo = guid_map[anim_lo_path]
- anim_hi_path = gen_anim_dir + \
- generate_utils.getAnimationNameByLayerAndIndex(
- which_layer, i, generate_utils.CHARS_PER_CELL - 1, byte) + \
- ".anim"
- guid_hi = guid_map[anim_hi_path]
-
- select_states[i] = anim.addAnimatorBlendTree(layer,
- generate_utils.getBlendStateName(which_layer, i, byte),
- generate_utils.getBlendParam(which_layer, byte),
- guid_lo, guid_hi, dx = dx, dy = dy)
- state = select_states[i]
-
- # Create transition to state.
- select_state_transition = anim.addTransition(state)
- select_param = generate_utils.getSelectParam()
- anim.addTransitionIntegerEqualityCondition(active_state,
- select_state_transition, select_param, i)
-
- # Create return-home transition.
- home_state_transition = anim.addTransition(default_state)
- home_state_transition.mapping['AnimatorStateTransition'].mapping['m_InterruptionSource'] = '0'
- dummy_param = generate_utils.getDummyParam()
- anim.addTransitionBooleanCondition(state,
- home_state_transition, dummy_param, False)
- pass
-
-# Generic toggle adding utility.
-# Generates the layer and parameter.
-# Returns a map containing the off and on states, as well as the
-# transitions between them.
-def generateToggle(layer_name: str,
- gen_anim_dir: str,
- off_anim_basename: str,
- on_anim_basename: str,
- anim: libunity.UnityAnimator) -> typing.Dict[str,
- libunity.UnityDocument]:
- layer = anim.addLayer(layer_name)
-
- # For simplicity, use the layer name as the parameter name.
- parameter_name = layer_name
- anim.addParameter(parameter_name, bool)
-
- off_state = anim.addAnimatorState(layer, layer_name + "_Off",
- is_default_state = True)
- on_state = anim.addAnimatorState(layer, layer_name + "_On", dy=100)
-
- if off_anim_basename:
- off_anim_path = gen_anim_dir + off_anim_basename
- off_anim_meta = libunity.Metadata()
- off_anim_meta.load(off_anim_path)
- anim.setAnimatorStateAnimation(off_state, off_anim_meta.guid)
-
- if on_anim_basename:
- on_anim_path = gen_anim_dir + on_anim_basename
- on_anim_meta = libunity.Metadata()
- on_anim_meta.load(on_anim_path)
- anim.setAnimatorStateAnimation(on_state, on_anim_meta.guid)
-
- off_to_on_trans = anim.addTransition(on_state)
- anim.addTransitionBooleanCondition(off_state,
- off_to_on_trans, parameter_name, True)
-
- on_to_off_trans = anim.addTransition(off_state)
- anim.addTransitionBooleanCondition(on_state,
- on_to_off_trans, parameter_name, False)
-
- result = {}
- result["off"] = off_state
- result["on"] = on_state
- result["off_to_on"] = off_to_on_trans
- result["on_to_off"] = on_to_off_trans
-
- return result
-
-def generateScaleLayer(anim: libunity.UnityAnimator,
- gen_anim_dir: str,
- guid_map: typing.Dict[str, str]):
-
- scale_layer = anim.addLayer(generate_utils.getScaleParam())
-
- path = "World Constraint/Container/TaSTT"
- attribute = "blendShape.Scale"
-
- guid_lo = generateFloatAnimation("TaSTT_Scale_0", gen_anim_dir,
- path, attribute,
- 0.0, guid_map)
- guid_hi = generateFloatAnimation("TaSTT_Scale_100", gen_anim_dir,
- path, attribute,
- 100.0, guid_map)
-
- anim.addAnimatorBlendTree(scale_layer,
- generate_utils.getScaleParam(),
- generate_utils.getScaleParam(),
- guid_lo, guid_hi,
- lo_threshold = 0.0, hi_threshold = 1.0);
-
- pass
-
-def generateFX(guid_map, gen_anim_dir):
- anim = libunity.UnityAnimator()
-
- layers = generateFXController(anim)
-
- # TODO(yum) parallelize
- for byte in range(0, generate_utils.BYTES_PER_CHAR):
- for which_layer, layer in layers[byte].items():
- print("Generating layer {}/{}".format(which_layer, len(layers[byte].items())), file=sys.stderr)
- generateFXLayer(which_layer, anim, layer, gen_anim_dir, byte)
-
- states = generateToggle(
- generate_utils.getSpeechNoiseToggleParam(),
- "Animations/",
- "TaSTT_Speech_Noise_Off.anim",
- "TaSTT_Speech_Noise_On.anim",
- anim)
- # Enable beeping only if user has turned it on.
- anim.addTransitionBooleanCondition(states["off"],
- states["off_to_on"], generate_utils.getSpeechNoiseEnableParam(), True)
- # Enable beeping only if board is out.
- anim.addTransitionBooleanCondition(states["off"],
- states["off_to_on"], generate_utils.getToggleParam(), True)
-
- generateToggle(generate_utils.getToggleParam(),
- "Animations/",
- "TaSTT_Toggle_Off.anim",
- "TaSTT_Toggle_On.anim",
- anim)
- generateToggle(generate_utils.getLockWorldParam(),
- "Animations/",
- "TaSTT_Lock_World_Disable.anim",
- "TaSTT_Lock_World_Enable.anim",
- anim)
- generateToggle(
- generate_utils.getClearBoardParam(),
- gen_anim_dir,
- None, # No animation in the `off` state.
- generate_utils.getClearAnimationName() + ".anim",
- anim)
- generateToggle(generate_utils.getIndicator0Param(),
- gen_anim_dir,
- generate_utils.getIndicator0Param() + "_Off.anim",
- generate_utils.getIndicator0Param() + "_On.anim",
- anim)
- generateToggle(generate_utils.getIndicator1Param(),
- gen_anim_dir,
- generate_utils.getIndicator1Param() + "_Off.anim",
- generate_utils.getIndicator1Param() + "_On.anim",
- anim)
- generateScaleLayer(anim, gen_anim_dir, guid_map)
-
- return anim
-
-def parseArgs():
- parser = argparse.ArgumentParser()
- parser.add_argument("cmd", type=str, help="")
- parser.add_argument("--gen_dir", type=str, help="The directory under " +
- "which all generated assets are placed")
- parser.add_argument("--gen_anim_dir", type=str, help="The directory under " +
- "which all generated animations are placed.")
- parser.add_argument("--guid_map", type=str, help="The path to a file which will store guids")
- args = parser.parse_args()
-
- if not args.gen_dir:
- args.gen_dir = "generated/"
-
- if not args.gen_anim_dir:
- args.gen_anim_dir = args.gen_dir + "animations/"
-
- if not args.guid_map:
- args.guid_map = "guid.map"
-
- return args
-
-if __name__ == "__main__":
- args = parseArgs()
-
- if args.cmd == "gen_anims":
- guid_map = {}
- with open(args.guid_map, 'rb') as f:
- guid_map = pickle.load(f)
-
- os.makedirs(args.gen_anim_dir, exist_ok=True)
- generateAnimations(args.gen_anim_dir, guid_map)
-
- with open(args.guid_map, 'wb') as f:
- pickle.dump(guid_map, f)
- elif args.cmd == "gen_fx":
- guid_map = {}
- with open(args.guid_map, 'rb') as f:
- guid_map = pickle.load(f)
-
- print(str(generateFX(guid_map, args.gen_anim_dir)))
-