optimze unity project for xr_rm

This commit is contained in:
2026-06-03 14:16:52 +08:00
parent 9260e46b3c
commit 3fc8024648
98 changed files with 20352 additions and 1568 deletions

1
.gitignore vendored
View File

@ -21,6 +21,7 @@ __pycache__/
.pytest_cache/ .pytest_cache/
.mypy_cache/ .mypy_cache/
*.egg-info/ *.egg-info/
*.log
qtcreator-* qtcreator-*
*.user *.user

View File

@ -15,7 +15,7 @@ PICO/XR UDP JSON
-> /xr_rm/<arm_name>/current_pose、raw_target_pose、target_pose、cmd_vel、target_clamped -> /xr_rm/<arm_name>/current_pose、raw_target_pose、target_pose、cmd_vel、target_clamped
``` ```
核心控制方式是相对位姿透传遥操作:`grip=true` 的第一帧锁定手柄起点和机器人 TCP 起点,之后用手柄位移增量生成目标 TCP并通过 `rm_movep_canfd` 下发位姿目标;`grip=false`、UDP 超时、异常或节点退出必须停止。 核心控制方式是相对位姿透传遥操作:`grip=true` 的第一帧锁定手柄起点和机器人 TCP 起点,之后用手柄位移增量生成目标 TCP并通过 `rm_movep_canfd` 下发位姿目标;`grip=false``pose_valid=false`UDP 超时、异常或节点退出必须停止。
## 固定工作流 ## 固定工作流
@ -38,12 +38,16 @@ PICO/XR UDP JSON
- 人负责目标、验收和最终合并。 - 人负责目标、验收和最终合并。
- AI 负责探索、修改、运行检查、总结风险。 - AI 负责探索、修改、运行检查、总结风险。
- Git 分支、测试和 `git diff` 是隔离与回滚边界。 - Git 分支、测试和 `git diff` 是隔离与回滚边界。
- 不允许绕过人工确认直接提交、推送或删除重要文件。 - 默认不允许绕过人工确认直接修改文件、提交、推送或删除重要文件。
- 除非用户明确写出“可以直接修改”“无需确认”“直接执行”等授权语句,否则任何会写入文件的操作前都必须先输出计划并等待人工确认。
## 代理执行规则 ## 代理执行规则
- 先探索,再修改。优先读相关 package、launch、YAML、消息、Unity 脚本和 README。 - 先探索,再修改。优先读相关 package、launch、YAML、消息、Unity 脚本和 README。
- 修改前说明目标理解、涉及文件、实现计划风险;小任务可以简短说明后直接执行 - 修改前必须先说明目标理解、涉及文件、实现计划风险和验证方式,并等待用户明确回复同意后再动手
- 小任务也遵守确认门禁;可以把计划写得很短,但不能在未获授权时直接写文件。
- 用户已经明确授权直接修改的当前请求,可以在简短说明后执行;仍需保持小步修改并在结束时总结差异。
- 在等待确认前,可以执行只读探索命令,例如查看文件、搜索代码、检查 `git status --short` 和阅读 diff。
- 只改与任务相关的最小文件集,避免无关重构、格式化和大范围重写。 - 只改与任务相关的最小文件集,避免无关重构、格式化和大范围重写。
- 不要自动 `git add .`、commit、push、force push、删分支或合并分支。 - 不要自动 `git add .`、commit、push、force push、删分支或合并分支。
- 不要回滚用户已有改动。工作区有未提交内容时,默认它们属于用户。 - 不要回滚用户已有改动。工作区有未提交内容时,默认它们属于用户。
@ -59,6 +63,7 @@ PICO/XR UDP JSON
- 不要随意修改工作空间边界、圆柱半径限制、`xr_to_robot_matrix`、初始化关节/TCP 位姿、速度/加速度限制、末端外设配置。 - 不要随意修改工作空间边界、圆柱半径限制、`xr_to_robot_matrix`、初始化关节/TCP 位姿、速度/加速度限制、末端外设配置。
- 真机相关改动必须保持以下安全停止路径: - 真机相关改动必须保持以下安全停止路径:
- `grip=false` - `grip=false`
- PICO/Unity 发送 `pose_valid=false`
- UDP 超时 / controller 数据过期 - UDP 超时 / controller 数据过期
- adapter 异常 - adapter 异常
- 节点 shutdown - 节点 shutdown
@ -88,9 +93,12 @@ ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true
模拟 UDP 模拟 UDP
```bash ```bash
ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 --seconds 10 ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 \
--pattern axis_sweep --seconds 60 --both-mode staggered
``` ```
当前 `sample_udp_sender` 默认用 `axis_sweep` 扫轴轨迹;`--both-mode staggered` 会先左后右,适合双臂方向检查。
常用话题: 常用话题:
```bash ```bash
@ -148,7 +156,13 @@ git diff --check
小功能开发: 小功能开发:
```text ```text
请实现 XXX。限制不改变现有接口不重构无关代码,修改前说明计划,修改后运行测试或告诉我如何测试 请实现 XXX。限制不改变现有接口不重构无关代码。本轮先不要修改文件,请先说明计划、涉及文件、风险和验证方式,等我回复“开始修改”后再动手
```
直接授权小改:
```text
请直接修改 XXX无需等待二次确认。限制只改相关文件修改后说明差异和验证方式。
``` ```
代码审查: 代码审查:
@ -166,17 +180,17 @@ git diff --check
## 包职责 ## 包职责
- `xr_rm_interfaces`:定义 `XrController` - `xr_rm_interfaces`:定义 `XrController`
- `xr_rm_input`:接收 UDP controller JSON发布左右手柄话题,并提供 `sample_udp_sender` - `xr_rm_input`:接收 UDP controller JSON发布左右手柄话题`pose_valid=false` 会强制 `grip=false`提供 `sample_udp_sender` 扫轴/正弦调试数据
- `xr_rm_teleop`:把手柄相对位移映射成 RM75 笛卡尔位姿透传目标。 - `xr_rm_teleop`:把手柄相对位移映射成 RM75 笛卡尔位姿透传目标。
- `xr_rm_bringup`:维护 launch、YAML、现场 UI 和运行入口。 - `xr_rm_bringup`:维护 launch、YAML、现场 UI 和运行入口。
- `unity/XR_RM_PICO_UDP_Sender`PICO 4 Ultra UDP Sender Unity 工程。 - `unity/XR_RM_PICO_UDP_Sender`PICO 4 Ultra UDP Sender Unity 工程,发送 `seq``source_time``pose_valid``pose_source`、tracking/controller status 等诊断字段
## 文档规则 ## 文档规则
- 根目录保留 `README.md``CODEX.md` - 根目录保留 `README.md``CODEX.md`
- `README.md` 是唯一项目主文档,包含 ROS2 构建、运行、mock、真机、安全和协议说明。 - `README.md` 是唯一项目主文档,包含 ROS2 构建、运行、mock、真机、安全和协议说明。
- `CODEX.md` 是代码代理规则文档,修改工作流时优先更新它。 - `CODEX.md` 是代码代理规则文档,修改工作流时优先更新它。
- `docs/` 只保留 Ubuntu 22.04 下 PICO 发送 UDP 数据的配置教程。 - `docs/` 下的 Markdown 只维护 Ubuntu 22.04 下 PICO 发送 UDP 数据的配置教程;已有非 Markdown 汇报材料不要扩写成源码教程
- 不要新增零散 Markdown除非用户明确要求。 - 不要新增零散 Markdown除非用户明确要求。
- 不要声明未实现功能已经完成。mock、真机、Unity/PICO 路线要写清楚。 - 不要声明未实现功能已经完成。mock、真机、Unity/PICO 路线要写清楚。
@ -184,9 +198,11 @@ git diff --check
- 当前 Unity 工程:`unity/XR_RM_PICO_UDP_Sender` - 当前 Unity 工程:`unity/XR_RM_PICO_UDP_Sender`
- 当前 PICO SDK`unity/PICO-Unity-Integration-SDK-release_3.4.0` - 当前 PICO SDK`unity/PICO-Unity-Integration-SDK-release_3.4.0`
- 当前 Unity package 还依赖 TextMeshProPICO 面板字体来自预生成的 `Assets/Resources/Fonts/Roboto-Regular SDF.asset``Roboto-Bold SDF.asset`;不要在 APK 运行时动态创建 TMP 字体资产。
- APK、Unity `Builds/``Library/`、日志和临时文件不是源码,不应提交。 - APK、Unity `Builds/``Library/`、日志和临时文件不是源码,不应提交。
- PICO UDP 目标 IP 必须是当前 Ubuntu ROS 主机在同一局域网的 IPv4。 - PICO UDP 目标 IP 必须是当前 Ubuntu ROS 主机在同一局域网的 IPv4。
- PICO App 暂停、退出或关闭发送时必须发送 `grip=false` 或让 ROS 侧超时安全停止。 - PICO App 暂停、退出或关闭发送时必须发送 `grip=false`,姿态无效时必须发送 `pose_valid=false` 并让 ROS receiver 强制停止。
- 当前 Unity `Project (+Z back)` 输出必须是 `+X` 右、`+Y` 上、`+Z` 后。PXR `pxr_predict` 原始坐标按实测先转换为 `project.x=native.z``project.y=native.y``project.z=-native.x``Source raw` 仅用于现场对照。若 `/xr/*_controller.pose.position` 已正确但某一只臂方向不对,优先改对应 YAML 的 `xr_to_robot_matrix`
## 不在当前任务范围内 ## 不在当前任务范围内

View File

@ -21,7 +21,7 @@ PICO/XR 双手柄 UDP JSON
- 通过统一的 `arm_debug.launch.py` 支持左臂、右臂、双臂的 mock 调试和真机调试。 - 通过统一的 `arm_debug.launch.py` 支持左臂、右臂、双臂的 mock 调试和真机调试。
- RM75 真机连接适配,包含 `rm_movep_canfd` 位姿透传、安全速度/加速度配置、可选初始化点位移动。 - RM75 真机连接适配,包含 `rm_movep_canfd` 位姿透传、安全速度/加速度配置、可选初始化点位移动。
- Tkinter 启动面板 `launcher_ui.py`,用于现场快速启动、监控 topic、检查环境和清理进程。 - Tkinter 启动面板 `launcher_ui.py`,用于现场快速启动、监控 topic、检查环境和清理进程。
- 自定义 PICO 4 Ultra UDP Sender Unity 工程,负责发送左右手柄 pose、`grip``trigger` - 自定义 PICO 4 Ultra UDP Sender Unity 工程,负责发送左右手柄 pose、`grip``trigger` 和 pose 诊断字段
暂未完成: 暂未完成:
@ -36,9 +36,14 @@ src/
├── README.md # 项目主文档 ├── README.md # 项目主文档
├── CODEX.md # Codex/Claude Code 项目工作流和安全规则 ├── CODEX.md # Codex/Claude Code 项目工作流和安全规则
├── docs/ ├── docs/
│ └── pico_udp_sender_ubuntu22_setup.md │ └── pico_udp_sender_ubuntu22_setup.md # Ubuntu 22.04 下 PICO UDP Sender 配置教程
├── unity/ ├── unity/
│ ├── XR_RM_PICO_UDP_Sender/ # PICO 4 Ultra UDP Sender Unity 工程 │ ├── XR_RM_PICO_UDP_Sender/ # PICO 4 Ultra UDP Sender Unity 工程
│ │ ├── Assets/Editor/ # Android/PICO 设置与 APK 构建菜单
│ │ ├── Assets/Scripts/ # UDP sender、配置面板、KeepAwake
│ │ ├── Assets/Resources/ # PICO 资源与 Roboto TMP 字体
│ │ ├── Packages/ # Unity package manifest
│ │ └── ProjectSettings/
│ └── PICO-Unity-Integration-SDK-release_3.4.0/ │ └── PICO-Unity-Integration-SDK-release_3.4.0/
├── xr_rm_bringup/ ├── xr_rm_bringup/
│ ├── config/ │ ├── config/
@ -56,7 +61,7 @@ src/
│ │ └── udp_receiver.launch.py # 低层 UDP 接收测试入口 │ │ └── udp_receiver.launch.py # 低层 UDP 接收测试入口
│ └── xr_rm_input/ │ └── xr_rm_input/
│ ├── udp_controller_receiver.py │ ├── udp_controller_receiver.py
│ └── sample_udp_sender.py # 本机模拟手柄 UDP 数据 │ └── sample_udp_sender.py # 本机扫轴/正弦模拟手柄 UDP 数据
├── xr_rm_interfaces/ ├── xr_rm_interfaces/
│ └── msg/ │ └── msg/
│ └── XrController.msg # hand/grip/trigger/pose │ └── XrController.msg # hand/grip/trigger/pose
@ -111,7 +116,7 @@ ros2 run xr_rm_bringup launcher_ui
面板顶部的 `Mode` 分为五类: 面板顶部的 `Mode` 分为五类:
- `Simulation`:左臂 mock、右臂 mock、双臂 mock、sample UDP 发送、one-click mock demo。 - `Simulation`:左臂 mock、右臂 mock、双臂 mock、sample UDP 发送、one-click mock demo、controller 位置/频率监控
- `Left Arm`:左臂网络 ping、左臂真机 launch、左手 sample UDP。 - `Left Arm`:左臂网络 ping、左臂真机 launch、左手 sample UDP。
- `Right Arm`:右臂网络 ping、右臂真机 launch、右手 sample UDP。 - `Right Arm`:右臂网络 ping、右臂真机 launch、右手 sample UDP。
- `Dual Arm`:左右臂 ping、双臂真机 launch、双手 sample UDP。 - `Dual Arm`:左右臂 ping、双臂真机 launch、双手 sample UDP。
@ -123,12 +128,14 @@ ros2 run xr_rm_bringup launcher_ui
- `Check Env`:检查 ROS2 Humble、工作空间 build、终端、核心 ROS 包、睿尔曼 API2。 - `Check Env`:检查 ROS2 Humble、工作空间 build、终端、核心 ROS 包、睿尔曼 API2。
- `Stop All`:结束由本工作空间启动的 launch、sample sender、topic monitor、相关 ROS 节点和终端窗口。 - `Stop All`:结束由本工作空间启动的 launch、sample sender、topic monitor、相关 ROS 节点和终端窗口。
每个模式都会附带三个监控入口: 每个模式都会附带基础监控入口:
- `Open Controller Topic Monitor`:同时查看 `/xr/left_controller``/xr/right_controller` - `Open Controller Topic Monitor`:同时查看 `/xr/left_controller``/xr/right_controller`
- `Open Target Velocity Monitor`:同时查看 `/xr_rm/left_rm75/cmd_vel``/xr_rm/right_rm75/cmd_vel`;该话题表示目标位姿变化率,仅用于调试。 - `Open Target Velocity Monitor`:同时查看 `/xr_rm/left_rm75/cmd_vel``/xr_rm/right_rm75/cmd_vel`;该话题表示目标位姿变化率,仅用于调试。
- `Open ROS Topic/Node List Monitor`:每秒刷新 `ros2 topic list``ros2 node list` - `Open ROS Topic/Node List Monitor`:每秒刷新 `ros2 topic list``ros2 node list`
`Simulation` 模式还提供 `Open Controller Position Monitor``Open Controller Hz Monitor`,用于快速看手柄位置字段和接收频率。
分屏监控依赖 `x-terminal-emulator` 指向 Terminator。若提示不支持可安装并切换 分屏监控依赖 `x-terminal-emulator` 指向 Terminator。若提示不支持可安装并切换
```bash ```bash
@ -148,9 +155,12 @@ sudo update-alternatives --config x-terminal-emulator
```bash ```bash
ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=true
ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 --seconds 10 ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 \
--pattern axis_sweep --seconds 60 --both-mode staggered
``` ```
`sample_udp_sender` 默认使用 `axis_sweep` 成对扫轴轨迹,并在终端打印 `XR +X/-X/+Y/-Y/+Z/-Z` 标签。`--hand both --both-mode staggered --seconds 60` 会先左后右,适合肉眼确认左右臂方向;如果只想左右同时动,可用 `--both-mode synchronized`
观察: 观察:
```bash ```bash
@ -200,8 +210,12 @@ ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false \
- `left_robot_ip`:左臂 IP默认 `192.168.192.18` - `left_robot_ip`:左臂 IP默认 `192.168.192.18`
- `right_robot_ip`:右臂 IP默认 `192.168.192.19` - `right_robot_ip`:右臂 IP默认 `192.168.192.19`
- `robot_port`RM75 TCP 端口,默认 `8080` - `robot_port`RM75 TCP 端口,默认 `8080`
- `left_avoid_singularity` / `right_avoid_singularity`:左右臂避奇异参数,默认左 `0`、右 `1`
- `avoid_singularity`:非空时覆盖左右臂避奇异参数。
- `frame_type``rm_movep_canfd` 坐标系类型,默认 `1`
- `control_rate_hz``rm_movep_canfd` 目标位姿发送频率,默认 `90.0` - `control_rate_hz``rm_movep_canfd` 目标位姿发送频率,默认 `90.0`
- `follow`:传给 `rm_movep_canfd` 的跟随标志,默认 `false` - `follow`:传给 `rm_movep_canfd` 的跟随标志,默认 `false`
- `configure_safety_limits`:连接真机后是否配置速度/加速度安全参数,默认 `true`
- `enable_tool_control`:是否在遥操作节点内启用末端工具控制 topic默认 `true` - `enable_tool_control`:是否在遥操作节点内启用末端工具控制 topic默认 `true`
- `configure_peripheral_on_connect`:遥操作节点连接真机后是否配置末端外设,默认 `true`;工具控制会复用同一个 RealMan 连接,避免两个进程同时抢占同一机械臂。 - `configure_peripheral_on_connect`:遥操作节点连接真机后是否配置末端外设,默认 `true`;工具控制会复用同一个 RealMan 连接,避免两个进程同时抢占同一机械臂。
- `move_to_initial_pose_on_connect`:连接后是否执行 `movej`/`movel` 初始化,默认 `false` - `move_to_initial_pose_on_connect`:连接后是否执行 `movej`/`movel` 初始化,默认 `false`
@ -226,18 +240,20 @@ ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false \
- `max_linear_speed`:目标位姿单帧步长限制对应的最大线速度。 - `max_linear_speed`:目标位姿单帧步长限制对应的最大线速度。
- `workspace_min` / `workspace_max`:笛卡尔工作空间边界。 - `workspace_min` / `workspace_max`:笛卡尔工作空间边界。
- `cyl_radius_limit`:基座圆柱半径限制。 - `cyl_radius_limit`:基座圆柱半径限制。
- `xr_to_robot_matrix`PICO/OpenXR 位移到 RM75 base 坐标的映射矩阵。 - `xr_to_robot_matrix``/xr/*_controller` Project 位移到 RM75 base 坐标的映射矩阵。
- `current_pose_poll_hz`:低频读取真机当前 TCP 的频率;控制中不再每帧阻塞读取状态。 - `current_pose_poll_hz`:低频读取真机当前 TCP 的频率;控制中不再每帧阻塞读取状态。
- `mock_initial_pose`mock 模式初始 TCP 位姿。 - `mock_initial_pose`mock 模式初始 TCP 位姿。
- `initial_joint_pose` / `initial_tcp_pose`:可选真机初始化点。 - `initial_joint_pose` / `initial_tcp_pose`:可选真机初始化点。
当前坐标约定: 当前 `/xr/*_controller` 的 Project 坐标约定:
- PICO/OpenXR`+X` 向右,`+Y` 向上,`+Z` 向后。 - Project`+X` 向右,`+Y` 向上,`+Z` 向后。
- Unity APK 的 `Project (+Z back)` 会把 PXR `pxr_predict` 原始坐标转换为 `project.x=native.z``project.y=native.y``project.z=-native.x`
- `Source raw` 模式保留原始 pose source 坐标,只用于现场对照。
- 左臂映射:机器人位移增量 = `[-手柄y, 手柄z, -手柄x]` - 左臂映射:机器人位移增量 = `[-手柄y, 手柄z, -手柄x]`
- 右臂映射:机器人位移增量 = `[手柄y, 手柄z, 手柄x]` - 右臂映射:机器人位移增量 = `[手柄y, 手柄z, 手柄x]`
如果某个方向相反,只改对应臂的 `xr_to_robot_matrix` 符号,不要同时改多个控制参数。 如果 `/xr/*_controller.pose.position` 已符合 Project 坐标,但某个机械臂方向相反,只改对应臂的 `xr_to_robot_matrix` 符号,不要同时改多个控制参数。
## 末端工具开合 ## 末端工具开合
@ -255,34 +271,52 @@ ros2 topic pub --once /xr_rm/right_rm75/tool_enable std_msgs/msg/Bool "{data: fa
## UDP 数据格式 ## UDP 数据格式
单个手柄 当前 Unity APK 每个周期发送一个双手柄 JSON 包
```json
{
"hand": "right",
"grip": true,
"trigger": 0.2,
"pos": [0.12, 1.05, 0.30],
"quat": [0.0, 0.0, 0.0, 1.0]
}
```
左右手柄一起发送:
```json ```json
{ {
"t": 12.345,
"source_time": 12.345,
"seq": 42,
"frame_id": "xr_world",
"controllers": { "controllers": {
"left": { "left": {
"grip": true, "grip": true,
"trigger": 0.0, "trigger": 0.0,
"pos": [-0.12, 1.05, 0.30], "pos": [-0.12, 1.05, 0.30],
"quat": [0.0, 0.0, 0.0, 1.0] "quat": [0.0, 0.0, 0.0, 1.0],
"pose_valid": true,
"pose_source": "pxr_predict",
"tracking_state": 3,
"controller_status": 2,
"grip_value": 1.0,
"axis": [0.0, 0.0],
"buttons": {
"grip": true,
"primary": false,
"secondary": false,
"menu": false,
"axis_click": false
}
}, },
"right": { "right": {
"grip": true, "grip": true,
"trigger": 0.4, "trigger": 0.4,
"pos": [0.12, 1.05, 0.30], "pos": [0.12, 1.05, 0.30],
"quat": [0.0, 0.0, 0.0, 1.0] "quat": [0.0, 0.0, 0.0, 1.0],
"pose_valid": true,
"pose_source": "unity_xr",
"tracking_state": 3,
"controller_status": -1,
"grip_value": 0.8,
"axis": [0.0, 0.0],
"buttons": {
"grip": true,
"primary": false,
"secondary": false,
"menu": false,
"axis_click": false
}
} }
} }
} }
@ -290,11 +324,19 @@ ros2 topic pub --once /xr_rm/right_rm75/tool_enable std_msgs/msg/Bool "{data: fa
字段说明: 字段说明:
- `hand``left``right` - `t` / `source_time`Unity 端 `Time.realtimeSinceStartupAsDouble`,用于后续延迟分析
- `seq`Unity 端递增包序号,用于后续丢包分析。
- `frame_id`:默认 `xr_world`,会写入 `XrController.header.frame_id`
- `grip`:运动使能。`true` 时进入相对位移控制,`false` 时停止。 - `grip`:运动使能。`true` 时进入相对位移控制,`false` 时停止。
- `trigger`:扳机值,范围 `0.0-1.0`。当前控制主链路不使用它,预留给后续夹爪集成。 - `trigger`:扳机值,范围 `0.0-1.0`。当前控制主链路不使用它,预留给后续夹爪集成。
- `pos`:手柄位置,长度 3。 - `pos`:手柄位置,长度 3。
- `quat`:手柄姿态四元数,默认按 `xyzw` 解析。 - `quat`:手柄姿态四元数,默认按 `xyzw` 解析。
- `pose_valid`姿态是否可信。ROS 接收端看到 `false` 会强制 `grip=false`
- `pose_source``pxr_predict``unity_xr``none`,用于判断姿态来自 PICO 预测接口还是 Unity XR fallback。
- `tracking_state` / `controller_status`Unity/PICO 侧追踪诊断值,只用于日志和排查。
- `grip_value``axis``buttons`PICO 端输入诊断字段,当前不会写入 `XrController` 消息。
`udp_controller_receiver` 仍兼容调试用的单手柄包:可以直接发送带 `hand``pos``quat` 的 JSON object也可以用 `controllers` list、顶层 `left/right``pose.position``position``p``q` 等常见字段。四元数默认按 `xyzw` 解析,也可通过 `quat_order:=wxyz` 切换。
PICO 4 Ultra 在 Ubuntu 22.04 下配置 Unity、构建 APK、安装到头显并向 ROS2 主机发送 UDP 的详细步骤见 [docs/pico_udp_sender_ubuntu22_setup.md](docs/pico_udp_sender_ubuntu22_setup.md)。 PICO 4 Ultra 在 Ubuntu 22.04 下配置 Unity、构建 APK、安装到头显并向 ROS2 主机发送 UDP 的详细步骤见 [docs/pico_udp_sender_ubuntu22_setup.md](docs/pico_udp_sender_ubuntu22_setup.md)。
@ -316,7 +358,7 @@ PICO 4 Ultra 在 Ubuntu 22.04 下配置 Unity、构建 APK、安装到头显并
为了达到“稳定可用的双臂 XR 遥操作/采摘平台”,建议按下面顺序推进: 为了达到“稳定可用的双臂 XR 遥操作/采摘平台”,建议按下面顺序推进:
1. 稳定 PICO 数据链路:固定 UDP JSON 协议和坐标系,增加频率、延迟、丢包统计,记录 `/xr/*_controller``/xr_rm/*/raw_target_pose``/xr_rm/*/target_pose``/xr_rm/*/target_clamped``/xr_rm/*/current_pose` 1. 稳定 PICO 数据链路:利用 `seq``source_time``pose_valid`频率、延迟、丢包和追踪状态统计,记录 `/xr/*_controller``/xr_rm/*/raw_target_pose``/xr_rm/*/target_pose``/xr_rm/*/target_clamped``/xr_rm/*/current_pose`
2. 提升真机安全性:增加启动前安全检查、软件急停 topic、UI Stop 状态提示、双臂中间区域互斥边界和速度/加速度限幅。 2. 提升真机安全性:增加启动前安全检查、软件急停 topic、UI Stop 状态提示、双臂中间区域互斥边界和速度/加速度限幅。
3. 集成末端执行器:明确夹爪 topic、力控比例、开合方向和安全上限`trigger` 从预留字段变成稳定夹爪输入。 3. 集成末端执行器:明确夹爪 topic、力控比例、开合方向和安全上限`trigger` 从预留字段变成稳定夹爪输入。
4. 接入视觉和数据记录:加入 D405/D435 相机 launch、TF、内外参和 rosbag2 实验记录。 4. 接入视觉和数据记录:加入 D405/D435 相机 launch、TF、内外参和 rosbag2 实验记录。
@ -344,10 +386,12 @@ Controller topic 没有数据:
- 确认 UDP 发送端目标 IP 是运行 ROS2 的主机 IP。 - 确认 UDP 发送端目标 IP 是运行 ROS2 的主机 IP。
- 确认端口是 `15000`,或 launch 与发送端端口一致。 - 确认端口是 `15000`,或 launch 与发送端端口一致。
-`sample_udp_sender` 在本机验证接收链路。 -`sample_udp_sender` 在本机验证接收链路。
- 如果 Unity HUD 显示某个手柄 `invalid none`ROS 侧会把该手柄 `grip` 强制置为 `false`
机械臂不动: 机械臂不动:
- 确认 `grip=true` - 确认 `grip=true`
- 确认 `udp_controller_receiver` 终端没有持续 `pose_valid=false` 日志;该字段不会写入 `XrController` 消息,但会让接收端强制停止。
- 确认 `/xr_rm/<arm>/raw_target_pose``/xr_rm/<arm>/target_pose` 是否在变化。 - 确认 `/xr_rm/<arm>/raw_target_pose``/xr_rm/<arm>/target_pose` 是否在变化。
- 确认 `/xr_rm/<arm>/target_clamped` 是否持续为 `true`,如果是,目标 TCP 可能被工作空间、圆柱半径或单帧步长限制夹住。 - 确认 `/xr_rm/<arm>/target_clamped` 是否持续为 `true`,如果是,目标 TCP 可能被工作空间、圆柱半径或单帧步长限制夹住。
- 确认真机 SDK 连接成功,且 RM75 没有报警或急停。 - 确认真机 SDK 连接成功,且 RM75 没有报警或急停。

View File

@ -5,9 +5,9 @@
目标链路: 目标链路:
```text ```text
PICO 4 Ultra 双手柄 6DoF pose / grip / trigger PICO 4 Ultra 双手柄 6DoF pose / grip / trigger / pose 诊断
-> Unity Android APK -> Unity Android APK
-> UDP JSON, 默认 192.168.9.99:15000 或当前 ROS 主机 IP:15000 -> UDP JSON, 默认当前 ROS 主机 IP:15000兜底 192.168.9.99:15000
-> xr_rm_input/udp_controller_receiver -> xr_rm_input/udp_controller_receiver
-> /xr/left_controller 与 /xr/right_controller -> /xr/left_controller 与 /xr/right_controller
``` ```
@ -19,12 +19,13 @@ PICO 4 Ultra 双手柄 6DoF pose / grip / trigger
- Unity Editor 路径:`/home/robot/Unity/Hub/Editor/2022.3.62f3c1/Editor/Unity` - Unity Editor 路径:`/home/robot/Unity/Hub/Editor/2022.3.62f3c1/Editor/Unity`
- PICO SDK 路径:`unity/PICO-Unity-Integration-SDK-release_3.4.0` - PICO SDK 路径:`unity/PICO-Unity-Integration-SDK-release_3.4.0`
- PICO package 引用:`Packages/manifest.json` 中的 `file:../../PICO-Unity-Integration-SDK-release_3.4.0` - PICO package 引用:`Packages/manifest.json` 中的 `file:../../PICO-Unity-Integration-SDK-release_3.4.0`
- Unity UI 依赖:`com.unity.textmeshpro`,运行面板使用预生成的 `Assets/Resources/Fonts/Roboto-Regular SDF.asset``Roboto-Bold SDF.asset`
- Android 包名:`com.local.xr_rm_udp_sender` - Android 包名:`com.local.xr_rm_udp_sender`
- 默认 UDP 目标:`192.168.9.99:15000` - 默认 UDP 目标:构建时优先使用 `XR_RM_UDP_TARGET_HOST`,否则自动选择本机局域网 IPv4兜底 `192.168.9.99:15000`
- 默认发送频率:`60 Hz` - 默认发送频率:`90 Hz`
- APK 输出:`unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk` - APK 输出:`unity/XR_RM_PICO_UDP_Sender/Builds/XR-RM-PICO-UDP-Sender.apk`
PICO 端建议稳定发送 `60 Hz` `90 Hz`,不要低于 `20 Hz`。ROS 遥操作节点默认 `command_timeout_sec=0.12`,超时会停止。 PICO 端建议稳定发送 `90 Hz`,网络或性能不足时可降到 `60 Hz`,不要低于 `20 Hz`。ROS 遥操作节点默认 `command_timeout_sec=0.12`,超时会停止。
## 2. Ubuntu 22.04 环境准备 ## 2. Ubuntu 22.04 环境准备
@ -164,6 +165,7 @@ XR-RM -> Build Android APK
- XR Management Android loader = PICO `PXR_Loader` - XR Management Android loader = PICO `PXR_Loader`
- `Assets/Scenes/Main.unity` - `Assets/Scenes/Main.unity`
- 场景中的 `PicoControllerUdpSender``PicoUdpConfigPanel``PicoKeepAwake` - 场景中的 `PicoControllerUdpSender``PicoUdpConfigPanel``PicoKeepAwake`
- TextMeshPro UI、Roboto SDF 字体、Saved/Favorite IP 面板、PICO predicted pose 优先读取和 Unity XR fallback
也可以使用 batchmode 构建: 也可以使用 batchmode 构建:
@ -238,8 +240,10 @@ ip -4 addr
| --- | --- | | --- | --- |
| `Target IP` | `192.168.9.99`,替换为当前 Ubuntu 主机 IP | | `Target IP` | `192.168.9.99`,替换为当前 Ubuntu 主机 IP |
| `Target Port` | `15000` | | `Target Port` | `15000` |
| `Send Rate` | `60 Hz``90 Hz` | | `Saved IP` | 最近使用或收藏的 IP可用摇杆左右切换 |
| `Coordinates` | `Project` | | `Favorite` | `ON` 表示当前 IP 已收藏 |
| `Send Rate` | 推荐 `90 Hz`,不稳定时用 `60 Hz` |
| `Coordinates` | `Project (+Z back)` |
| `UDP Sending` | `ON` | | `UDP Sending` | `ON` |
如果 Ubuntu 开启了 UFW 如果 Ubuntu 开启了 UFW
@ -254,7 +258,7 @@ sudo ufw status
面板操作: 面板操作:
- 摇杆上/下:选择行 - 摇杆上/下:选择行
- 摇杆左/右:调整端口频率、坐标模式或发送开关 - 摇杆左/右:选择 Saved IP、收藏当前 IP、调整端口/频率、切换坐标模式或发送开关
- Trigger / A / X编辑当前行或触发当前行 - Trigger / A / X编辑当前行或触发当前行
- B / Y保存并应用 - B / Y保存并应用
- Menu在配置面板和运行 HUD 之间切换 - Menu在配置面板和运行 HUD 之间切换
@ -262,9 +266,10 @@ sudo ufw status
验证现象: 验证现象:
- 面板中 `L ok / R ok` 表示 Unity 能读到左右手柄 - 面板中 `L valid pxr/unity``R valid pxr/unity` 表示 Unity 能读到对应手柄姿态;`invalid none` 表示该手柄姿态不可用
- `UDP Sending ON`ROS2 的 `/xr/left_controller``/xr/right_controller` 应持续刷新。 - `UDP Sending ON`ROS2 的 `/xr/left_controller``/xr/right_controller` 应持续刷新。
- HUD 显示包计数、追踪状态、grip 和 KeepAwake 状态。 - HUD 显示包计数、发送端点、左右姿态状态和 KeepAwake 状态。
- `pose_valid=false`ROS 接收端会强制该手柄 `grip=false`,即使 PICO 端按下了 Grip。
- 按住 `grip` 并移动手柄时mock 模式下 `/xr_rm/*/target_pose` 应连续变化,`/xr_rm/*/cmd_vel` 会显示目标位姿变化率。 - 按住 `grip` 并移动手柄时mock 模式下 `/xr_rm/*/target_pose` 应连续变化,`/xr_rm/*/cmd_vel` 会显示目标位姿变化率。
- 松开 `grip` 后,机械臂慢停,`cmd_vel` 应回到零。 - 松开 `grip` 后,机械臂慢停,`cmd_vel` 应回到零。
@ -352,9 +357,12 @@ ros2 topic echo /xr_rm/right_rm75/target_pose
如果要排除 PICO 端问题,可用本机 sample sender 验证 ROS 端: 如果要排除 PICO 端问题,可用本机 sample sender 验证 ROS 端:
```bash ```bash
ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 --seconds 20 ros2 run xr_rm_input sample_udp_sender --hand both --host 127.0.0.1 --port 15000 \
--pattern axis_sweep --seconds 60 --both-mode staggered
``` ```
`axis_sweep` 会按 `XR +X/-X/+Y/-Y/+Z/-Z` 打印方向标签。双手 `staggered` 模式先左后右,便于现场逐只确认映射。
## 11. UDP JSON 协议 ## 11. UDP JSON 协议
Unity APK 每个周期发送一个双手柄 JSON 包: Unity APK 每个周期发送一个双手柄 JSON 包:
@ -362,19 +370,47 @@ Unity APK 每个周期发送一个双手柄 JSON 包:
```json ```json
{ {
"t": 12.345, "t": 12.345,
"source_time": 12.345,
"seq": 42,
"frame_id": "xr_world", "frame_id": "xr_world",
"controllers": { "controllers": {
"left": { "left": {
"grip": true, "grip": true,
"trigger": 0.0, "trigger": 0.0,
"pos": [-0.12, 1.05, 0.30], "pos": [-0.12, 1.05, 0.30],
"quat": [0.0, 0.0, 0.0, 1.0] "quat": [0.0, 0.0, 0.0, 1.0],
"pose_valid": true,
"pose_source": "pxr_predict",
"tracking_state": 3,
"controller_status": 2,
"grip_value": 1.0,
"axis": [0.0, 0.0],
"buttons": {
"grip": true,
"primary": false,
"secondary": false,
"menu": false,
"axis_click": false
}
}, },
"right": { "right": {
"grip": true, "grip": true,
"trigger": 0.4, "trigger": 0.4,
"pos": [0.12, 1.05, 0.30], "pos": [0.12, 1.05, 0.30],
"quat": [0.0, 0.0, 0.0, 1.0] "quat": [0.0, 0.0, 0.0, 1.0],
"pose_valid": true,
"pose_source": "unity_xr",
"tracking_state": 3,
"controller_status": -1,
"grip_value": 0.8,
"axis": [0.0, 0.0],
"buttons": {
"grip": true,
"primary": false,
"secondary": false,
"menu": false,
"axis_click": false
}
} }
} }
} }
@ -385,6 +421,8 @@ Unity APK 每个周期发送一个双手柄 JSON 包:
| 字段 | 类型 | 说明 | | 字段 | 类型 | 说明 |
| --- | --- | --- | | --- | --- | --- |
| `t` | number | `Time.realtimeSinceStartupAsDouble` | | `t` | number | `Time.realtimeSinceStartupAsDouble` |
| `source_time` | number | 当前同 `t`,预留给延迟统计 |
| `seq` | number | 发送端递增包序号,预留给丢包统计 |
| `frame_id` | string | 默认 `xr_world` | | `frame_id` | string | 默认 `xr_world` |
| `controllers.left` | object | 左手柄 | | `controllers.left` | object | 左手柄 |
| `controllers.right` | object | 右手柄 | | `controllers.right` | object | 右手柄 |
@ -392,25 +430,31 @@ Unity APK 每个周期发送一个双手柄 JSON 包:
| `trigger` | float | `0.0-1.0` | | `trigger` | float | `0.0-1.0` |
| `pos` | float[3] | `[x, y, z]` | | `pos` | float[3] | `[x, y, z]` |
| `quat` | float[4] | `[qx, qy, qz, qw]` | | `quat` | float[4] | `[qx, qy, qz, qw]` |
| `pose_valid` | bool | 姿态有效性;`false` 时 ROS receiver 强制 `grip=false` |
| `pose_source` | string | `pxr_predict``unity_xr``none` |
| `tracking_state` | int | Unity XR tracking state 原始值 |
| `controller_status` | int | PICO controller status 原始值Unity XR fallback 时为 `-1` |
| `grip_value` | float | Grip 模拟量,范围 `0.0-1.0` |
| `axis` | float[2] | 主摇杆二维轴 |
| `buttons` | object | `grip``primary``secondary``menu``axis_click` |
坐标转换应与当前工程保持一致 当前工程的 `Project (+Z back)` 坐标是 ROS 侧使用的统一项目坐标
```csharp ```csharp
payload.pos[0] = position.x; project.x = native.z;
payload.pos[1] = position.y; project.y = native.y;
payload.pos[2] = -position.z; project.z = -native.x;
payload.quat[0] = -rotation.x;
payload.quat[1] = -rotation.y;
payload.quat[2] = rotation.z;
payload.quat[3] = rotation.w;
``` ```
这条转换只用于 `pose_source=pxr_predict``Coordinates=Project (+Z back)` 的情况。`Source raw` 模式保留 PXR native 或 Unity XR fallback 的原始坐标,便于现场对照;`unity_xr` fallback 暂不做 PXR native 转换,避免把已经由 Unity XR 提供的坐标二次转换。
禁用发送、App 暂停或退出时,必须额外发送一次左右手柄 `grip=false`,让 ROS 侧安全停止。 禁用发送、App 暂停或退出时,必须额外发送一次左右手柄 `grip=false`,让 ROS 侧安全停止。
`udp_controller_receiver` 仍兼容调试脚本的旧格式:单个 object 可以直接带 `hand``pos``quat``controllers` 可以是 object 或 list位置字段也支持 `position``p``pose.position`,姿态字段支持 `orientation``q`。四元数默认按 `xyzw` 解析,也可用 `quat_order:=wxyz` 覆盖。
## 12. 坐标方向检查 ## 12. 坐标方向检查
当前配置使用 PICO/OpenXR 位置坐标: 当前 Project 输出使用下面的位置坐标:
```text ```text
+X: 向右 +X: 向右
@ -418,6 +462,8 @@ payload.quat[3] = rotation.w;
+Z: 向后 +Z: 向后
``` ```
PXR `pxr_predict` 原始坐标按现场实测通常表现为右移 `native.z+`、前伸 `native.x+`,所以 Unity 在 Project 模式中会输出 `project.x=native.z``project.z=-native.x`。如果切到 `Source raw`,应能看到原始 PXR 现象,用于确认当前到底是 PXR native 问题还是 ROS 侧映射问题。
双臂配置中的映射关系: 双臂配置中的映射关系:
```text ```text
@ -428,12 +474,14 @@ payload.quat[3] = rotation.w;
建议现场按下面顺序验证: 建议现场按下面顺序验证:
1. 只启动 `use_mock:=true` 1. 只启动 `use_mock:=true`
2. 按住左手 `grip`,沿 PICO 的 `+X/-X``+Y/-Y``+Z/-Z` 每次只动一个 2. APK 中选择 `Project (+Z back)`,按住左手 `grip`,每次只沿右/左、上/下、前/后移动一个方向
3. 记录 `/xr/left_controller.pose.position` 的变化方向。 3. 记录 `/xr/left_controller.pose.position` 的变化方向。
4. 记录 `/xr_rm/left_rm75/target_pose` 的方向。 4. 记录 `/xr_rm/left_rm75/target_pose` 的方向。
5. 右手重复。 5. 右手重复。
如果两个手柄在 ROS topic 里的某个轴都反了,优先检查 Unity 的坐标转换。如果 ROS topic 正确,但某一只机械臂运动方向不符合现场坐标,只改对应 YAML 的 `xr_to_robot_matrix` 如果两个手柄在 ROS topic 里的某个轴都反了,优先检查 Unity/PICO 发送到 `/xr/*_controller.pose.position` 的实际方向。如果 ROS topic 正确,但某一只机械臂运动方向不符合现场坐标,只改对应 YAML 的 `xr_to_robot_matrix`
`Coordinates=Source raw` 只用于诊断,不建议用于正式遥操作。现场方向修正优先通过 ROS 侧 topic 观察定位:如果 `/xr/*_controller.pose.position` 已经符合 Project 坐标约定,就不要在 Unity 中继续临时改符号。
## 13. 真机前置检查 ## 13. 真机前置检查
@ -468,13 +516,13 @@ ros2 launch xr_rm_bringup arm_debug.launch.py arm:=both use_mock:=false \
| Unity 找不到 PICO package | 检查 `Packages/manifest.json``../../PICO-Unity-Integration-SDK-release_3.4.0` 路径 | | Unity 找不到 PICO package | 检查 `Packages/manifest.json``../../PICO-Unity-Integration-SDK-release_3.4.0` 路径 |
| Gradle 或 Android 构建失败 | 检查 Android Build Support、SDK/NDK/OpenJDK、IL2CPP、ARM64、Android Build Target 和 PICO SDK 编译错误 | | Gradle 或 Android 构建失败 | 检查 Android Build Support、SDK/NDK/OpenJDK、IL2CPP、ARM64、Android Build Target 和 PICO SDK 编译错误 |
| PICO 安装失败或 `unauthorized` | 重新授权 USB 调试,执行 `adb kill-server && adb start-server && adb devices` | | PICO 安装失败或 `unauthorized` | 重新授权 USB 调试,执行 `adb kill-server && adb start-server && adb devices` |
| ROS2 收不到 UDP | 检查同一 Wi-Fi、Target IP、Target Port、UFW、`UDP Sending ON``L ok / R ok` | | ROS2 收不到 UDP | 检查同一 Wi-Fi、Target IP、Target Port、UFW、`UDP Sending ON`左右手柄是否 `valid pxr/unity` |
| tcpdump 有包但 topic 没数据 | JSON 字段不对,确认包含 `controllers.left/right`,且 `pos` 长度 3、`quat` 长度 4 | | tcpdump 有包但 topic 没数据 | JSON 字段不对,确认包含 `controllers.left/right`,且 `pos` 长度 3、`quat` 长度 4 |
| 有 topic 但机械臂不动 | 检查 `grip`、teleop 节点订阅话题、工作空间限幅、UDP 超时 | | 有 topic 但机械臂不动 | 检查 `grip``pose_valid` 日志、teleop 节点订阅话题、工作空间限幅、UDP 超时 |
| 按左手右臂动 | PICO 端 left/right 获取或填充反了 | | 按左手右臂动 | PICO 端 left/right 获取或填充反了 |
| 松开 grip 后仍有速度 | 确认 PICO 持续发送 `grip=false`,并检查 teleop 超时停止 | | 松开 grip 后仍有速度 | 确认 PICO 持续发送 `grip=false`,并检查 teleop 超时停止 |
| 经常超时 | 发送频率太低、网络丢包、PICO 应用后台暂停;提高 `sendHz` 并保持应用前台运行 | | 经常超时 | 发送频率太低、网络丢包、PICO 应用后台暂停;提高 `sendHz` 并保持应用前台运行 |
| 前后方向反了 | 先切换 Unity 坐标转换,再验证 ROS topic | | 前后方向反了 | 先`/xr/*_controller.pose.position` 是否符合 `+Z` 向后topic 正确时修改对应 YAML 的 `xr_to_robot_matrix` |
| 某一只臂方向反了 | 修改对应 YAML 的 `xr_to_robot_matrix` | | 某一只臂方向反了 | 修改对应 YAML 的 `xr_to_robot_matrix` |
## 15. 参考链接 ## 15. 参考链接

View File

@ -0,0 +1,260 @@
#if UNITY_EDITOR
using System;
using System.IO;
using System.Text;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.TextCore.LowLevel;
public static class XrRmTmpFontAssetBuilder
{
private const string ResourcesDirectory = "Assets/Resources";
private const string FontDirectory = "Assets/Resources/Fonts";
private const string RegularFontPath = FontDirectory + "/Roboto-Regular.ttf";
private const string BoldFontPath = FontDirectory + "/Roboto-Bold.ttf";
private const string RegularAssetPath = FontDirectory + "/Roboto-Regular SDF.asset";
private const string BoldAssetPath = FontDirectory + "/Roboto-Bold SDF.asset";
private const string PreferredTmpSettingsPath = ResourcesDirectory + "/TMP Settings.asset";
private const string StandardTmpSettingsPath = "Assets/TextMesh Pro/Resources/TMP Settings.asset";
private const int SamplingPointSize = 90;
private const int AtlasPadding = 9;
private const int AtlasSize = 1024;
private const string MobileSdfShaderName = "TextMeshPro/Mobile/Distance Field";
[MenuItem("XR-RM/Rebuild Roboto TMP Font Assets")]
public static void RebuildRobotoFontAssets()
{
EnsureRobotoFontAssets(true);
}
public static void EnsureRobotoFontAssets(bool forceRebuild = false)
{
Directory.CreateDirectory(ResourcesDirectory);
Directory.CreateDirectory(FontDirectory);
EnsureTmpEssentialResources();
TMP_FontAsset regularFontAsset = EnsureFontAsset(
RegularFontPath,
RegularAssetPath,
"Roboto-Regular SDF",
forceRebuild);
TMP_FontAsset boldFontAsset = EnsureFontAsset(
BoldFontPath,
BoldAssetPath,
"Roboto-Bold SDF",
forceRebuild);
EnsureTmpSettings(regularFontAsset, boldFontAsset);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log("XR-RM Roboto TMP font assets are ready.");
}
private static TMP_FontAsset EnsureFontAsset(string fontPath, string assetPath, string assetName, bool forceRebuild)
{
if (!forceRebuild)
{
TMP_FontAsset existing = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(assetPath);
if (existing != null)
{
return existing;
}
}
if (!File.Exists(fontPath))
{
throw new FileNotFoundException("Roboto font file is missing.", fontPath);
}
AssetDatabase.ImportAsset(fontPath, ImportAssetOptions.ForceSynchronousImport);
Font sourceFont = AssetDatabase.LoadAssetAtPath<Font>(fontPath);
if (sourceFont == null)
{
throw new InvalidOperationException("Unity could not import Roboto font: " + fontPath);
}
if (File.Exists(assetPath))
{
AssetDatabase.DeleteAsset(assetPath);
}
TMP_FontAsset fontAsset = TMP_FontAsset.CreateFontAsset(
sourceFont,
SamplingPointSize,
AtlasPadding,
GlyphRenderMode.SDFAA,
AtlasSize,
AtlasSize,
AtlasPopulationMode.Dynamic,
false);
if (fontAsset == null)
{
throw new InvalidOperationException("TextMesh Pro could not create font asset from: " + fontPath);
}
fontAsset.name = assetName;
fontAsset.atlasTextures[0].name = assetName + " Atlas";
fontAsset.material.name = assetName + " Atlas Material";
if (!fontAsset.TryAddCharacters(BuildAsciiCharacterSet(), out string missingCharacters) ||
!string.IsNullOrEmpty(missingCharacters))
{
throw new InvalidOperationException(assetName + " is missing ASCII glyphs: " + missingCharacters);
}
fontAsset.atlasPopulationMode = AtlasPopulationMode.Static;
fontAsset.ReadFontAssetDefinition();
Texture2D atlasTexture = fontAsset.atlasTexture;
Material atlasMaterial = fontAsset.material;
AssetDatabase.CreateAsset(fontAsset, assetPath);
if (atlasTexture != null)
{
AssetDatabase.AddObjectToAsset(atlasTexture, fontAsset);
}
if (atlasMaterial != null)
{
AssetDatabase.AddObjectToAsset(atlasMaterial, fontAsset);
}
EditorUtility.SetDirty(fontAsset);
return fontAsset;
}
private static void EnsureTmpSettings(TMP_FontAsset regularFontAsset, TMP_FontAsset fallbackFontAsset)
{
string settingsPath = ResolveTmpSettingsPath();
TMP_Settings settings = AssetDatabase.LoadAssetAtPath<TMP_Settings>(settingsPath);
if (settings == null)
{
settings = ScriptableObject.CreateInstance<TMP_Settings>();
Directory.CreateDirectory(Path.GetDirectoryName(settingsPath));
AssetDatabase.CreateAsset(settings, settingsPath);
}
SerializedObject serializedSettings = new SerializedObject(settings);
SetObject(serializedSettings, "m_defaultFontAsset", regularFontAsset);
SetString(serializedSettings, "m_defaultFontAssetPath", "Fonts/");
SetFloat(serializedSettings, "m_defaultFontSize", 36.0f);
SetFloat(serializedSettings, "m_defaultAutoSizeMinRatio", 0.5f);
SetFloat(serializedSettings, "m_defaultAutoSizeMaxRatio", 1.0f);
SetBool(serializedSettings, "m_enableWordWrapping", false);
SetBool(serializedSettings, "m_enableKerning", true);
SetBool(serializedSettings, "m_EnableRaycastTarget", false);
SetObjectArray(serializedSettings, "m_fallbackFontAssets", fallbackFontAsset);
serializedSettings.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(settings);
}
private static void EnsureTmpEssentialResources()
{
if (Shader.Find(MobileSdfShaderName) != null)
{
return;
}
UnityEditor.PackageManager.PackageInfo packageInfo =
UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(TMP_Text).Assembly);
if (packageInfo == null)
{
throw new InvalidOperationException("Could not locate the TextMesh Pro package.");
}
string packagePath = Path.GetFullPath(Path.Combine(
packageInfo.resolvedPath,
"Package Resources/TMP Essential Resources.unitypackage"));
if (!File.Exists(packagePath))
{
throw new FileNotFoundException("TMP Essential Resources package is missing.", packagePath);
}
Debug.Log("XR-RM importing TMP Essential Resources: " + packagePath);
AssetDatabase.ImportPackage(packagePath, false);
AssetDatabase.Refresh();
if (Shader.Find(MobileSdfShaderName) == null)
{
throw new InvalidOperationException("TMP Essential Resources did not import the shader: " + MobileSdfShaderName);
}
}
private static string ResolveTmpSettingsPath()
{
if (File.Exists(PreferredTmpSettingsPath))
{
return PreferredTmpSettingsPath;
}
if (File.Exists(StandardTmpSettingsPath))
{
return StandardTmpSettingsPath;
}
return PreferredTmpSettingsPath;
}
private static void SetObject(SerializedObject serializedObject, string propertyName, UnityEngine.Object value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property != null)
{
property.objectReferenceValue = value;
}
}
private static void SetObjectArray(SerializedObject serializedObject, string propertyName, UnityEngine.Object value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property == null)
{
return;
}
property.arraySize = value == null ? 0 : 1;
if (value != null)
{
property.GetArrayElementAtIndex(0).objectReferenceValue = value;
}
}
private static void SetString(SerializedObject serializedObject, string propertyName, string value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property != null)
{
property.stringValue = value;
}
}
private static void SetFloat(SerializedObject serializedObject, string propertyName, float value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property != null)
{
property.floatValue = value;
}
}
private static void SetBool(SerializedObject serializedObject, string propertyName, bool value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property != null)
{
property.boolValue = value;
}
}
private static string BuildAsciiCharacterSet()
{
StringBuilder builder = new StringBuilder(95);
for (char character = ' '; character <= '~'; character++)
{
builder.Append(character);
}
return builder.ToString();
}
}
#endif

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 920e39429cd12b700a4f933dc98ab844
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,10 +1,8 @@
#if UNITY_EDITOR #if UNITY_EDITOR
using System; using System;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets; using System.Net.Sockets;
using UnityEditor.Build; using UnityEditor.Build;
using UnityEditor.Build.Reporting; using UnityEditor.Build.Reporting;
@ -21,18 +19,25 @@ using Unity.XR.PXR;
public static class XrRmUdpSenderProjectSetup public static class XrRmUdpSenderProjectSetup
{ {
private const string ScenePath = "Assets/Scenes/Main.unity"; private const string ScenePath = "Assets/Scenes/Main.unity";
private const string PicoProjectSettingPath = "Assets/Resources/PXR_ProjectSetting.asset";
private const string PackageName = "com.local.xr_rm_udp_sender"; private const string PackageName = "com.local.xr_rm_udp_sender";
private const string ProductName = "XR-RM-PICO-UDP-Sender"; private const string ProductName = "XR-RM-PICO-UDP-Sender";
private const string VersionName = "1.0";
private const string DefaultTargetHost = "192.168.9.99"; private const string DefaultTargetHost = "192.168.9.99";
private const string TargetHostEnvVar = "XR_RM_UDP_TARGET_HOST"; private const string TargetHostEnvVar = "XR_RM_UDP_TARGET_HOST";
private const int MaxAndroidVersionCode = 2100000000;
private static readonly DateTime BuildVersionEpochUtc = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
[MenuItem("XR-RM/Apply UDP Sender Android Settings")] [MenuItem("XR-RM/Apply UDP Sender Android Settings")]
public static void ApplyAndroidSettings() public static void ApplyAndroidSettings()
{ {
PlayerSettings.companyName = "XR-RM";
PlayerSettings.productName = ProductName; PlayerSettings.productName = ProductName;
PlayerSettings.bundleVersion = VersionName;
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, PackageName); PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, PackageName);
PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel29; PlayerSettings.Android.minSdkVersion = AndroidSdkVersions.AndroidApiLevel29;
PlayerSettings.Android.targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto; PlayerSettings.Android.targetSdkVersion = AndroidSdkVersions.AndroidApiLevelAuto;
PlayerSettings.Android.preferredInstallLocation = AndroidPreferredInstallLocation.ForceInternal;
PlayerSettings.Android.forceInternetPermission = true; PlayerSettings.Android.forceInternetPermission = true;
PlayerSettings.SetScriptingBackend(NamedBuildTarget.Android, ScriptingImplementation.IL2CPP); PlayerSettings.SetScriptingBackend(NamedBuildTarget.Android, ScriptingImplementation.IL2CPP);
PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64; PlayerSettings.Android.targetArchitectures = AndroidArchitecture.ARM64;
@ -49,7 +54,9 @@ public static class XrRmUdpSenderProjectSetup
EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Android; EditorUserBuildSettings.selectedBuildTargetGroup = BuildTargetGroup.Android;
EnsurePicoSettings(); EnsurePicoSettings();
NormalizePicoProjectSettings();
EnablePicoLoader(); EnablePicoLoader();
XrRmTmpFontAssetBuilder.EnsureRobotoFontAssets();
CreateOrRefreshScene(); CreateOrRefreshScene();
EditorBuildSettings.scenes = new[] { new EditorBuildSettingsScene(ScenePath, true) }; EditorBuildSettings.scenes = new[] { new EditorBuildSettingsScene(ScenePath, true) };
AssetDatabase.SaveAssets(); AssetDatabase.SaveAssets();
@ -77,7 +84,7 @@ public static class XrRmUdpSenderProjectSetup
component = sender.AddComponent<PicoControllerUdpSender>(); component = sender.AddComponent<PicoControllerUdpSender>();
} }
string targetHost = ResolveTargetHost(); string targetHost = ResolveTargetHost(component.host);
component.host = targetHost; component.host = targetHost;
component.port = 15000; component.port = 15000;
component.sendHz = 90.0f; component.sendHz = 90.0f;
@ -131,6 +138,7 @@ public static class XrRmUdpSenderProjectSetup
public static void BuildAndroidApk() public static void BuildAndroidApk()
{ {
ApplyAndroidSettings(); ApplyAndroidSettings();
BumpAndroidVersionCodeForInstall();
Directory.CreateDirectory("Builds"); Directory.CreateDirectory("Builds");
BuildPlayerOptions options = new BuildPlayerOptions BuildPlayerOptions options = new BuildPlayerOptions
@ -152,6 +160,54 @@ public static class XrRmUdpSenderProjectSetup
Debug.Log($"Android APK built: {options.locationPathName}"); Debug.Log($"Android APK built: {options.locationPathName}");
} }
private static void BumpAndroidVersionCodeForInstall()
{
long elapsedMinutes = (long)(DateTime.UtcNow - BuildVersionEpochUtc).TotalMinutes;
long currentVersionCode = Math.Max(0, PlayerSettings.Android.bundleVersionCode);
long nextVersionCode = Math.Max(currentVersionCode + 1, elapsedMinutes);
if (nextVersionCode > MaxAndroidVersionCode)
{
throw new InvalidOperationException(
$"Android versionCode {nextVersionCode} exceeds safe install limit {MaxAndroidVersionCode}.");
}
PlayerSettings.Android.bundleVersionCode = (int)nextVersionCode;
Debug.Log($"XR-RM Android versionCode set to {nextVersionCode} for installable APK builds.");
}
private static void NormalizePicoProjectSettings()
{
UnityEngine.Object projectSetting = AssetDatabase.LoadMainAssetAtPath(PicoProjectSettingPath);
if (projectSetting == null)
{
return;
}
SerializedObject serializedSetting = new SerializedObject(projectSetting);
SetBoolOrInt(serializedSetting, "portalInited", false);
serializedSetting.ApplyModifiedPropertiesWithoutUndo();
EditorUtility.SetDirty(projectSetting);
}
private static void SetBoolOrInt(SerializedObject serializedObject, string propertyName, bool value)
{
SerializedProperty property = serializedObject.FindProperty(propertyName);
if (property == null)
{
return;
}
if (property.propertyType == SerializedPropertyType.Boolean)
{
property.boolValue = value;
}
else if (property.propertyType == SerializedPropertyType.Integer)
{
property.intValue = value ? 1 : 0;
}
}
private static void EnablePicoLoader() private static void EnablePicoLoader()
{ {
XRGeneralSettingsPerBuildTarget buildTargetSettings = AssetDatabase.FindAssets("t:XRGeneralSettingsPerBuildTarget") XRGeneralSettingsPerBuildTarget buildTargetSettings = AssetDatabase.FindAssets("t:XRGeneralSettingsPerBuildTarget")
@ -196,7 +252,7 @@ public static class XrRmUdpSenderProjectSetup
EditorUtility.SetDirty(buildTargetSettings); EditorUtility.SetDirty(buildTargetSettings);
} }
private static string ResolveTargetHost() private static string ResolveTargetHost(string existingHost)
{ {
string envHost = Environment.GetEnvironmentVariable(TargetHostEnvVar); string envHost = Environment.GetEnvironmentVariable(TargetHostEnvVar);
if (IsUsableTargetHost(envHost)) if (IsUsableTargetHost(envHost))
@ -204,40 +260,12 @@ public static class XrRmUdpSenderProjectSetup
return envHost.Trim(); return envHost.Trim();
} }
string discoveredHost = GetPreferredLocalIPv4Address(); if (IsUsableTargetHost(existingHost))
return string.IsNullOrEmpty(discoveredHost) ? DefaultTargetHost : discoveredHost; {
return existingHost.Trim();
} }
private static string GetPreferredLocalIPv4Address() return DefaultTargetHost;
{
List<string> candidates = new List<string>();
foreach (NetworkInterface networkInterface in NetworkInterface.GetAllNetworkInterfaces())
{
if (networkInterface.OperationalStatus != OperationalStatus.Up)
{
continue;
}
if (networkInterface.NetworkInterfaceType == NetworkInterfaceType.Loopback ||
networkInterface.NetworkInterfaceType == NetworkInterfaceType.Tunnel)
{
continue;
}
foreach (UnicastIPAddressInformation addressInfo in networkInterface.GetIPProperties().UnicastAddresses)
{
string address = addressInfo.Address.ToString();
if (IsUsableTargetHost(address))
{
candidates.Add(address);
}
}
}
return candidates
.OrderByDescending(GetAddressScore)
.ThenBy(address => address, StringComparer.Ordinal)
.FirstOrDefault();
} }
private static bool IsUsableTargetHost(string value) private static bool IsUsableTargetHost(string value)
@ -256,37 +284,5 @@ public static class XrRmUdpSenderProjectSetup
return !loopback && !linkLocal && !unspecified; return !loopback && !linkLocal && !unspecified;
} }
private static int GetAddressScore(string address)
{
string[] parts = address.Split('.');
if (parts.Length != 4 ||
!int.TryParse(parts[0], out int first) ||
!int.TryParse(parts[1], out int second))
{
return 0;
}
if (first == 192 && second == 168)
{
return 50;
}
if (first == 10)
{
return 40;
}
if (first == 172 && second >= 16 && second <= 31)
{
return 35;
}
if (first == 198 && (second == 18 || second == 19))
{
return 5;
}
return 10;
}
} }
#endif #endif

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 94df55e59529ba0f495c83a2e46c2cb6
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9605d99cce67df46ea0e78c679b423e9
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: c8a92aff4ec89686dbbebe474b26d3d6
TrueTypeFontImporter:
externalObjects: {}
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontNames:
- Roboto
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
ascentCalculationMode: 1
useLegacyBoundsCalculation: 0
shouldRoundAdvanceValue: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 563e0a689f15a82a692f51982090bbf0
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d38777a8d8b70d68788b4c07547a8aca
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,21 @@
fileFormatVersion: 2
guid: b58718e8be5f47e71bf35d8503ed1ff8
TrueTypeFontImporter:
externalObjects: {}
serializedVersion: 4
fontSize: 16
forceTextureCase: -2
characterSpacing: 0
characterPadding: 1
includeFontData: 1
fontNames:
- Roboto
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
ascentCalculationMode: 1
useLegacyBoundsCalculation: 0
shouldRoundAdvanceValue: 1
userData:
assetBundleName:
assetBundleVariant:

View File

@ -50,6 +50,6 @@ MonoBehaviour:
recommendMSAA: 0 recommendMSAA: 0
validationFFREnabled: 0 validationFFREnabled: 0
validationETFREnabled: 0 validationETFREnabled: 0
portalInited: 1 portalInited: 0
isDataCollectionDisabled: 0 isDataCollectionDisabled: 0
portalFirstSelected: 0 portalFirstSelected: 0

View File

@ -168,7 +168,7 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: e4c7a11d85854eddb40a6a614bfb129c, type: 3} m_Script: {fileID: 11500000, guid: e4c7a11d85854eddb40a6a614bfb129c, type: 3}
m_Name: m_Name:
m_EditorClassIdentifier: m_EditorClassIdentifier:
host: 192.168.9.92 host: 192.168.43.7
port: 15000 port: 15000
sendHz: 90 sendHz: 90
convertUnityToProjectCoordinates: 1 convertUnityToProjectCoordinates: 1

View File

@ -5,6 +5,9 @@ using System.Net.Sockets;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.XR; using UnityEngine.XR;
#if !PICO_OPENXR_SDK
using Unity.XR.PXR;
#endif
public class PicoControllerUdpSender : MonoBehaviour public class PicoControllerUdpSender : MonoBehaviour
{ {
@ -12,7 +15,12 @@ public class PicoControllerUdpSender : MonoBehaviour
private const string PortPrefsKey = "xr_rm_udp_sender.port"; private const string PortPrefsKey = "xr_rm_udp_sender.port";
private const string SendHzPrefsKey = "xr_rm_udp_sender.send_hz"; private const string SendHzPrefsKey = "xr_rm_udp_sender.send_hz";
private const string ConvertPrefsKey = "xr_rm_udp_sender.convert_coordinates"; private const string ConvertPrefsKey = "xr_rm_udp_sender.convert_coordinates";
private const int MaxPacketsPerFrame = 4; private const string PoseSourceNone = "none";
private const string PoseSourceUnityXr = "unity_xr";
private const string PoseSourcePxrPredict = "pxr_predict";
private const int InvalidTrackingState = -1;
private const int UnknownControllerStatus = -1;
private const double SendScheduleEpsilonSeconds = 0.0001;
[Header("ROS UDP Target")] [Header("ROS UDP Target")]
public string host = "192.168.9.99"; public string host = "192.168.9.99";
@ -25,15 +33,24 @@ public class PicoControllerUdpSender : MonoBehaviour
private UdpClient client; private UdpClient client;
private IPEndPoint endPoint; private IPEndPoint endPoint;
private float nextSendTime; private double nextSendTime;
private bool sendEnabled; private bool sendEnabled;
private readonly Packet packet = new Packet(); private readonly Packet packet = new Packet();
private readonly List<InputDevice> deviceBuffer = new List<InputDevice>(); private readonly List<InputDevice> deviceBuffer = new List<InputDevice>();
private int nextSeq = 1;
public bool SendEnabled => sendEnabled; public bool SendEnabled => sendEnabled;
public bool HasEndpoint => endPoint != null; public bool HasEndpoint => endPoint != null;
public bool LeftDeviceValid { get; private set; } public bool LeftDeviceValid { get; private set; }
public bool RightDeviceValid { get; private set; } public bool RightDeviceValid { get; private set; }
public bool LeftPoseValid { get; private set; }
public bool RightPoseValid { get; private set; }
public string LeftPoseSource { get; private set; } = PoseSourceNone;
public string RightPoseSource { get; private set; } = PoseSourceNone;
public int LeftTrackingState { get; private set; } = InvalidTrackingState;
public int RightTrackingState { get; private set; } = InvalidTrackingState;
public int LeftControllerStatus { get; private set; } = UnknownControllerStatus;
public int RightControllerStatus { get; private set; } = UnknownControllerStatus;
public bool LeftGripPressed { get; private set; } public bool LeftGripPressed { get; private set; }
public bool RightGripPressed { get; private set; } public bool RightGripPressed { get; private set; }
public string LastError { get; private set; } = string.Empty; public string LastError { get; private set; } = string.Empty;
@ -44,6 +61,8 @@ public class PicoControllerUdpSender : MonoBehaviour
private class Packet private class Packet
{ {
public double t; public double t;
public double source_time;
public int seq;
public string frame_id = "xr_world"; public string frame_id = "xr_world";
public Controllers controllers = new Controllers(); public Controllers controllers = new Controllers();
} }
@ -62,6 +81,23 @@ public class PicoControllerUdpSender : MonoBehaviour
public float trigger; public float trigger;
public float[] pos = new float[] { 0.0f, 1.0f, 0.0f }; public float[] pos = new float[] { 0.0f, 1.0f, 0.0f };
public float[] quat = new float[] { 0.0f, 0.0f, 0.0f, 1.0f }; public float[] quat = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
public bool pose_valid;
public int tracking_state = -1;
public int controller_status = -1;
public float grip_value;
public float[] axis = new float[] { 0.0f, 0.0f };
public ButtonsPayload buttons = new ButtonsPayload();
public string pose_source = PoseSourceNone;
}
[Serializable]
private class ButtonsPayload
{
public bool grip;
public bool primary;
public bool secondary;
public bool menu;
public bool axis_click;
} }
private void OnEnable() private void OnEnable()
@ -69,7 +105,7 @@ public class PicoControllerUdpSender : MonoBehaviour
LoadConfig(); LoadConfig();
ReopenClient(); ReopenClient();
sendEnabled = sendOnStart; sendEnabled = sendOnStart;
nextSendTime = 0.0f; nextSendTime = 0.0;
} }
private void OnDisable() private void OnDisable()
@ -86,7 +122,7 @@ public class PicoControllerUdpSender : MonoBehaviour
return; return;
} }
nextSendTime = 0.0f; nextSendTime = 0.0;
} }
private void OnApplicationFocus(bool hasFocus) private void OnApplicationFocus(bool hasFocus)
@ -97,7 +133,7 @@ public class PicoControllerUdpSender : MonoBehaviour
return; return;
} }
nextSendTime = 0.0f; nextSendTime = 0.0;
} }
private void Update() private void Update()
@ -110,21 +146,21 @@ public class PicoControllerUdpSender : MonoBehaviour
FillController(XRNode.LeftHand, packet.controllers.left); FillController(XRNode.LeftHand, packet.controllers.left);
FillController(XRNode.RightHand, packet.controllers.right); FillController(XRNode.RightHand, packet.controllers.right);
float now = Time.unscaledTime; double now = Time.realtimeSinceStartupAsDouble;
float interval = 1.0f / Mathf.Max(sendHz, 1.0f); double interval = 1.0 / Math.Max((double)sendHz, 1.0);
if (nextSendTime <= 0.0f || now - nextSendTime > interval * MaxPacketsPerFrame) if (nextSendTime <= 0.0 || now - nextSendTime > interval)
{ {
nextSendTime = now; nextSendTime = now;
} }
int sentThisFrame = 0; if (now + SendScheduleEpsilonSeconds < nextSendTime)
while (now >= nextSendTime && sentThisFrame < MaxPacketsPerFrame)
{ {
packet.t = Time.realtimeSinceStartupAsDouble; return;
}
StampPacket();
SendPacket(); SendPacket();
nextSendTime += interval; nextSendTime += interval;
sentThisFrame++;
}
} }
public bool ApplyConfig(string newHost, int newPort, float newSendHz, bool newConvertUnityToProjectCoordinates, bool save) public bool ApplyConfig(string newHost, int newPort, float newSendHz, bool newConvertUnityToProjectCoordinates, bool save)
@ -169,7 +205,7 @@ public class PicoControllerUdpSender : MonoBehaviour
} }
sendEnabled = enabled; sendEnabled = enabled;
nextSendTime = 0.0f; nextSendTime = 0.0;
} }
private void LoadConfig() private void LoadConfig()
@ -228,6 +264,8 @@ public class PicoControllerUdpSender : MonoBehaviour
{ {
InputDevice device = GetControllerDevice(node); InputDevice device = GetControllerDevice(node);
bool isLeft = node == XRNode.LeftHand; bool isLeft = node == XRNode.LeftHand;
ResetControllerPayload(payload);
if (isLeft) if (isLeft)
{ {
LeftDeviceValid = device.isValid; LeftDeviceValid = device.isValid;
@ -239,40 +277,111 @@ public class PicoControllerUdpSender : MonoBehaviour
if (!device.isValid) if (!device.isValid)
{ {
payload.grip = false;
payload.trigger = 0.0f;
SetGripState(isLeft, false); SetGripState(isLeft, false);
SetPoseState(isLeft, false, PoseSourceNone, InvalidTrackingState, UnknownControllerStatus);
return; return;
} }
Vector3 position = Vector3.zero; Vector3 unityPosition = Vector3.zero;
Quaternion rotation = Quaternion.identity; Quaternion unityRotation = Quaternion.identity;
float trigger = 0.0f; float trigger = 0.0f;
float gripValue = 0.0f; float gripValue = 0.0f;
Vector2 axis = Vector2.zero;
bool gripButton = false; bool gripButton = false;
bool primaryButton = false;
bool secondaryButton = false;
bool menuButton = false;
bool axisClick = false;
bool isTracked = true;
InputTrackingState trackingState = 0;
device.TryGetFeatureValue(CommonUsages.devicePosition, out position); bool hasUnityPosition = device.TryGetFeatureValue(CommonUsages.devicePosition, out unityPosition);
device.TryGetFeatureValue(CommonUsages.deviceRotation, out rotation); bool hasUnityRotation = device.TryGetFeatureValue(CommonUsages.deviceRotation, out unityRotation);
device.TryGetFeatureValue(CommonUsages.trigger, out trigger); device.TryGetFeatureValue(CommonUsages.trigger, out trigger);
device.TryGetFeatureValue(CommonUsages.grip, out gripValue); device.TryGetFeatureValue(CommonUsages.grip, out gripValue);
device.TryGetFeatureValue(CommonUsages.gripButton, out gripButton); device.TryGetFeatureValue(CommonUsages.gripButton, out gripButton);
device.TryGetFeatureValue(CommonUsages.primary2DAxis, out axis);
device.TryGetFeatureValue(CommonUsages.primary2DAxisClick, out axisClick);
device.TryGetFeatureValue(CommonUsages.primaryButton, out primaryButton);
device.TryGetFeatureValue(CommonUsages.secondaryButton, out secondaryButton);
device.TryGetFeatureValue(CommonUsages.menuButton, out menuButton);
payload.grip = gripButton || gripValue > 0.5f; bool hasIsTracked = device.TryGetFeatureValue(CommonUsages.isTracked, out isTracked);
SetGripState(isLeft, payload.grip); bool hasTrackingState = device.TryGetFeatureValue(CommonUsages.trackingState, out trackingState);
payload.trigger = Mathf.Clamp01(trigger); int trackingStateValue = hasTrackingState ? (int)trackingState : InvalidTrackingState;
bool trackedOk = !hasIsTracked || isTracked;
bool trackingStateOk = !hasTrackingState
|| ((trackingState & InputTrackingState.Position) != 0
&& (trackingState & InputTrackingState.Rotation) != 0);
bool unityPoseValid = hasUnityPosition
&& hasUnityRotation
&& trackedOk
&& trackingStateOk
&& IsFinite(unityPosition)
&& IsFinite(unityRotation);
if (convertUnityToProjectCoordinates) Vector3 posePosition = Vector3.zero;
Quaternion poseRotation = Quaternion.identity;
string poseSource = PoseSourceNone;
int controllerStatus = UnknownControllerStatus;
bool poseValid = TryGetPicoPredictedPose(isLeft, out posePosition, out poseRotation, out controllerStatus);
if (poseValid)
{ {
payload.pos[0] = position.x; poseSource = PoseSourcePxrPredict;
payload.pos[1] = position.y; }
payload.pos[2] = -position.z; else if (unityPoseValid)
{
posePosition = unityPosition;
poseRotation = unityRotation;
poseSource = PoseSourceUnityXr;
poseValid = true;
}
payload.quat[0] = -rotation.x; payload.trigger = Mathf.Clamp01(trigger);
payload.quat[1] = -rotation.y; payload.grip_value = Mathf.Clamp01(gripValue);
payload.quat[2] = rotation.z; payload.axis[0] = axis.x;
payload.quat[3] = rotation.w; payload.axis[1] = axis.y;
payload.buttons.grip = gripButton;
payload.buttons.primary = primaryButton;
payload.buttons.secondary = secondaryButton;
payload.buttons.menu = menuButton;
payload.buttons.axis_click = axisClick;
payload.pose_valid = poseValid;
payload.pose_source = poseSource;
payload.tracking_state = trackingStateValue;
payload.controller_status = controllerStatus;
payload.grip = poseValid && (gripButton || gripValue > 0.5f);
SetGripState(isLeft, payload.grip);
SetPoseState(isLeft, poseValid, poseSource, trackingStateValue, controllerStatus);
WritePose(payload, posePosition, poseRotation, poseSource);
}
private void WritePose(ControllerPayload payload, Vector3 position, Quaternion rotation, string poseSource)
{
if (convertUnityToProjectCoordinates && poseSource == PoseSourcePxrPredict)
{
WritePoseValues(
payload,
ConvertPicoNativePositionToProject(position),
ConvertPicoNativeRotationToProject(rotation));
} }
else else
{
WritePoseValues(payload, position, rotation);
}
}
private static Vector3 ConvertPicoNativePositionToProject(Vector3 position)
{
return new Vector3(position.z, position.y, -position.x);
}
private static Quaternion ConvertPicoNativeRotationToProject(Quaternion rotation)
{
return Quaternion.Euler(0.0f, 90.0f, 0.0f) * rotation;
}
private static void WritePoseValues(ControllerPayload payload, Vector3 position, Quaternion rotation)
{ {
payload.pos[0] = position.x; payload.pos[0] = position.x;
payload.pos[1] = position.y; payload.pos[1] = position.y;
@ -283,8 +392,59 @@ public class PicoControllerUdpSender : MonoBehaviour
payload.quat[2] = rotation.z; payload.quat[2] = rotation.z;
payload.quat[3] = rotation.w; payload.quat[3] = rotation.w;
} }
private bool TryGetPicoPredictedPose(
bool isLeft,
out Vector3 position,
out Quaternion rotation,
out int controllerStatus)
{
position = Vector3.zero;
rotation = Quaternion.identity;
controllerStatus = UnknownControllerStatus;
#if !PICO_OPENXR_SDK && !UNITY_EDITOR
try
{
PXR_Input.Controller controller = isLeft
? PXR_Input.Controller.LeftController
: PXR_Input.Controller.RightController;
if (!PXR_Input.IsControllerConnected(controller))
{
return false;
} }
PXR_Input.ControllerStatus status = PXR_Input.GetControllerStatus(controller);
controllerStatus = (int)status;
if (!IsPositionCapablePicoStatus(status))
{
return false;
}
double predictTime = PXR_System.GetPredictedDisplayTime();
position = PXR_Input.GetControllerPredictPosition(controller, predictTime);
rotation = PXR_Input.GetControllerPredictRotation(controller, predictTime);
return IsFinite(position) && IsFinite(rotation);
}
catch (Exception exception)
{
LastError = $"PXR pose failed: {exception.Message}";
return false;
}
#else
return false;
#endif
}
#if !PICO_OPENXR_SDK && !UNITY_EDITOR
private static bool IsPositionCapablePicoStatus(PXR_Input.ControllerStatus status)
{
return status == PXR_Input.ControllerStatus.Static
|| status == PXR_Input.ControllerStatus.SixDof
|| status == PXR_Input.ControllerStatus.CollidedIn6Dof;
}
#endif
private InputDevice GetControllerDevice(XRNode node) private InputDevice GetControllerDevice(XRNode node)
{ {
InputDevice device = InputDevices.GetDeviceAtXRNode(node); InputDevice device = InputDevices.GetDeviceAtXRNode(node);
@ -340,15 +500,106 @@ public class PicoControllerUdpSender : MonoBehaviour
} }
} }
private void SendSafeStopPacket() private void SetPoseState(
bool isLeft,
bool poseValid,
string poseSource,
int trackingState,
int controllerStatus)
{
if (isLeft)
{
LeftPoseValid = poseValid;
LeftPoseSource = poseSource;
LeftTrackingState = trackingState;
LeftControllerStatus = controllerStatus;
}
else
{
RightPoseValid = poseValid;
RightPoseSource = poseSource;
RightTrackingState = trackingState;
RightControllerStatus = controllerStatus;
}
}
private static void ResetControllerPayload(ControllerPayload payload)
{
if (payload.pos == null || payload.pos.Length != 3)
{
payload.pos = new float[] { 0.0f, 0.0f, 0.0f };
}
if (payload.quat == null || payload.quat.Length != 4)
{
payload.quat = new float[] { 0.0f, 0.0f, 0.0f, 1.0f };
}
if (payload.axis == null || payload.axis.Length != 2)
{
payload.axis = new float[] { 0.0f, 0.0f };
}
if (payload.buttons == null)
{
payload.buttons = new ButtonsPayload();
}
payload.grip = false;
payload.trigger = 0.0f;
payload.pos[0] = 0.0f;
payload.pos[1] = 0.0f;
payload.pos[2] = 0.0f;
payload.quat[0] = 0.0f;
payload.quat[1] = 0.0f;
payload.quat[2] = 0.0f;
payload.quat[3] = 1.0f;
payload.pose_valid = false;
payload.tracking_state = InvalidTrackingState;
payload.controller_status = UnknownControllerStatus;
payload.grip_value = 0.0f;
payload.axis[0] = 0.0f;
payload.axis[1] = 0.0f;
payload.buttons.grip = false;
payload.buttons.primary = false;
payload.buttons.secondary = false;
payload.buttons.menu = false;
payload.buttons.axis_click = false;
payload.pose_source = PoseSourceNone;
}
private void StampPacket()
{ {
packet.t = Time.realtimeSinceStartupAsDouble; packet.t = Time.realtimeSinceStartupAsDouble;
packet.controllers.left.grip = false; packet.source_time = packet.t;
packet.controllers.left.trigger = 0.0f; packet.seq = nextSeq;
packet.controllers.right.grip = false; nextSeq = nextSeq == int.MaxValue ? 1 : nextSeq + 1;
packet.controllers.right.trigger = 0.0f; }
private static bool IsFinite(Vector3 value)
{
return IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z);
}
private static bool IsFinite(Quaternion value)
{
return IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z) && IsFinite(value.w);
}
private static bool IsFinite(float value)
{
return !float.IsNaN(value) && !float.IsInfinity(value);
}
private void SendSafeStopPacket()
{
StampPacket();
ResetControllerPayload(packet.controllers.left);
ResetControllerPayload(packet.controllers.right);
LeftGripPressed = false; LeftGripPressed = false;
RightGripPressed = false; RightGripPressed = false;
SetPoseState(true, false, PoseSourceNone, InvalidTrackingState, UnknownControllerStatus);
SetPoseState(false, false, PoseSourceNone, InvalidTrackingState, UnknownControllerStatus);
SendPacket(); SendPacket();
} }

View File

@ -1,5 +1,9 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Net;
using System.Net.Sockets;
using TMPro;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityEngine.XR; using UnityEngine.XR;
@ -11,29 +15,55 @@ using Unity.XR.PXR;
public class PicoUdpConfigPanel : MonoBehaviour public class PicoUdpConfigPanel : MonoBehaviour
{ {
private const int RowCount = 6; private const int RowCount = 8;
private const int MaxSavedIps = 5;
private const float AxisRepeatDelay = 0.22f; private const float AxisRepeatDelay = 0.22f;
private const string RecentIpPrefsKey = "xr_rm_udp_sender.ip_recent";
private const string FavoriteIpPrefsKey = "xr_rm_udp_sender.ip_favorites";
private const string IpListSeparator = "|";
private const string RegularFontResourcePath = "Fonts/Roboto-Regular SDF";
private const string EmphasisFontResourcePath = "Fonts/Roboto-Bold SDF";
private const int RowTargetIp = 0;
private const int RowSavedIp = 1;
private const int RowFavorite = 2;
private const int RowTargetPort = 3;
private const int RowSendHz = 4;
private const int RowCoordinates = 5;
private const int RowSending = 6;
private const int RowSaveApply = 7;
public int targetFrameRate = 90; public int targetFrameRate = 90;
public bool showOnStart = true; public bool showOnStart = true;
public bool togglePanelWithMenuButton = true; public bool togglePanelWithMenuButton = true;
private readonly Text[] rowTexts = new Text[RowCount]; private readonly ConfigRow[] rows = new ConfigRow[RowCount];
private readonly Color normalColor = new Color(0.86f, 0.91f, 0.96f, 1.0f); private readonly List<string> recentIps = new List<string>();
private readonly Color selectedColor = new Color(0.35f, 0.82f, 1.0f, 1.0f); private readonly List<string> favoriteIps = new List<string>();
private readonly Color mutedColor = new Color(0.58f, 0.66f, 0.74f, 1.0f); private readonly List<string> savedIpChoices = new List<string>();
private readonly Color normalColor = new Color(0.92f, 0.96f, 0.98f, 1.0f);
private readonly Color selectedColor = new Color(0.32f, 0.86f, 1.0f, 1.0f);
private readonly Color mutedColor = new Color(0.64f, 0.72f, 0.78f, 1.0f);
private readonly Color cardColor = new Color(0.018f, 0.026f, 0.032f, 0.86f);
private readonly Color rowColor = new Color(0.08f, 0.105f, 0.125f, 0.66f);
private readonly Color selectedRowColor = new Color(0.05f, 0.20f, 0.28f, 0.92f);
private PicoControllerUdpSender sender; private PicoControllerUdpSender sender;
private Canvas canvas; private Canvas canvas;
private GameObject configPanelObject; private GameObject configPanelObject;
private GameObject runHudObject; private GameObject runHudObject;
private Text titleText; private Image hudStatusStripe;
private Text statusText; private TextMeshProUGUI titleText;
private Text footerText; private TextMeshProUGUI statusText;
private Text hudTitleText; private TextMeshProUGUI connectionHeaderText;
private Text hudStatusText; private TextMeshProUGUI runtimeHeaderText;
private Text hudDetailText; private TextMeshProUGUI footerText;
private Text hudHintText; private TextMeshProUGUI hudTitleText;
private TextMeshProUGUI hudStatusText;
private TextMeshProUGUI hudDetailText;
private TextMeshProUGUI hudHintText;
private TMP_FontAsset regularFontAsset;
private TMP_FontAsset emphasisFontAsset;
private TouchScreenKeyboard keyboard; private TouchScreenKeyboard keyboard;
private KeyboardTarget keyboardTarget; private KeyboardTarget keyboardTarget;
private string hostDraft; private string hostDraft;
@ -42,6 +72,7 @@ public class PicoUdpConfigPanel : MonoBehaviour
private bool convertDraft; private bool convertDraft;
private PicoKeepAwake keepAwake; private PicoKeepAwake keepAwake;
private int selectedRow; private int selectedRow;
private int selectedSavedIpIndex;
private float nextAxisTime; private float nextAxisTime;
private bool wasActivatePressed; private bool wasActivatePressed;
private bool wasApplyPressed; private bool wasApplyPressed;
@ -56,6 +87,15 @@ public class PicoUdpConfigPanel : MonoBehaviour
SendHz SendHz
} }
private class ConfigRow
{
public GameObject root;
public Image background;
public Image accent;
public TextMeshProUGUI labelText;
public TextMeshProUGUI valueText;
}
private void Awake() private void Awake()
{ {
sender = GetComponent<PicoControllerUdpSender>(); sender = GetComponent<PicoControllerUdpSender>();
@ -69,6 +109,8 @@ public class PicoUdpConfigPanel : MonoBehaviour
{ {
keepAwake = gameObject.AddComponent<PicoKeepAwake>(); keepAwake = gameObject.AddComponent<PicoKeepAwake>();
} }
LoadFonts();
} }
private void Start() private void Start()
@ -76,6 +118,7 @@ public class PicoUdpConfigPanel : MonoBehaviour
ApplyRuntimeDefaults(); ApplyRuntimeDefaults();
EnsurePanel(); EnsurePanel();
LoadDraftsFromSender(); LoadDraftsFromSender();
LoadIpLists();
canvas.gameObject.SetActive(true); canvas.gameObject.SetActive(true);
SetConfigPanelVisible(showOnStart, false); SetConfigPanelVisible(showOnStart, false);
RefreshPanel(); RefreshPanel();
@ -96,6 +139,7 @@ public class PicoUdpConfigPanel : MonoBehaviour
Screen.sleepTimeout = SleepTimeout.NeverSleep; Screen.sleepTimeout = SleepTimeout.NeverSleep;
XRSettings.eyeTextureResolutionScale = 1.0f; XRSettings.eyeTextureResolutionScale = 1.0f;
RequestPicoDisplayRefreshRate(); RequestPicoDisplayRefreshRate();
RequestPicoPassthrough();
} }
private void RequestPicoDisplayRefreshRate() private void RequestPicoDisplayRefreshRate()
@ -120,6 +164,24 @@ public class PicoUdpConfigPanel : MonoBehaviour
#endif #endif
} }
private void RequestPicoPassthrough()
{
#if UNITY_ANDROID && !UNITY_EDITOR
try
{
#if PICO_OPENXR_SDK
PassthroughFeature.EnableVideoSeeThrough = true;
#else
PXR_Manager.EnableVideoSeeThrough = true;
#endif
}
catch (Exception exception)
{
Debug.LogWarning($"XR-RM failed to enable PICO video see-through: {exception.Message}");
}
#endif
}
private void EnsurePanel() private void EnsurePanel()
{ {
Camera panelCamera = Camera.main; Camera panelCamera = Camera.main;
@ -130,8 +192,6 @@ public class PicoUdpConfigPanel : MonoBehaviour
panelCamera = cameraObject.AddComponent<Camera>(); panelCamera = cameraObject.AddComponent<Camera>();
panelCamera.nearClipPlane = 0.05f; panelCamera.nearClipPlane = 0.05f;
panelCamera.farClipPlane = 100.0f; panelCamera.farClipPlane = 100.0f;
panelCamera.clearFlags = CameraClearFlags.SolidColor;
panelCamera.backgroundColor = new Color(0.02f, 0.025f, 0.03f, 1.0f);
if (FindObjectOfType<AudioListener>() == null) if (FindObjectOfType<AudioListener>() == null)
{ {
@ -141,6 +201,9 @@ public class PicoUdpConfigPanel : MonoBehaviour
AddTrackedPoseDriverIfAvailable(cameraObject); AddTrackedPoseDriverIfAvailable(cameraObject);
} }
panelCamera.clearFlags = CameraClearFlags.SolidColor;
panelCamera.backgroundColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
GameObject canvasObject = new GameObject("XR-RM UDP Config Canvas", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler)); GameObject canvasObject = new GameObject("XR-RM UDP Config Canvas", typeof(RectTransform), typeof(Canvas), typeof(CanvasScaler));
canvasObject.transform.SetParent(panelCamera.transform, false); canvasObject.transform.SetParent(panelCamera.transform, false);
canvasObject.transform.localPosition = new Vector3(0.0f, 0.0f, 2.0f); canvasObject.transform.localPosition = new Vector3(0.0f, 0.0f, 2.0f);
@ -156,75 +219,102 @@ public class PicoUdpConfigPanel : MonoBehaviour
canvasRect.sizeDelta = new Vector2(980.0f, 640.0f); canvasRect.sizeDelta = new Vector2(980.0f, 640.0f);
CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>(); CanvasScaler scaler = canvasObject.GetComponent<CanvasScaler>();
scaler.dynamicPixelsPerUnit = 12.0f; scaler.dynamicPixelsPerUnit = 18.0f;
scaler.referencePixelsPerUnit = 100.0f; scaler.referencePixelsPerUnit = 100.0f;
configPanelObject = new GameObject("Config Panel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)); configPanelObject = new GameObject("Config Panel", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
configPanelObject.transform.SetParent(canvasObject.transform, false); configPanelObject.transform.SetParent(canvasObject.transform, false);
RectTransform backgroundRect = configPanelObject.GetComponent<RectTransform>(); RectTransform backgroundRect = configPanelObject.GetComponent<RectTransform>();
backgroundRect.anchorMin = Vector2.zero; SetRect(backgroundRect, new Vector2(0.08f, 0.08f), new Vector2(0.92f, 0.92f), Vector2.zero, Vector2.zero);
backgroundRect.anchorMax = Vector2.one;
backgroundRect.offsetMin = Vector2.zero;
backgroundRect.offsetMax = Vector2.zero;
Image background = configPanelObject.GetComponent<Image>(); Image background = configPanelObject.GetComponent<Image>();
background.color = new Color(0.045f, 0.06f, 0.075f, 0.94f); background.color = cardColor;
titleText = CreateText("Title", configPanelObject.transform, 38, FontStyle.Bold, TextAnchor.MiddleLeft); titleText = CreateText("Title", configPanelObject.transform, 40, FontStyles.Bold, TextAlignmentOptions.Left);
SetRect(titleText.rectTransform, new Vector2(0.06f, 0.82f), new Vector2(0.94f, 0.96f), Vector2.zero, Vector2.zero); SetRect(titleText.rectTransform, new Vector2(0.06f, 0.875f), new Vector2(0.94f, 0.965f), Vector2.zero, Vector2.zero);
statusText = CreateText("Status", configPanelObject.transform, 22, FontStyle.Normal, TextAnchor.MiddleLeft); statusText = CreateText("Status", configPanelObject.transform, 21, FontStyles.Normal, TextAlignmentOptions.Left);
statusText.color = mutedColor; statusText.color = mutedColor;
SetRect(statusText.rectTransform, new Vector2(0.06f, 0.72f), new Vector2(0.94f, 0.82f), Vector2.zero, Vector2.zero); SetRect(statusText.rectTransform, new Vector2(0.06f, 0.795f), new Vector2(0.94f, 0.872f), Vector2.zero, Vector2.zero);
for (int i = 0; i < RowCount; i++) connectionHeaderText = CreateText("Connection Header", configPanelObject.transform, 18, FontStyles.Bold, TextAlignmentOptions.Left);
{ connectionHeaderText.color = selectedColor;
Text rowText = CreateText("Row " + i, configPanelObject.transform, 27, FontStyle.Normal, TextAnchor.MiddleLeft); SetRect(connectionHeaderText.rectTransform, new Vector2(0.06f, 0.725f), new Vector2(0.94f, 0.775f), Vector2.zero, Vector2.zero);
float top = 0.68f - i * 0.09f;
SetRect(rowText.rectTransform, new Vector2(0.08f, top - 0.075f), new Vector2(0.92f, top), Vector2.zero, Vector2.zero);
rowTexts[i] = rowText;
}
footerText = CreateText("Footer", configPanelObject.transform, 23, FontStyle.Normal, TextAnchor.MiddleCenter); CreateConfigRow(RowTargetIp, 0.655f, 0.715f);
footerText.color = normalColor; CreateConfigRow(RowSavedIp, 0.585f, 0.645f);
SetRect(footerText.rectTransform, new Vector2(0.05f, 0.025f), new Vector2(0.95f, 0.15f), Vector2.zero, Vector2.zero); CreateConfigRow(RowFavorite, 0.515f, 0.575f);
runtimeHeaderText = CreateText("Runtime Header", configPanelObject.transform, 18, FontStyles.Bold, TextAlignmentOptions.Left);
runtimeHeaderText.color = selectedColor;
SetRect(runtimeHeaderText.rectTransform, new Vector2(0.06f, 0.445f), new Vector2(0.94f, 0.495f), Vector2.zero, Vector2.zero);
CreateConfigRow(RowTargetPort, 0.375f, 0.435f);
CreateConfigRow(RowSendHz, 0.305f, 0.365f);
CreateConfigRow(RowCoordinates, 0.235f, 0.295f);
CreateConfigRow(RowSending, 0.165f, 0.225f);
CreateConfigRow(RowSaveApply, 0.095f, 0.155f);
footerText = CreateText("Footer", configPanelObject.transform, 19, FontStyles.Normal, TextAlignmentOptions.Center);
footerText.color = mutedColor;
SetRect(footerText.rectTransform, new Vector2(0.05f, 0.020f), new Vector2(0.95f, 0.075f), Vector2.zero, Vector2.zero);
CreateRunHud(canvasObject.transform); CreateRunHud(canvasObject.transform);
} }
private void CreateConfigRow(int index, float bottom, float top)
{
GameObject rowObject = new GameObject("Row " + index, typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
rowObject.transform.SetParent(configPanelObject.transform, false);
RectTransform rowRect = rowObject.GetComponent<RectTransform>();
SetRect(rowRect, new Vector2(0.06f, bottom), new Vector2(0.94f, top), Vector2.zero, Vector2.zero);
Image background = rowObject.GetComponent<Image>();
background.color = rowColor;
GameObject accentObject = CreateImage("Accent", rowObject.transform, new Color(0.0f, 0.0f, 0.0f, 0.0f));
SetRect(accentObject.GetComponent<RectTransform>(), new Vector2(0.0f, 0.0f), new Vector2(0.018f, 1.0f), Vector2.zero, Vector2.zero);
TextMeshProUGUI labelText = CreateText("Label", rowObject.transform, 20, FontStyles.Bold, TextAlignmentOptions.Left);
SetRect(labelText.rectTransform, new Vector2(0.045f, 0.10f), new Vector2(0.38f, 0.90f), Vector2.zero, Vector2.zero);
TextMeshProUGUI valueText = CreateText("Value", rowObject.transform, 21, FontStyles.Normal, TextAlignmentOptions.Left);
SetRect(valueText.rectTransform, new Vector2(0.39f, 0.10f), new Vector2(0.965f, 0.90f), Vector2.zero, Vector2.zero);
rows[index] = new ConfigRow
{
root = rowObject,
background = background,
accent = accentObject.GetComponent<Image>(),
labelText = labelText,
valueText = valueText,
};
}
private void CreateRunHud(Transform canvasParent) private void CreateRunHud(Transform canvasParent)
{ {
runHudObject = new GameObject("Run HUD", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image)); runHudObject = new GameObject("Run HUD", typeof(RectTransform), typeof(CanvasRenderer), typeof(Image));
runHudObject.transform.SetParent(canvasParent, false); runHudObject.transform.SetParent(canvasParent, false);
RectTransform hudRect = runHudObject.GetComponent<RectTransform>(); RectTransform hudRect = runHudObject.GetComponent<RectTransform>();
hudRect.anchorMin = Vector2.zero; SetRect(hudRect, new Vector2(0.035f, 0.055f), new Vector2(0.54f, 0.285f), Vector2.zero, Vector2.zero);
hudRect.anchorMax = Vector2.one;
hudRect.offsetMin = Vector2.zero;
hudRect.offsetMax = Vector2.zero;
Image hudBackground = runHudObject.GetComponent<Image>(); Image hudBackground = runHudObject.GetComponent<Image>();
hudBackground.color = new Color(0.018f, 0.026f, 0.032f, 0.88f); hudBackground.color = new Color(0.012f, 0.020f, 0.026f, 0.82f);
GameObject headerObject = CreateImage("HUD Header", runHudObject.transform, new Color(0.035f, 0.05f, 0.06f, 0.88f)); GameObject stripeObject = CreateImage("HUD Status Stripe", runHudObject.transform, selectedColor);
SetRect(headerObject.GetComponent<RectTransform>(), new Vector2(0.05f, 0.70f), new Vector2(0.95f, 0.92f), Vector2.zero, Vector2.zero); SetRect(stripeObject.GetComponent<RectTransform>(), new Vector2(0.0f, 0.0f), new Vector2(0.018f, 1.0f), Vector2.zero, Vector2.zero);
hudStatusStripe = stripeObject.GetComponent<Image>();
hudTitleText = CreateText("HUD Title", headerObject.transform, 34, FontStyle.Bold, TextAnchor.MiddleLeft); hudTitleText = CreateText("HUD Title", runHudObject.transform, 24, FontStyles.Bold, TextAlignmentOptions.Left);
SetRect(hudTitleText.rectTransform, new Vector2(0.04f, 0.54f), new Vector2(0.96f, 0.95f), Vector2.zero, Vector2.zero); SetRect(hudTitleText.rectTransform, new Vector2(0.055f, 0.74f), new Vector2(0.95f, 0.96f), Vector2.zero, Vector2.zero);
hudStatusText = CreateText("HUD Status", headerObject.transform, 28, FontStyle.Bold, TextAnchor.MiddleLeft); hudStatusText = CreateText("HUD Status", runHudObject.transform, 22, FontStyles.Bold, TextAlignmentOptions.Left);
hudStatusText.color = selectedColor; hudStatusText.color = selectedColor;
SetRect(hudStatusText.rectTransform, new Vector2(0.04f, 0.15f), new Vector2(0.96f, 0.56f), Vector2.zero, Vector2.zero); SetRect(hudStatusText.rectTransform, new Vector2(0.055f, 0.49f), new Vector2(0.95f, 0.75f), Vector2.zero, Vector2.zero);
hudDetailText = CreateText("HUD Detail", runHudObject.transform, 25, FontStyle.Normal, TextAnchor.MiddleCenter); hudDetailText = CreateText("HUD Detail", runHudObject.transform, 19, FontStyles.Normal, TextAlignmentOptions.Left);
SetRect(hudDetailText.rectTransform, new Vector2(0.07f, 0.36f), new Vector2(0.93f, 0.64f), Vector2.zero, Vector2.zero); SetRect(hudDetailText.rectTransform, new Vector2(0.055f, 0.16f), new Vector2(0.95f, 0.50f), Vector2.zero, Vector2.zero);
hudHintText = CreateText("HUD Hint", runHudObject.transform, 23, FontStyle.Normal, TextAnchor.MiddleCenter); hudHintText = CreateText("HUD Hint", runHudObject.transform, 17, FontStyles.Normal, TextAlignmentOptions.Left);
hudHintText.color = mutedColor; hudHintText.color = mutedColor;
SetRect(hudHintText.rectTransform, new Vector2(0.07f, 0.08f), new Vector2(0.93f, 0.20f), Vector2.zero, Vector2.zero); SetRect(hudHintText.rectTransform, new Vector2(0.055f, 0.02f), new Vector2(0.95f, 0.16f), Vector2.zero, Vector2.zero);
GameObject verticalLine = CreateImage("HUD Vertical Reticle", runHudObject.transform, new Color(0.35f, 0.82f, 1.0f, 0.38f));
SetRect(verticalLine.GetComponent<RectTransform>(), new Vector2(0.498f, 0.27f), new Vector2(0.502f, 0.36f), Vector2.zero, Vector2.zero);
GameObject horizontalLine = CreateImage("HUD Horizontal Reticle", runHudObject.transform, new Color(0.35f, 0.82f, 1.0f, 0.38f));
SetRect(horizontalLine.GetComponent<RectTransform>(), new Vector2(0.47f, 0.313f), new Vector2(0.53f, 0.319f), Vector2.zero, Vector2.zero);
} }
private static void AddTrackedPoseDriverIfAvailable(GameObject cameraObject) private static void AddTrackedPoseDriverIfAvailable(GameObject cameraObject)
@ -246,25 +336,62 @@ public class PicoUdpConfigPanel : MonoBehaviour
} }
} }
private Text CreateText(string name, Transform parent, int fontSize, FontStyle fontStyle, TextAnchor alignment) private void LoadFonts()
{ {
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(Text)); regularFontAsset = Resources.Load<TMP_FontAsset>(RegularFontResourcePath);
emphasisFontAsset = Resources.Load<TMP_FontAsset>(EmphasisFontResourcePath);
if (regularFontAsset == null)
{
regularFontAsset = TMP_Settings.GetFontAsset();
}
if (emphasisFontAsset == null)
{
emphasisFontAsset = regularFontAsset;
}
if (regularFontAsset == null)
{
Debug.LogWarning("XR-RM failed to load Roboto TMP font assets. Rebuild with XR-RM/Rebuild Roboto TMP Font Assets.");
}
}
private TextMeshProUGUI CreateText(string name, Transform parent, int fontSize, FontStyles fontStyle, TextAlignmentOptions alignment)
{
GameObject textObject = new GameObject(name, typeof(RectTransform), typeof(CanvasRenderer), typeof(TextMeshProUGUI));
textObject.transform.SetParent(parent, false); textObject.transform.SetParent(parent, false);
Text text = textObject.GetComponent<Text>(); TextMeshProUGUI text = textObject.GetComponent<TextMeshProUGUI>();
text.font = Resources.GetBuiltinResource<Font>("Arial.ttf"); TMP_FontAsset fontAsset = GetFontAssetForStyle(fontStyle);
if (fontAsset != null)
{
text.font = fontAsset;
}
text.fontSize = fontSize; text.fontSize = fontSize;
text.fontStyle = fontStyle; text.fontStyle = fontStyle;
text.alignment = alignment; text.alignment = alignment;
text.horizontalOverflow = HorizontalWrapMode.Wrap; text.enableAutoSizing = true;
text.verticalOverflow = VerticalWrapMode.Truncate; text.fontSizeMin = Mathf.Max(13, fontSize - 6);
text.resizeTextForBestFit = true; text.fontSizeMax = fontSize;
text.resizeTextMinSize = Mathf.Max(12, fontSize - 8); text.enableWordWrapping = false;
text.resizeTextMaxSize = fontSize; text.overflowMode = TextOverflowModes.Ellipsis;
text.raycastTarget = false;
text.color = normalColor; text.color = normalColor;
Shadow shadow = textObject.AddComponent<Shadow>();
shadow.effectColor = new Color(0.0f, 0.0f, 0.0f, 0.78f);
shadow.effectDistance = new Vector2(1.4f, -1.4f);
shadow.useGraphicAlpha = true;
return text; return text;
} }
private TMP_FontAsset GetFontAssetForStyle(FontStyles fontStyle)
{
return fontStyle == FontStyles.Bold && emphasisFontAsset != null ? emphasisFontAsset : regularFontAsset;
}
private static void SetRect(RectTransform rectTransform, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax) private static void SetRect(RectTransform rectTransform, Vector2 anchorMin, Vector2 anchorMax, Vector2 offsetMin, Vector2 offsetMax)
{ {
rectTransform.anchorMin = anchorMin; rectTransform.anchorMin = anchorMin;
@ -290,6 +417,61 @@ public class PicoUdpConfigPanel : MonoBehaviour
convertDraft = sender.convertUnityToProjectCoordinates; convertDraft = sender.convertUnityToProjectCoordinates;
} }
private void LoadIpLists()
{
LoadIpList(RecentIpPrefsKey, recentIps);
LoadIpList(FavoriteIpPrefsKey, favoriteIps);
RefreshSavedIpChoices(hostDraft);
}
private void LoadIpList(string prefsKey, List<string> target)
{
target.Clear();
string raw = PlayerPrefs.GetString(prefsKey, string.Empty);
if (string.IsNullOrEmpty(raw))
{
return;
}
string[] values = raw.Split(new[] { IpListSeparator }, StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < values.Length && target.Count < MaxSavedIps; i++)
{
AddUniqueIp(target, values[i]);
}
}
private void SaveIpLists()
{
PlayerPrefs.SetString(RecentIpPrefsKey, string.Join(IpListSeparator, recentIps.ToArray()));
PlayerPrefs.SetString(FavoriteIpPrefsKey, string.Join(IpListSeparator, favoriteIps.ToArray()));
PlayerPrefs.Save();
}
private void RefreshSavedIpChoices(string preferredIp)
{
savedIpChoices.Clear();
for (int i = 0; i < favoriteIps.Count; i++)
{
AddUniqueIp(savedIpChoices, favoriteIps[i]);
}
for (int i = 0; i < recentIps.Count; i++)
{
AddUniqueIp(savedIpChoices, recentIps[i]);
}
if (savedIpChoices.Count == 0 && IsValidIpv4(hostDraft))
{
savedIpChoices.Add(hostDraft);
}
selectedSavedIpIndex = Mathf.Clamp(FindIpIndex(savedIpChoices, preferredIp), 0, Mathf.Max(0, savedIpChoices.Count - 1));
if (selectedSavedIpIndex < 0)
{
selectedSavedIpIndex = 0;
}
}
private void HandleKeyboard() private void HandleKeyboard()
{ {
if (keyboard == null) if (keyboard == null)
@ -317,6 +499,7 @@ public class PicoUdpConfigPanel : MonoBehaviour
if (keyboardTarget == KeyboardTarget.Host) if (keyboardTarget == KeyboardTarget.Host)
{ {
hostDraft = trimmed; hostDraft = trimmed;
RefreshSavedIpChoices(hostDraft);
panelMessage = "Target IP edited"; panelMessage = "Target IP edited";
} }
else if (keyboardTarget == KeyboardTarget.Port) else if (keyboardTarget == KeyboardTarget.Port)
@ -403,22 +586,30 @@ public class PicoUdpConfigPanel : MonoBehaviour
private void AdjustSelectedValue(int direction) private void AdjustSelectedValue(int direction)
{ {
if (selectedRow == 1) if (selectedRow == RowSavedIp)
{
SelectSavedIp(direction);
}
else if (selectedRow == RowFavorite)
{
ToggleFavoriteIp();
}
else if (selectedRow == RowTargetPort)
{ {
portDraft = Mathf.Clamp(portDraft + direction, 1, 65535); portDraft = Mathf.Clamp(portDraft + direction, 1, 65535);
panelMessage = "Target port adjusted"; panelMessage = "Target port adjusted";
} }
else if (selectedRow == 2) else if (selectedRow == RowSendHz)
{ {
sendHzDraft = Mathf.Clamp(sendHzDraft + direction * 5.0f, 1.0f, 120.0f); sendHzDraft = Mathf.Clamp(sendHzDraft + direction * 5.0f, 1.0f, 120.0f);
panelMessage = "Send rate adjusted"; panelMessage = "Send rate adjusted";
} }
else if (selectedRow == 3) else if (selectedRow == RowCoordinates)
{ {
convertDraft = !convertDraft; convertDraft = !convertDraft;
panelMessage = "Coordinate mode toggled"; panelMessage = "Coordinate mode toggled";
} }
else if (selectedRow == 4) else if (selectedRow == RowSending)
{ {
ToggleSending(); ToggleSending();
} }
@ -426,33 +617,91 @@ public class PicoUdpConfigPanel : MonoBehaviour
private void ActivateSelectedRow() private void ActivateSelectedRow()
{ {
if (selectedRow == 0) if (selectedRow == RowTargetIp)
{ {
OpenKeyboard(KeyboardTarget.Host, hostDraft, TouchScreenKeyboardType.URL); OpenKeyboard(KeyboardTarget.Host, hostDraft, TouchScreenKeyboardType.URL);
} }
else if (selectedRow == 1) else if (selectedRow == RowSavedIp)
{ {
OpenKeyboard(KeyboardTarget.Port, portDraft.ToString(), TouchScreenKeyboardType.NumberPad); ApplySelectedSavedIp();
} }
else if (selectedRow == 2) else if (selectedRow == RowFavorite)
{ {
OpenKeyboard(KeyboardTarget.SendHz, Mathf.RoundToInt(sendHzDraft).ToString(), TouchScreenKeyboardType.NumberPad); ToggleFavoriteIp();
} }
else if (selectedRow == 3) else if (selectedRow == RowTargetPort)
{
OpenKeyboard(KeyboardTarget.Port, portDraft.ToString(CultureInfo.InvariantCulture), TouchScreenKeyboardType.NumberPad);
}
else if (selectedRow == RowSendHz)
{
OpenKeyboard(KeyboardTarget.SendHz, Mathf.RoundToInt(sendHzDraft).ToString(CultureInfo.InvariantCulture), TouchScreenKeyboardType.NumberPad);
}
else if (selectedRow == RowCoordinates)
{ {
convertDraft = !convertDraft; convertDraft = !convertDraft;
panelMessage = "Coordinate mode toggled"; panelMessage = "Coordinate mode toggled";
} }
else if (selectedRow == 4) else if (selectedRow == RowSending)
{ {
ToggleSending(); ToggleSending();
} }
else if (selectedRow == 5) else if (selectedRow == RowSaveApply)
{ {
ApplyDrafts(); ApplyDrafts();
} }
} }
private void SelectSavedIp(int direction)
{
if (savedIpChoices.Count == 0)
{
panelMessage = "No saved IPs";
return;
}
selectedSavedIpIndex = (selectedSavedIpIndex + direction + savedIpChoices.Count) % savedIpChoices.Count;
panelMessage = "Saved IP selected";
}
private void ApplySelectedSavedIp()
{
if (savedIpChoices.Count == 0)
{
panelMessage = "No saved IPs";
return;
}
selectedSavedIpIndex = Mathf.Clamp(selectedSavedIpIndex, 0, savedIpChoices.Count - 1);
hostDraft = savedIpChoices[selectedSavedIpIndex];
panelMessage = "Saved IP applied";
}
private void ToggleFavoriteIp()
{
if (!IsValidIpv4(hostDraft))
{
panelMessage = "Invalid IP for favorite";
return;
}
int index = FindIpIndex(favoriteIps, hostDraft);
if (index >= 0)
{
favoriteIps.RemoveAt(index);
panelMessage = "Favorite removed";
}
else
{
InsertIpAtFront(favoriteIps, hostDraft);
TrimIpList(favoriteIps);
panelMessage = "Favorite saved";
}
SaveIpLists();
RefreshSavedIpChoices(hostDraft);
}
private void OpenKeyboard(KeyboardTarget target, string text, TouchScreenKeyboardType keyboardType) private void OpenKeyboard(KeyboardTarget target, string text, TouchScreenKeyboardType keyboardType)
{ {
keyboardTarget = target; keyboardTarget = target;
@ -469,7 +718,83 @@ public class PicoUdpConfigPanel : MonoBehaviour
private void ApplyDrafts() private void ApplyDrafts()
{ {
bool applied = sender.ApplyConfig(hostDraft, portDraft, sendHzDraft, convertDraft, true); bool applied = sender.ApplyConfig(hostDraft, portDraft, sendHzDraft, convertDraft, true);
panelMessage = applied ? "Saved and applied" : sender.LastError; if (!applied)
{
panelMessage = sender.LastError;
return;
}
RecordRecentIp(hostDraft);
SaveIpLists();
RefreshSavedIpChoices(hostDraft);
panelMessage = "Saved and applied";
}
private void RecordRecentIp(string ip)
{
if (!IsValidIpv4(ip))
{
return;
}
InsertIpAtFront(recentIps, ip);
TrimIpList(recentIps);
}
private static void InsertIpAtFront(List<string> values, string ip)
{
int existingIndex = FindIpIndex(values, ip);
if (existingIndex >= 0)
{
values.RemoveAt(existingIndex);
}
values.Insert(0, ip.Trim());
}
private static bool AddUniqueIp(List<string> values, string ip)
{
string trimmed = ip == null ? string.Empty : ip.Trim();
if (!IsValidIpv4(trimmed) || FindIpIndex(values, trimmed) >= 0)
{
return false;
}
values.Add(trimmed);
return true;
}
private static void TrimIpList(List<string> values)
{
while (values.Count > MaxSavedIps)
{
values.RemoveAt(values.Count - 1);
}
}
private static int FindIpIndex(List<string> values, string ip)
{
string trimmed = ip == null ? string.Empty : ip.Trim();
for (int i = 0; i < values.Count; i++)
{
if (string.Equals(values[i], trimmed, StringComparison.OrdinalIgnoreCase))
{
return i;
}
}
return -1;
}
private static bool IsValidIpv4(string value)
{
string trimmed = value == null ? string.Empty : value.Trim();
return IPAddress.TryParse(trimmed, out IPAddress address) && address.AddressFamily == AddressFamily.InterNetwork;
}
private bool IsCurrentHostFavorite()
{
return FindIpIndex(favoriteIps, hostDraft) >= 0;
} }
private bool IsActivatePressed() private bool IsActivatePressed()
@ -479,12 +804,19 @@ public class PicoUdpConfigPanel : MonoBehaviour
private static Vector2 ReadPrimaryAxis() private static Vector2 ReadPrimaryAxis()
{ {
if (TryReadPrimaryAxis(XRNode.RightHand, out Vector2 rightAxis)) bool hasRightAxis = TryReadPrimaryAxis(XRNode.RightHand, out Vector2 rightAxis);
bool hasLeftAxis = TryReadPrimaryAxis(XRNode.LeftHand, out Vector2 leftAxis);
if (hasRightAxis && hasLeftAxis)
{
return rightAxis.sqrMagnitude >= leftAxis.sqrMagnitude ? rightAxis : leftAxis;
}
if (hasRightAxis)
{ {
return rightAxis; return rightAxis;
} }
if (TryReadPrimaryAxis(XRNode.LeftHand, out Vector2 leftAxis)) if (hasLeftAxis)
{ {
return leftAxis; return leftAxis;
} }
@ -552,15 +884,19 @@ public class PicoUdpConfigPanel : MonoBehaviour
{ {
titleText.text = "XR-RM PICO UDP Sender"; titleText.text = "XR-RM PICO UDP Sender";
statusText.text = BuildConfigStatusText(); statusText.text = BuildConfigStatusText();
connectionHeaderText.text = "Connection";
runtimeHeaderText.text = "Runtime";
SetRow(0, "Target IP", hostDraft); SetRow(RowTargetIp, "Target IP", hostDraft);
SetRow(1, "Target Port", portDraft.ToString()); SetRow(RowSavedIp, "Saved IP", BuildSavedIpText());
SetRow(2, "Send Rate", Mathf.RoundToInt(sendHzDraft) + " Hz"); SetRow(RowFavorite, "Favorite", IsCurrentHostFavorite() ? "ON" : "OFF");
SetRow(3, "Coordinates", convertDraft ? "Project (+Z forward)" : "Unity raw"); SetRow(RowTargetPort, "Target Port", portDraft.ToString(CultureInfo.InvariantCulture));
SetRow(4, "UDP Sending", sender.SendEnabled ? "ON" : "OFF"); SetRow(RowSendHz, "Send Rate", Mathf.RoundToInt(sendHzDraft).ToString(CultureInfo.InvariantCulture) + " Hz");
SetRow(5, "Save & Apply", "press trigger"); SetRow(RowCoordinates, "Coordinates", convertDraft ? "Project (+Z back)" : "Source raw");
SetRow(RowSending, "UDP Sending", sender.SendEnabled ? "ON" : "OFF");
SetRow(RowSaveApply, "Save & Apply", "press trigger");
footerText.text = "Stick: select / adjust Trigger, A or X: edit\nB or Y: save Menu: run view"; footerText.text = "Stick: select / adjust Trigger, A or X: edit/apply B or Y: save Menu: run view";
} }
private void RefreshRunHud() private void RefreshRunHud()
@ -570,11 +906,13 @@ public class PicoUdpConfigPanel : MonoBehaviour
return; return;
} }
hudTitleText.text = "XR-RM UDP Sender"; Color stateColor = sender.SendEnabled ? new Color(0.28f, 0.92f, 0.62f, 1.0f) : new Color(1.0f, 0.68f, 0.30f, 1.0f);
hudStatusStripe.color = stateColor;
hudTitleText.text = "XR-RM UDP";
hudStatusText.color = stateColor;
hudStatusText.text = (sender.SendEnabled ? "SENDING" : "PAUSED") + " " + BuildEndpointText(); hudStatusText.text = (sender.SendEnabled ? "SENDING" : "PAUSED") + " " + BuildEndpointText();
hudDetailText.text = BuildTrackingStateText() + " " + BuildGripStateText() + "\n" hudDetailText.text = BuildCompactTrackingStateText() + "\n" + BuildPacketStateText(false);
+ BuildPacketStateText(false) + " " + BuildKeepAwakeStateText(); hudHintText.text = "Menu: config";
hudHintText.text = "Menu: config panel";
} }
private string BuildConfigStatusText() private string BuildConfigStatusText()
@ -593,14 +931,70 @@ public class PicoUdpConfigPanel : MonoBehaviour
return sender.HasEndpoint ? sender.host + ":" + sender.port : "not configured"; return sender.HasEndpoint ? sender.host + ":" + sender.port : "not configured";
} }
private string BuildTrackingStateText() private string BuildSavedIpText()
{ {
return "L " + (sender.LeftDeviceValid ? "ok" : "--") + " / R " + (sender.RightDeviceValid ? "ok" : "--"); if (savedIpChoices.Count == 0)
{
return "none";
} }
private string BuildGripStateText() selectedSavedIpIndex = Mathf.Clamp(selectedSavedIpIndex, 0, savedIpChoices.Count - 1);
string ip = savedIpChoices[selectedSavedIpIndex];
string marker = FindIpIndex(favoriteIps, ip) >= 0 ? "favorite" : "recent";
return ip + " " + marker + " " + (selectedSavedIpIndex + 1).ToString(CultureInfo.InvariantCulture) + "/" + savedIpChoices.Count.ToString(CultureInfo.InvariantCulture);
}
private string BuildTrackingStateText()
{ {
return "Grip L " + (sender.LeftGripPressed ? "ON" : "--") + " / R " + (sender.RightGripPressed ? "ON" : "--"); return "L " + BuildPoseSummary(true) + " / R " + BuildPoseSummary(false);
}
private string BuildCompactTrackingStateText()
{
return "L " + BuildCompactPoseSummary(true) + " / R " + BuildCompactPoseSummary(false);
}
private string BuildCompactPoseSummary(bool left)
{
bool deviceValid = left ? sender.LeftDeviceValid : sender.RightDeviceValid;
if (!deviceValid)
{
return "--";
}
bool poseValid = left ? sender.LeftPoseValid : sender.RightPoseValid;
string source = ShortPoseSource(left ? sender.LeftPoseSource : sender.RightPoseSource);
return (poseValid ? "valid" : "invalid") + " " + source;
}
private string BuildPoseSummary(bool left)
{
bool deviceValid = left ? sender.LeftDeviceValid : sender.RightDeviceValid;
if (!deviceValid)
{
return "--";
}
bool poseValid = left ? sender.LeftPoseValid : sender.RightPoseValid;
string source = ShortPoseSource(left ? sender.LeftPoseSource : sender.RightPoseSource);
int status = left ? sender.LeftControllerStatus : sender.RightControllerStatus;
int tracking = left ? sender.LeftTrackingState : sender.RightTrackingState;
return (poseValid ? "valid" : "invalid") + " " + source + " s" + status + " t" + tracking;
}
private static string ShortPoseSource(string source)
{
if (source == "pxr_predict")
{
return "pxr";
}
if (source == "unity_xr")
{
return "unity";
}
return "none";
} }
private string BuildPacketStateText(bool compact) private string BuildPacketStateText(bool compact)
@ -628,11 +1022,20 @@ public class PicoUdpConfigPanel : MonoBehaviour
private void SetRow(int index, string label, string value) private void SetRow(int index, string label, string value)
{ {
Text rowText = rowTexts[index]; ConfigRow row = rows[index];
bool selected = index == selectedRow; bool selected = index == selectedRow;
rowText.color = selected ? selectedColor : normalColor; row.background.color = selected ? selectedRowColor : rowColor;
rowText.fontStyle = selected ? FontStyle.Bold : FontStyle.Normal; row.accent.color = selected ? selectedColor : new Color(0.18f, 0.25f, 0.30f, 0.70f);
rowText.text = (selected ? "> " : " ") + label.PadRight(14) + value; row.labelText.text = label;
row.valueText.text = value;
row.labelText.color = selected ? selectedColor : mutedColor;
row.valueText.color = normalColor;
row.valueText.fontStyle = selected ? FontStyles.Bold : FontStyles.Normal;
TMP_FontAsset valueFontAsset = GetFontAssetForStyle(row.valueText.fontStyle);
if (valueFontAsset != null)
{
row.valueText.font = valueFontAsset;
}
} }
private static bool TryParseFloat(string value, out float parsedValue) private static bool TryParseFloat(string value, out float parsedValue)

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f54d1bd14bd3ca042bd867b519fee8cc
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6ab70aee4d56447429c680537fbf93ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,46 @@
Digitized data copyright (c) 2010 Google Corporation
with Reserved Font Arimo, Tinos and Cousine.
Copyright (c) 2012 Red Hat, Inc.
with Reserved Font Name Liberation.
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide development of collaborative font projects, to support the font creation efforts of academic and linguistic communities, and to provide a free and open framework in which fonts may be shared and improved in partnership with others.
The OFL allows the licensed fonts to be used, studied, modified and redistributed freely as long as they are not sold by themselves. The fonts, including any derivative works, can be bundled, embedded, redistributed and/or sold with any software provided that any reserved names are not used by derivative works. The fonts and derivatives, however, cannot be released under any other type of license. The requirement for fonts to remain under this license does not apply to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright Holder(s) under this license and clearly marked as such. This may include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the copyright statement(s).
"Original Version" refers to the collection of Font Software components as distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting, or substituting -- in part or in whole -- any of the components of the Original Version, by changing formats or by porting the Font Software to a new environment.
"Author" refers to any designer, engineer, programmer, technical writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining a copy of the Font Software, to use, study, copy, merge, embed, modify, redistribute, and sell modified and unmodified copies of the Font Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components, in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled, redistributed and/or sold with any software, provided that each copy contains the above copyright notice and this license. These can be included either as stand-alone text files, human-readable headers or in the appropriate machine-readable metadata fields within text or binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font Name(s) unless explicit written permission is granted by the corresponding Copyright Holder. This restriction only applies to the primary font name as presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font Software shall not be used to promote, endorse or advertise any Modified Version, except to acknowledge the contribution(s) of the Copyright Holder(s) and the Author(s) or with their explicit written permission.
5) The Font Software, modified or unmodified, in part or in whole, must be distributed entirely under this license, and must not be distributed under any other license. The requirement for fonts to remain under this license does not apply to any document created using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 6e59c59b81ab47f9b6ec5781fa725d2c
timeCreated: 1484171296
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,19 @@
fileFormatVersion: 2
guid: e3265ab4bf004d28a9537516768c1c75
timeCreated: 1484171297
licenseType: Pro
TrueTypeFontImporter:
serializedVersion: 2
fontSize: 16
forceTextureCase: -2
characterSpacing: 1
characterPadding: 0
includeFontData: 1
use2xBehaviour: 0
fontNames: []
fallbackFontReferences: []
customCharacters:
fontRenderingMode: 0
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 243e06394e614e5d99fab26083b707fa
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 731f1baa9d144a9897cb1d341c2092b8
folderAsset: yes
timeCreated: 1442040525
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF - Drop Shadow
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords: OUTLINE_ON UNDERLAY_ON
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee,
type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _ColorMask: 15
- _Diffuse: 0.5
- _DiffusePower: 1
- _FaceDilate: 0.1
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0.1
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.9
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.64125
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SpecularPower: 2
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 1024
- _TextureWidth: 1024
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0.5
- _UnderlayOffsetY: -0.5
- _UnderlaySoftness: 0.05
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e73a58f6e2794ae7b1b7e50b7fb811b0
timeCreated: 1484172806
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,343 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2180264
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF Material
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 1
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28268798066460806}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _BumpScale: 1
- _ColorMask: 15
- _CullMode: 0
- _Cutoff: 0.5
- _DetailNormalMapScale: 1
- _Diffuse: 0.5
- _DstBlend: 0
- _FaceDilate: 0
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlossMapScale: 1
- _Glossiness: 0.5
- _GlossyReflections: 1
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _Metallic: 0
- _Mode: 0
- _OcclusionStrength: 1
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0
- _Parallax: 0.02
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.90909094
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.7386364
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SpecularPower: 2
- _SrcBlend: 1
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 512
- _TextureWidth: 512
- _UVSec: 0
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0
- _UnderlayOffsetY: 0
- _UnderlaySoftness: 0
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
- _ZWrite: 1
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 71c1514a6bd24e1e882cebbe1904ce04, type: 3}
m_Name: LiberationSans SDF - Fallback
m_EditorClassIdentifier:
hashCode: -1699145518
material: {fileID: 2180264}
materialHashCode: 462855346
m_Version: 1.1.0
m_SourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75
m_SourceFontFile_EditorRef: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75,
type: 3}
m_SourceFontFile: {fileID: 12800000, guid: e3265ab4bf004d28a9537516768c1c75, type: 3}
m_AtlasPopulationMode: 1
m_FaceInfo:
m_FamilyName: Liberation Sans
m_StyleName: Regular
m_PointSize: 86
m_Scale: 1
m_LineHeight: 98.8916
m_AscentLine: 77.853516
m_CapLine: 59
m_MeanLine: 45
m_Baseline: 0
m_DescentLine: -18.22461
m_SuperscriptOffset: 77.853516
m_SuperscriptSize: 0.5
m_SubscriptOffset: -18.22461
m_SubscriptSize: 0.5
m_UnderlineOffset: -12.261719
m_UnderlineThickness: 6.298828
m_StrikethroughOffset: 18
m_StrikethroughThickness: 6.298828
m_TabWidth: 24
m_GlyphTable: []
m_CharacterTable: []
m_AtlasTextures:
- {fileID: 28268798066460806}
m_AtlasTextureIndex: 0
m_IsMultiAtlasTexturesEnabled: 0
m_ClearDynamicDataOnBuild: 1
m_UsedGlyphRects: []
m_FreeGlyphRects:
- m_X: 0
m_Y: 0
m_Width: 511
m_Height: 511
m_fontInfo:
Name: Liberation Sans
PointSize: 86
Scale: 1
CharacterCount: 250
LineHeight: 98.90625
Baseline: 0
Ascender: 77.84375
CapHeight: 59.1875
Descender: -18.21875
CenterLine: 0
SuperscriptOffset: 77.84375
SubscriptOffset: -12.261719
SubSize: 0.5
Underline: -12.261719
UnderlineThickness: 6.298828
strikethrough: 23.675
strikethroughThickness: 0
TabWidth: 239.0625
Padding: 9
AtlasWidth: 1024
AtlasHeight: 1024
atlas: {fileID: 0}
m_AtlasWidth: 512
m_AtlasHeight: 512
m_AtlasPadding: 9
m_AtlasRenderMode: 4169
m_glyphInfoList: []
m_KerningTable:
kerningPairs: []
m_FontFeatureTable:
m_GlyphPairAdjustmentRecords: []
fallbackFontAssets: []
m_FallbackFontAssetTable: []
m_CreationSettings:
sourceFontFileName:
sourceFontFileGUID: e3265ab4bf004d28a9537516768c1c75
pointSizeSamplingMode: 0
pointSize: 86
padding: 9
packingMode: 4
atlasWidth: 512
atlasHeight: 512
characterSetSelectionMode: 1
characterSequence: 32 - 126, 160 - 255, 8192 - 8303, 8364, 8482, 9633
referencedFontAssetGUID: 8f586378b4e144a9851e7b34d9b748ee
referencedTextAssetGUID:
fontStyle: 0
fontStyleModifier: 0
renderMode: 4169
includeFontFeatures: 1
m_FontWeightTable:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
fontWeights:
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
- regularTypeface: {fileID: 0}
italicTypeface: {fileID: 0}
normalStyle: 0
normalSpacingOffset: 0
boldStyle: 0.75
boldSpacing: 7
italicStyle: 35
tabSize: 10
--- !u!28 &28268798066460806
Texture2D:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF Atlas
m_ImageContentsHash:
serializedVersion: 2
Hash: 00000000000000000000000000000000
m_ForcedFallbackFormat: 4
m_DownscaleFallback: 0
serializedVersion: 2
m_Width: 0
m_Height: 0
m_CompleteImageSize: 0
m_TextureFormat: 1
m_MipCount: 1
m_IsReadable: 1
m_StreamingMipmaps: 0
m_StreamingMipmapsPriority: 0
m_AlphaIsTransparency: 0
m_ImageCount: 1
m_TextureDimension: 2
m_TextureSettings:
serializedVersion: 2
m_FilterMode: 1
m_Aniso: 1
m_MipBias: 0
m_WrapU: 0
m_WrapV: 0
m_WrapW: 0
m_LightmapFormat: 0
m_ColorSpace: 0
image data: 0
_typelessdata:
m_StreamData:
offset: 0
size: 0
path:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 2e498d1c8094910479dc3e1b768306a4
timeCreated: 1484171803
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,104 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2100000
Material:
serializedVersion: 6
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: LiberationSans SDF - Outline
m_Shader: {fileID: 4800000, guid: fe393ace9b354375a9cb14cdbbc28be4, type: 3}
m_ShaderKeywords: OUTLINE_ON
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _Cube:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _FaceTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 28684132378477856, guid: 8f586378b4e144a9851e7b34d9b748ee,
type: 2}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OutlineTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _Ambient: 0.5
- _Bevel: 0.5
- _BevelClamp: 0
- _BevelOffset: 0
- _BevelRoundness: 0
- _BevelWidth: 0
- _BumpFace: 0
- _BumpOutline: 0
- _ColorMask: 15
- _Diffuse: 0.5
- _FaceDilate: 0.1
- _FaceUVSpeedX: 0
- _FaceUVSpeedY: 0
- _GlowInner: 0.05
- _GlowOffset: 0
- _GlowOuter: 0.05
- _GlowPower: 0.75
- _GradientScale: 10
- _LightAngle: 3.1416
- _MaskSoftnessX: 0
- _MaskSoftnessY: 0
- _OutlineSoftness: 0
- _OutlineUVSpeedX: 0
- _OutlineUVSpeedY: 0
- _OutlineWidth: 0.1
- _PerspectiveFilter: 0.875
- _Reflectivity: 10
- _ScaleRatioA: 0.9
- _ScaleRatioB: 0.73125
- _ScaleRatioC: 0.64125
- _ScaleX: 1
- _ScaleY: 1
- _ShaderFlags: 0
- _Sharpness: 0
- _SpecularPower: 2
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _TextureHeight: 1024
- _TextureWidth: 1024
- _UnderlayDilate: 0
- _UnderlayOffsetX: 0
- _UnderlayOffsetY: 0
- _UnderlaySoftness: 0
- _VertexOffsetX: 0
- _VertexOffsetY: 0
- _WeightBold: 0.75
- _WeightNormal: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _EnvMatrixRotation: {r: 0, g: 0, b: 0, a: 0}
- _FaceColor: {r: 1, g: 1, b: 1, a: 1}
- _GlowColor: {r: 0, g: 1, b: 0, a: 0.5}
- _MaskCoord: {r: 0, g: 0, b: 32767, a: 32767}
- _OutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectFaceColor: {r: 0, g: 0, b: 0, a: 1}
- _ReflectOutlineColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecularColor: {r: 1, g: 1, b: 1, a: 1}
- _UnderlayColor: {r: 0, g: 0, b: 0, a: 0.5}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 79459efec17a4d00a321bdcc27bbc385
timeCreated: 1484172856
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f586378b4e144a9851e7b34d9b748ee
timeCreated: 1484171803
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
)]}〕〉》」』】〙〗〟’”⦆»ヽヾーァィゥェォッャュョヮヵヶぁぃぅぇぉっゃゅょゎゕゖㇰㇱㇲㇳㇴㇵㇶㇷㇸㇹㇺㇻㇼㇽㇾㇿ々〻‐゠–〜?!‼⁇⁈⁉・、%,.:;。!?]):;=}¢°"†‡℃〆%,.

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: fade42e8bc714b018fac513c043d323b
timeCreated: 1425440388
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1 @@
([{〔〈《「『【〘〖〝‘“⦅«$—…‥〳〴〵\{£¥"々〇$¥₩ #

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d82c1b31c7e74239bff1220585707d2b
timeCreated: 1425440388
licenseType: Store
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 512a49d95c0c4332bdd98131869c23c9
folderAsset: yes
timeCreated: 1441876896
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,659 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!21 &2103686
Material:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: TextMeshPro/Sprite
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_ShaderKeywords: UNITY_UI_CLIP_RECT
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _MainTex:
m_Texture: {fileID: 2800000, guid: dffef66376be4fa480fb02b19edbe903, type: 3}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Floats:
- _ColorMask: 15
- _CullMode: 0
- _Stencil: 0
- _StencilComp: 8
- _StencilOp: 0
- _StencilReadMask: 255
- _StencilWriteMask: 255
- _UseUIAlphaClip: 0
m_Colors:
- _ClipRect: {r: -32767, g: -32767, b: 32767, a: 32767}
- _Color: {r: 1, g: 1, b: 1, a: 1}
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 84a92b25f83d49b9bc132d206b370281, type: 3}
m_Name: EmojiOne
m_EditorClassIdentifier:
hashCode: -1836805472
material: {fileID: 2103686}
materialHashCode: 0
m_Version: 1.1.0
m_FaceInfo:
m_FamilyName:
m_StyleName:
m_PointSize: 0
m_Scale: 0
m_LineHeight: 0
m_AscentLine: 0
m_CapLine: 0
m_MeanLine: 0
m_Baseline: 0
m_DescentLine: 0
m_SuperscriptOffset: 0
m_SuperscriptSize: 0
m_SubscriptOffset: 0
m_SubscriptSize: 0
m_UnderlineOffset: 0
m_UnderlineThickness: 0
m_StrikethroughOffset: 0
m_StrikethroughThickness: 0
m_TabWidth: 0
spriteSheet: {fileID: 2800000, guid: dffef66376be4fa480fb02b19edbe903, type: 3}
m_SpriteCharacterTable:
- m_ElementType: 2
m_Unicode: 128522
m_GlyphIndex: 0
m_Scale: 1
m_Name: Smiling face with smiling eyes
m_HashCode: -1318250903
- m_ElementType: 2
m_Unicode: 128523
m_GlyphIndex: 1
m_Scale: 1
m_Name: 1f60b
m_HashCode: 57188339
- m_ElementType: 2
m_Unicode: 128525
m_GlyphIndex: 2
m_Scale: 1
m_Name: 1f60d
m_HashCode: 57188341
- m_ElementType: 2
m_Unicode: 128526
m_GlyphIndex: 3
m_Scale: 1
m_Name: 1f60e
m_HashCode: 57188340
- m_ElementType: 2
m_Unicode: 128512
m_GlyphIndex: 4
m_Scale: 1
m_Name: Grinning face
m_HashCode: -95541379
- m_ElementType: 2
m_Unicode: 128513
m_GlyphIndex: 5
m_Scale: 1
m_Name: 1f601
m_HashCode: 57188256
- m_ElementType: 2
m_Unicode: 128514
m_GlyphIndex: 6
m_Scale: 1
m_Name: Face with tears of joy
m_HashCode: 239522663
- m_ElementType: 2
m_Unicode: 128515
m_GlyphIndex: 7
m_Scale: 1
m_Name: 1f603
m_HashCode: 57188258
- m_ElementType: 2
m_Unicode: 128516
m_GlyphIndex: 8
m_Scale: 1
m_Name: 1f604
m_HashCode: 57188261
- m_ElementType: 2
m_Unicode: 128517
m_GlyphIndex: 9
m_Scale: 1
m_Name: 1f605
m_HashCode: 57188260
- m_ElementType: 2
m_Unicode: 128518
m_GlyphIndex: 10
m_Scale: 1
m_Name: 1f606
m_HashCode: 57188263
- m_ElementType: 2
m_Unicode: 128521
m_GlyphIndex: 11
m_Scale: 1
m_Name: 1f609
m_HashCode: 57188264
- m_ElementType: 2
m_Unicode: 0
m_GlyphIndex: 12
m_Scale: 1
m_Name: .notdef
m_HashCode: -600915428
- m_ElementType: 2
m_Unicode: 129315
m_GlyphIndex: 13
m_Scale: 1
m_Name: 1f923
m_HashCode: 57200239
- m_ElementType: 2
m_Unicode: 9786
m_GlyphIndex: 14
m_Scale: 1
m_Name: 263a
m_HashCode: 1748406
- m_ElementType: 2
m_Unicode: 9785
m_GlyphIndex: 15
m_Scale: 1
m_Name: 2639
m_HashCode: 1748462
m_SpriteGlyphTable:
- m_Index: 0
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 0
m_Y: 384
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 1
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 128
m_Y: 384
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 2
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 256
m_Y: 384
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 3
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 384
m_Y: 384
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 4
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 0
m_Y: 256
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 5
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 128
m_Y: 256
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 6
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 256
m_Y: 256
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 7
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 384
m_Y: 256
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 8
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 0
m_Y: 128
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 9
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 128
m_Y: 128
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 10
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 256
m_Y: 128
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 11
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 384
m_Y: 128
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 12
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 0
m_Y: 0
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 13
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 128
m_Y: 0
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 14
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 256
m_Y: 0
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
- m_Index: 15
m_Metrics:
m_Width: 128
m_Height: 128
m_HorizontalBearingX: 0
m_HorizontalBearingY: 115.6
m_HorizontalAdvance: 128
m_GlyphRect:
m_X: 384
m_Y: 0
m_Width: 128
m_Height: 128
m_Scale: 1
m_AtlasIndex: 0
sprite: {fileID: 0}
spriteInfoList:
- id: 0
x: 0
y: 384
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: Smiling face with smiling eyes
hashCode: -1318250903
unicode: 128522
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 1
x: 128
y: 384
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f60b
hashCode: 57188339
unicode: 128523
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 2
x: 256
y: 384
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f60d
hashCode: 57188341
unicode: 128525
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 3
x: 384
y: 384
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f60e
hashCode: 57188340
unicode: 128526
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 4
x: 0
y: 256
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: Grinning face
hashCode: -95541379
unicode: 128512
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 5
x: 128
y: 256
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f601
hashCode: 57188256
unicode: 128513
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 6
x: 256
y: 256
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: Face with tears of joy
hashCode: 239522663
unicode: 128514
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 7
x: 384
y: 256
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f603
hashCode: 57188258
unicode: 128515
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 8
x: 0
y: 128
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f604
hashCode: 57188261
unicode: 128516
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 9
x: 128
y: 128
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f605
hashCode: 57188260
unicode: 128517
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 10
x: 256
y: 128
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f606
hashCode: 57188263
unicode: 128518
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 11
x: 384
y: 128
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f609
hashCode: 57188264
unicode: 128521
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 12
x: 0
y: 0
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f618
hashCode: 57188168
unicode: 128536
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 13
x: 128
y: 0
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 1f923
hashCode: 57200239
unicode: 129315
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 14
x: 256
y: 0
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 263a
hashCode: 1748406
unicode: 9786
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
- id: 15
x: 384
y: 0
width: 128
height: 128
xOffset: 0
yOffset: 115.6
xAdvance: 128
scale: 1
name: 2639
hashCode: 1748462
unicode: 9785
pivot: {x: 0.5, y: 0.5}
sprite: {fileID: 0}
fallbackSpriteAssets: []
--- !u!21 &1369835458
Material:
serializedVersion: 6
m_ObjectHideFlags: 1
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: TextMeshPro/Sprite
m_Shader: {fileID: 4800000, guid: cf81c85f95fe47e1a27f6ae460cf182c, type: 3}
m_ShaderKeywords:
m_LightmapFlags: 5
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 0
m_CustomRenderQueue: -1
stringTagMap: {}
disabledShaderPasses: []
m_SavedProperties:
serializedVersion: 3
m_TexEnvs: []
m_Floats: []
m_Colors: []

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: c41005c129ba4d66911b75229fd70b45
timeCreated: 1480316912
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 4aecb92fff08436c8303b10eab8da368
folderAsset: yes
timeCreated: 1441876950
licenseType: Pro
DefaultImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,68 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_PrefabParentObject: {fileID: 0}
m_PrefabInternal: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: ab2114bdc8544297b417dfefe9f1e410, type: 3}
m_Name: Default Style Sheet
m_EditorClassIdentifier:
m_StyleList:
- m_Name: H1
m_HashCode: 2425
m_OpeningDefinition: <size=2em><b><#40ff80>*
m_ClosingDefinition: '*</size></b></color>'
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d00000032000000650000006d0000003e0000003c000000620000003e0000003c000000230000003400000030000000660000006600000038000000300000003e0000002a000000
m_ClosingTagArray: 2a0000003c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: Quote
m_HashCode: 92254330
m_OpeningDefinition: <i><size=75%><margin=10%>
m_ClosingDefinition: </i></size></width></margin>
m_OpeningTagArray: 3c000000690000003e0000003c00000073000000690000007a000000650000003d0000003700000035000000250000003e0000003c0000006d000000610000007200000067000000690000006e0000003d0000003100000030000000250000003e000000
m_ClosingTagArray: 3c0000002f000000690000003e0000003c0000002f00000073000000690000007a000000650000003e0000003c0000002f00000077000000690000006400000074000000680000003e0000003c0000002f0000006d000000610000007200000067000000690000006e0000003e000000
- m_Name: Link
m_HashCode: 2687968
m_OpeningDefinition: <u><#40a0ff><link="ID_01">
m_ClosingDefinition: </u></color></link>
m_OpeningTagArray: 3c000000750000003e0000003c000000230000003400000030000000610000003000000066000000660000003e0000003c0000006c000000690000006e0000006b0000003d0000002200000049000000440000005f0000003000000031000000220000003e000000
m_ClosingTagArray: 3c0000002f000000750000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f0000006c000000690000006e0000006b0000003e000000
- m_Name: Title
m_HashCode: 98732960
m_OpeningDefinition: <size=125%><b><align=center>
m_ClosingDefinition: </size></b></align>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000003200000035000000250000003e0000003c000000620000003e0000003c000000610000006c00000069000000670000006e0000003d00000063000000650000006e0000007400000065000000720000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000610000006c00000069000000670000006e0000003e000000
- m_Name: H2
m_HashCode: 2426
m_OpeningDefinition: <size=1.5em><b><#4080FF>
m_ClosingDefinition: </size></b></color>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000002e00000035000000650000006d0000003e0000003c000000620000003e0000003c000000230000003400000030000000380000003000000046000000460000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: H3
m_HashCode: 2427
m_OpeningDefinition: <size=1.17em><b><#FF8040>
m_ClosingDefinition: </size></b></color>
m_OpeningTagArray: 3c00000073000000690000007a000000650000003d000000310000002e0000003100000037000000650000006d0000003e0000003c000000620000003e0000003c000000230000004600000046000000380000003000000034000000300000003e000000
m_ClosingTagArray: 3c0000002f00000073000000690000007a000000650000003e0000003c0000002f000000620000003e0000003c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: C1
m_HashCode: 2194
m_OpeningDefinition: <color=#ffff40>
m_ClosingDefinition: </color>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000006600000066000000660000006600000034000000300000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e000000
- m_Name: C2
m_HashCode: 2193
m_OpeningDefinition: <color=#ff40FF><size=125%>
m_ClosingDefinition: </color></size>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000006600000066000000340000003000000046000000460000003e0000003c00000073000000690000007a000000650000003d000000310000003200000035000000250000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f00000073000000690000007a000000650000003e000000
- m_Name: C3
m_HashCode: 2192
m_OpeningDefinition: <color=#80A0FF><b>
m_ClosingDefinition: </color></b>
m_OpeningTagArray: 3c000000630000006f0000006c0000006f000000720000003d000000230000003800000030000000410000003000000046000000460000003e0000003c000000620000003e000000
m_ClosingTagArray: 3c0000002f000000630000006f0000006c0000006f000000720000003e0000003c0000002f000000620000003e000000

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: f952c082cb03451daed3ee968ac6c63e
timeCreated: 1432805430
licenseType: Store
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,45 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &11400000
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 2705215ac5b84b70bacc50632be6e391, type: 3}
m_Name: TMP Settings
m_EditorClassIdentifier:
m_enableWordWrapping: 0
m_enableKerning: 1
m_enableExtraPadding: 0
m_enableTintAllSprites: 0
m_enableParseEscapeCharacters: 1
m_EnableRaycastTarget: 0
m_GetFontFeaturesAtRuntime: 1
m_missingGlyphCharacter: 0
m_warningsDisabled: 0
m_defaultFontAsset: {fileID: 11400000, guid: d38777a8d8b70d68788b4c07547a8aca, type: 2}
m_defaultFontAssetPath: Fonts/
m_defaultFontSize: 36
m_defaultAutoSizeMinRatio: 0.5
m_defaultAutoSizeMaxRatio: 1
m_defaultTextMeshProTextContainerSize: {x: 20, y: 5}
m_defaultTextMeshProUITextContainerSize: {x: 200, y: 50}
m_autoSizeTextContainer: 0
m_IsTextObjectScaleStatic: 0
m_fallbackFontAssets:
- {fileID: 11400000, guid: 9605d99cce67df46ea0e78c679b423e9, type: 2}
m_matchMaterialPreset: 1
m_defaultSpriteAsset: {fileID: 11400000, guid: c41005c129ba4d66911b75229fd70b45, type: 2}
m_defaultSpriteAssetPath: Sprite Assets/
m_enableEmojiSupport: 1
m_MissingCharacterSpriteUnicode: 0
m_defaultColorGradientPresetsPath: Color Gradient Presets/
m_defaultStyleSheet: {fileID: 11400000, guid: f952c082cb03451daed3ee968ac6c63e, type: 2}
m_StyleSheetsResourcePath:
m_leadingCharacters: {fileID: 4900000, guid: d82c1b31c7e74239bff1220585707d2b, type: 3}
m_followingCharacters: {fileID: 4900000, guid: fade42e8bc714b018fac513c043d323b, type: 3}
m_UseModernHangulLineBreakingRules: 0

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 3f5b5dff67a942289a9defa416b206f3
timeCreated: 1436653997
licenseType: Pro
NativeFormatImporter:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: e9f693669af91aa45ad615fc681ed29f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,143 @@
Shader "TextMeshPro/Bitmap Custom Atlas" {
Properties {
_MainTex ("Font Atlas", 2D) = "white" {}
_FaceTex ("Font Texture", 2D) = "white" {}
[HDR]_FaceColor ("Text Color", Color) = (1,1,1,1)
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_ClipRect("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_Padding ("Padding", float) = 0
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_CullMode("Cull Mode", Float) = 0
_ColorMask("Color Mask", Float) = 15
}
SubShader{
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Lighting Off
Cull [_CullMode]
ZTest [unity_GUIZTestMode]
ZWrite Off
Fog { Mode Off }
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
float4 mask : TEXCOORD2;
};
uniform sampler2D _MainTex;
uniform sampler2D _FaceTex;
uniform float4 _FaceTex_ST;
uniform fixed4 _FaceColor;
uniform float _VertexOffsetX;
uniform float _VertexOffsetY;
uniform float4 _ClipRect;
uniform float _MaskSoftnessX;
uniform float _MaskSoftnessY;
float2 UnpackUV(float uv)
{
float2 output;
output.x = floor(uv / 4096);
output.y = uv - 4096 * output.x;
return output * 0.001953125;
}
v2f vert (appdata_t v)
{
float4 vert = v.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
float4 vPosition = UnityPixelSnap(UnityObjectToClipPos(vert));
fixed4 faceColor = v.color;
faceColor *= _FaceColor;
v2f OUT;
OUT.vertex = vPosition;
OUT.color = faceColor;
OUT.texcoord0 = v.texcoord0;
OUT.texcoord1 = TRANSFORM_TEX(UnpackUV(v.texcoord1), _FaceTex);
float2 pixelSize = vPosition.w;
pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
// Clamp _ClipRect to 16bit.
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
OUT.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
return OUT;
}
fixed4 frag (v2f IN) : SV_Target
{
fixed4 color = tex2D(_MainTex, IN.texcoord0) * tex2D(_FaceTex, IN.texcoord1) * IN.color;
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_BitmapShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 48bb5f55d8670e349b6e614913f9d910
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,145 @@
Shader "TextMeshPro/Mobile/Bitmap" {
Properties {
_MainTex ("Font Atlas", 2D) = "white" {}
[HDR]_Color ("Text Color", Color) = (1,1,1,1)
_DiffusePower ("Diffuse Power", Range(1.0,4.0)) = 1.0
_VertexOffsetX("Vertex OffsetX", float) = 0
_VertexOffsetY("Vertex OffsetY", float) = 0
_MaskSoftnessX("Mask SoftnessX", float) = 0
_MaskSoftnessY("Mask SoftnessY", float) = 0
_ClipRect("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_CullMode("Cull Mode", Float) = 0
_ColorMask("Color Mask", Float) = 15
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Lighting Off
Cull [_CullMode]
ZTest [unity_GUIZTestMode]
ZWrite Off
Fog { Mode Off }
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct v2f {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float4 mask : TEXCOORD2;
};
sampler2D _MainTex;
fixed4 _Color;
float _DiffusePower;
uniform float _VertexOffsetX;
uniform float _VertexOffsetY;
uniform float4 _ClipRect;
uniform float _MaskSoftnessX;
uniform float _MaskSoftnessY;
v2f vert (appdata_t v)
{
v2f OUT;
float4 vert = v.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
OUT.vertex = UnityPixelSnap(UnityObjectToClipPos(vert));
OUT.color = v.color;
OUT.color *= _Color;
OUT.color.rgb *= _DiffusePower;
OUT.texcoord0 = v.texcoord0;
float2 pixelSize = OUT.vertex.w;
//pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
// Clamp _ClipRect to 16bit.
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
OUT.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
return OUT;
}
fixed4 frag (v2f IN) : COLOR
{
fixed4 color = fixed4(IN.color.rgb, IN.color.a * tex2D(_MainTex, IN.texcoord0).a);
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
Lighting Off Cull Off ZTest Always ZWrite Off Fog { Mode Off }
Blend SrcAlpha OneMinusSrcAlpha
BindChannels {
Bind "Color", color
Bind "Vertex", vertex
Bind "TexCoord", texcoord0
}
Pass {
SetTexture [_MainTex] {
constantColor [_Color] combine constant * primary, constant * texture
}
}
}
CustomEditor "TMPro.EditorUtilities.TMP_BitmapShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 1e3b057af24249748ff873be7fafee47
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,143 @@
Shader "TextMeshPro/Bitmap" {
Properties {
_MainTex ("Font Atlas", 2D) = "white" {}
_FaceTex ("Font Texture", 2D) = "white" {}
[HDR]_FaceColor ("Text Color", Color) = (1,1,1,1)
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_ClipRect("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_StencilComp("Stencil Comparison", Float) = 8
_Stencil("Stencil ID", Float) = 0
_StencilOp("Stencil Operation", Float) = 0
_StencilWriteMask("Stencil Write Mask", Float) = 255
_StencilReadMask("Stencil Read Mask", Float) = 255
_CullMode("Cull Mode", Float) = 0
_ColorMask("Color Mask", Float) = 15
}
SubShader{
Tags { "Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent" }
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Lighting Off
Cull [_CullMode]
ZTest [unity_GUIZTestMode]
ZWrite Off
Fog { Mode Off }
Blend SrcAlpha OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
struct appdata_t {
float4 vertex : POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct v2f {
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
float4 mask : TEXCOORD2;
};
uniform sampler2D _MainTex;
uniform sampler2D _FaceTex;
uniform float4 _FaceTex_ST;
uniform fixed4 _FaceColor;
uniform float _VertexOffsetX;
uniform float _VertexOffsetY;
uniform float4 _ClipRect;
uniform float _MaskSoftnessX;
uniform float _MaskSoftnessY;
float2 UnpackUV(float uv)
{
float2 output;
output.x = floor(uv / 4096);
output.y = uv - 4096 * output.x;
return output * 0.001953125;
}
v2f vert (appdata_t v)
{
float4 vert = v.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
vert.xy += (vert.w * 0.5) / _ScreenParams.xy;
float4 vPosition = UnityPixelSnap(UnityObjectToClipPos(vert));
fixed4 faceColor = v.color;
faceColor *= _FaceColor;
v2f OUT;
OUT.vertex = vPosition;
OUT.color = faceColor;
OUT.texcoord0 = v.texcoord0;
OUT.texcoord1 = TRANSFORM_TEX(UnpackUV(v.texcoord1), _FaceTex);
float2 pixelSize = vPosition.w;
pixelSize /= abs(float2(_ScreenParams.x * UNITY_MATRIX_P[0][0], _ScreenParams.y * UNITY_MATRIX_P[1][1]));
// Clamp _ClipRect to 16bit.
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
OUT.mask = float4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
return OUT;
}
fixed4 frag (v2f IN) : SV_Target
{
fixed4 color = tex2D(_MainTex, IN.texcoord0);
color = fixed4 (tex2D(_FaceTex, IN.texcoord1).rgb * IN.color.rgb, IN.color.a * color.a);
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_BitmapShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 128e987d567d4e2c824d754223b3f3b0
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,317 @@
Shader "TextMeshPro/Distance Field Overlay" {
Properties {
_FaceTex ("Face Texture", 2D) = "white" {}
_FaceUVSpeedX ("Face UV Speed X", Range(-5, 5)) = 0.0
_FaceUVSpeedY ("Face UV Speed Y", Range(-5, 5)) = 0.0
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineTex ("Outline Texture", 2D) = "white" {}
_OutlineUVSpeedX ("Outline UV Speed X", Range(-5, 5)) = 0.0
_OutlineUVSpeedY ("Outline UV Speed Y", Range(-5, 5)) = 0.0
_OutlineWidth ("Outline Thickness", Range(0, 1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
_Bevel ("Bevel", Range(0,1)) = 0.5
_BevelOffset ("Bevel Offset", Range(-0.5,0.5)) = 0
_BevelWidth ("Bevel Width", Range(-.5,0.5)) = 0
_BevelClamp ("Bevel Clamp", Range(0,1)) = 0
_BevelRoundness ("Bevel Roundness", Range(0,1)) = 0
_LightAngle ("Light Angle", Range(0.0, 6.2831853)) = 3.1416
[HDR]_SpecularColor ("Specular", Color) = (1,1,1,1)
_SpecularPower ("Specular", Range(0,4)) = 2.0
_Reflectivity ("Reflectivity", Range(5.0,15.0)) = 10
_Diffuse ("Diffuse", Range(0,1)) = 0.5
_Ambient ("Ambient", Range(1,0)) = 0.5
_BumpMap ("Normal map", 2D) = "bump" {}
_BumpOutline ("Bump Outline", Range(0,1)) = 0
_BumpFace ("Bump Face", Range(0,1)) = 0
_ReflectFaceColor ("Reflection Color", Color) = (0,0,0,1)
_ReflectOutlineColor("Reflection Color", Color) = (0,0,0,1)
_Cube ("Reflection Cubemap", Cube) = "black" { /* TexGen CubeReflect */ }
_EnvMatrixRotation ("Texture Rotation", vector) = (0, 0, 0, 0)
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0, 0.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
[HDR]_GlowColor ("Color", Color) = (0, 1, 0, 0.5)
_GlowOffset ("Offset", Range(-1,1)) = 0
_GlowInner ("Inner", Range(0,1)) = 0.05
_GlowOuter ("Outer", Range(0,1)) = 0.05
_GlowPower ("Falloff", Range(1, 0)) = 0.75
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = 0.5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5.0
_ScaleX ("Scale X", float) = 1.0
_ScaleY ("Scale Y", float) = 1.0
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_MaskCoord ("Mask Coordinates", vector) = (0, 0, 32767, 32767)
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Overlay"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest Always
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ BEVEL_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma shader_feature __ GLOW_ON
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
#include "TMPro.cginc"
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 position : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 position : SV_POSITION;
fixed4 color : COLOR;
float2 atlas : TEXCOORD0; // Atlas
float4 param : TEXCOORD1; // alphaClip, scale, bias, weight
float4 mask : TEXCOORD2; // Position in object space(xy), pixel Size(zw)
float3 viewDir : TEXCOORD3;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 texcoord2 : TEXCOORD4; // u,v, scale, bias
fixed4 underlayColor : COLOR1;
#endif
float4 textures : TEXCOORD5;
};
// Used by Unity internally to handle Texture Tiling and Offset.
float4 _FaceTex_ST;
float4 _OutlineTex_ST;
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input,output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.position;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
if (UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
float bias =(.5 - weight) + (.5 / scale);
float alphaClip = (1.0 - _OutlineWidth*_ScaleRatioA - _OutlineSoftness*_ScaleRatioA);
#if GLOW_ON
alphaClip = min(alphaClip, 1.0 - _GlowOffset * _ScaleRatioB - _GlowOuter * _ScaleRatioB);
#endif
alphaClip = alphaClip / 2.0 - ( .5 / scale) - weight;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 underlayColor = _UnderlayColor;
underlayColor.rgb *= underlayColor.a;
float bScale = scale;
bScale /= 1 + ((_UnderlaySoftness*_ScaleRatioC) * bScale);
float bBias = (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale);
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 bOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
// Support for texture tiling and offset
float2 textureUV = UnpackUV(input.texcoord1.x);
float2 faceUV = TRANSFORM_TEX(textureUV, _FaceTex);
float2 outlineUV = TRANSFORM_TEX(textureUV, _OutlineTex);
output.position = vPosition;
output.color = input.color;
output.atlas = input.texcoord0;
output.param = float4(alphaClip, scale, bias, weight);
output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
output.viewDir = mul((float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz);
#if (UNDERLAY_ON || UNDERLAY_INNER)
output.texcoord2 = float4(input.texcoord0 + bOffset, bScale, bBias);
output.underlayColor = underlayColor;
#endif
output.textures = float4(faceUV, outlineUV);
return output;
}
fixed4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float c = tex2D(_MainTex, input.atlas).a;
#ifndef UNDERLAY_ON
clip(c - input.param.x);
#endif
float scale = input.param.y;
float bias = input.param.z;
float weight = input.param.w;
float sd = (bias - c) * scale;
float outline = (_OutlineWidth * _ScaleRatioA) * scale;
float softness = (_OutlineSoftness * _ScaleRatioA) * scale;
half4 faceColor = _FaceColor;
half4 outlineColor = _OutlineColor;
faceColor.rgb *= input.color.rgb;
faceColor *= tex2D(_FaceTex, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y);
outlineColor *= tex2D(_OutlineTex, input.textures.zw + float2(_OutlineUVSpeedX, _OutlineUVSpeedY) * _Time.y);
faceColor = GetColor(sd, faceColor, outlineColor, outline, softness);
#if BEVEL_ON
float3 dxy = float3(0.5 / _TextureWidth, 0.5 / _TextureHeight, 0);
float3 n = GetSurfaceNormal(input.atlas, weight, dxy);
float3 bump = UnpackNormal(tex2D(_BumpMap, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y)).xyz;
bump *= lerp(_BumpFace, _BumpOutline, saturate(sd + outline * 0.5));
n = normalize(n- bump);
float3 light = normalize(float3(sin(_LightAngle), cos(_LightAngle), -1.0));
float3 col = GetSpecular(n, light);
faceColor.rgb += col*faceColor.a;
faceColor.rgb *= 1-(dot(n, light)*_Diffuse);
faceColor.rgb *= lerp(_Ambient, 1, n.z*n.z);
fixed4 reflcol = texCUBE(_Cube, reflect(input.viewDir, -n));
faceColor.rgb += reflcol.rgb * lerp(_ReflectFaceColor.rgb, _ReflectOutlineColor.rgb, saturate(sd + outline * 0.5)) * faceColor.a;
#endif
#if UNDERLAY_ON
float d = tex2D(_MainTex, input.texcoord2.xy).a * input.texcoord2.z;
faceColor += input.underlayColor * saturate(d - input.texcoord2.w) * (1 - faceColor.a);
#endif
#if UNDERLAY_INNER
float d = tex2D(_MainTex, input.texcoord2.xy).a * input.texcoord2.z;
faceColor += input.underlayColor * (1 - saturate(d - input.texcoord2.w)) * saturate(1 - sd) * (1 - faceColor.a);
#endif
#if GLOW_ON
float4 glowColor = GetGlowColor(sd, scale);
faceColor.rgb += glowColor.rgb * glowColor.a;
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * input.mask.zw);
faceColor *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(faceColor.a - 0.001);
#endif
return faceColor * input.color.a;
}
ENDCG
}
}
Fallback "TextMeshPro/Mobile/Distance Field"
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: dd89cf5b9246416f84610a006f916af7
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,310 @@
Shader "TextMeshPro/Distance Field SSD" {
Properties {
_FaceTex ("Face Texture", 2D) = "white" {}
_FaceUVSpeedX ("Face UV Speed X", Range(-5, 5)) = 0.0
_FaceUVSpeedY ("Face UV Speed Y", Range(-5, 5)) = 0.0
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineTex ("Outline Texture", 2D) = "white" {}
_OutlineUVSpeedX ("Outline UV Speed X", Range(-5, 5)) = 0.0
_OutlineUVSpeedY ("Outline UV Speed Y", Range(-5, 5)) = 0.0
_OutlineWidth ("Outline Thickness", Range(0, 1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
_Bevel ("Bevel", Range(0,1)) = 0.5
_BevelOffset ("Bevel Offset", Range(-0.5,0.5)) = 0
_BevelWidth ("Bevel Width", Range(-.5,0.5)) = 0
_BevelClamp ("Bevel Clamp", Range(0,1)) = 0
_BevelRoundness ("Bevel Roundness", Range(0,1)) = 0
_LightAngle ("Light Angle", Range(0.0, 6.2831853)) = 3.1416
[HDR]_SpecularColor ("Specular", Color) = (1,1,1,1)
_SpecularPower ("Specular", Range(0,4)) = 2.0
_Reflectivity ("Reflectivity", Range(5.0,15.0)) = 10
_Diffuse ("Diffuse", Range(0,1)) = 0.5
_Ambient ("Ambient", Range(1,0)) = 0.5
_BumpMap ("Normal map", 2D) = "bump" {}
_BumpOutline ("Bump Outline", Range(0,1)) = 0
_BumpFace ("Bump Face", Range(0,1)) = 0
_ReflectFaceColor ("Reflection Color", Color) = (0,0,0,1)
_ReflectOutlineColor("Reflection Color", Color) = (0,0,0,1)
_Cube ("Reflection Cubemap", Cube) = "black" { /* TexGen CubeReflect */ }
_EnvMatrixRotation ("Texture Rotation", vector) = (0, 0, 0, 0)
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0, 0.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
[HDR]_GlowColor ("Color", Color) = (0, 1, 0, 0.5)
_GlowOffset ("Offset", Range(-1,1)) = 0
_GlowInner ("Inner", Range(0,1)) = 0.05
_GlowOuter ("Outer", Range(0,1)) = 0.05
_GlowPower ("Falloff", Range(1, 0)) = 0.75
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = 0.5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5.0
_ScaleX ("Scale X", float) = 1.0
_ScaleY ("Scale Y", float) = 1.0
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_MaskCoord ("Mask Coordinates", vector) = (0, 0, 32767, 32767)
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue" = "Transparent"
"IgnoreProjector" = "True"
"RenderType" = "Transparent"
}
Stencil
{
Ref[_Stencil]
Comp[_StencilComp]
Pass[_StencilOp]
ReadMask[_StencilReadMask]
WriteMask[_StencilWriteMask]
}
Cull[_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest[unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask[_ColorMask]
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ BEVEL_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma shader_feature __ GLOW_ON
#pragma shader_feature __ FORCE_LINEAR
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
#include "TMPro.cginc"
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 position : POSITION;
float3 normal : NORMAL;
float4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 position : SV_POSITION;
float4 color : COLOR;
float2 atlas : TEXCOORD0;
float weight : TEXCOORD1;
float2 mask : TEXCOORD2; // Position in object space(xy)
float3 viewDir : TEXCOORD3;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float2 texcoord2 : TEXCOORD4;
float4 underlayColor : COLOR1;
#endif
float4 textures : TEXCOORD5;
};
// Used by Unity internally to handle Texture Tiling and Offset.
float4 _FaceTex_ST;
float4 _OutlineTex_ST;
float4 SRGBToLinear(float4 rgba) {
return float4(lerp(rgba.rgb / 12.92f, pow((rgba.rgb + 0.055f) / 1.055f, 2.4f), step(0.04045f, rgba.rgb)), rgba.a);
}
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input,output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.position;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 underlayColor = _UnderlayColor;
underlayColor.rgb *= underlayColor.a;
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 bOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
// Support for texture tiling and offset
float2 textureUV = UnpackUV(input.texcoord1.x);
float2 faceUV = TRANSFORM_TEX(textureUV, _FaceTex);
float2 outlineUV = TRANSFORM_TEX(textureUV, _OutlineTex);
float4 color = input.color;
#if (FORCE_LINEAR && !UNITY_COLORSPACE_GAMMA)
color = SRGBToLinear(input.color);
#endif
output.position = vPosition;
output.color = color;
output.atlas = input.texcoord0;
output.weight = weight;
output.mask = half2(vert.xy * 2 - clampedRect.xy - clampedRect.zw);
output.viewDir = mul((float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz);
#if (UNDERLAY_ON || UNDERLAY_INNER)
output.texcoord2 = input.texcoord0 + bOffset;
output.underlayColor = underlayColor;
#endif
output.textures = float4(faceUV, outlineUV);
return output;
}
fixed4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float c = tex2D(_MainTex, input.atlas).a;
float2 pixelSize = float2(ddx(input.atlas.y), ddy(input.atlas.y));
pixelSize *= _TextureWidth * .75;
float scale = rsqrt(dot(pixelSize, pixelSize)) * _GradientScale * (_Sharpness + 1);
float weight = input.weight;
float bias = (.5 - weight) + (.5 / scale);
float sd = (bias - c) * scale;
float outline = (_OutlineWidth * _ScaleRatioA) * scale;
float softness = (_OutlineSoftness * _ScaleRatioA) * scale;
half4 faceColor = _FaceColor;
half4 outlineColor = _OutlineColor;
faceColor.rgb *= input.color.rgb;
faceColor *= tex2D(_FaceTex, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y);
outlineColor *= tex2D(_OutlineTex, input.textures.zw + float2(_OutlineUVSpeedX, _OutlineUVSpeedY) * _Time.y);
faceColor = GetColor(sd, faceColor, outlineColor, outline, softness);
#if BEVEL_ON
float3 dxy = float3(0.5 / _TextureWidth, 0.5 / _TextureHeight, 0);
float3 n = GetSurfaceNormal(input.atlas, weight, dxy);
float3 bump = UnpackNormal(tex2D(_BumpMap, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y)).xyz;
bump *= lerp(_BumpFace, _BumpOutline, saturate(sd + outline * 0.5));
n = normalize(n - bump);
float3 light = normalize(float3(sin(_LightAngle), cos(_LightAngle), -1.0));
float3 col = GetSpecular(n, light);
faceColor.rgb += col * faceColor.a;
faceColor.rgb *= 1 - (dot(n, light) * _Diffuse);
faceColor.rgb *= lerp(_Ambient, 1, n.z * n.z);
fixed4 reflcol = texCUBE(_Cube, reflect(input.viewDir, -n));
faceColor.rgb += reflcol.rgb * lerp(_ReflectFaceColor.rgb, _ReflectOutlineColor.rgb, saturate(sd + outline * 0.5)) * faceColor.a;
#endif
#if (UNDERLAY_ON || UNDERLAY_INNER)
float bScale = scale;
bScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * bScale);
float bBias = (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale);
#endif
#if UNDERLAY_ON
float d = tex2D(_MainTex, input.texcoord2.xy).a * bScale;
faceColor += input.underlayColor * saturate(d - bBias) * (1 - faceColor.a);
#endif
#if UNDERLAY_INNER
float d = tex2D(_MainTex, input.texcoord2.xy).a * bScale;
faceColor += input.underlayColor * (1 - saturate(d - bBias)) * saturate(1 - sd) * (1 - faceColor.a);
#endif
#if GLOW_ON
float4 glowColor = GetGlowColor(sd, scale);
faceColor.rgb += glowColor.rgb * glowColor.a;
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
float2 maskZW = 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + (1 / scale));
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * maskZW);
faceColor *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(faceColor.a - 0.001);
#endif
return faceColor * input.color.a;
}
ENDCG
}
}
Fallback "TextMeshPro/Mobile/Distance Field"
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 14eb328de4b8eb245bb7cea29e4ac00b
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,247 @@
// Simplified SDF shader:
// - No Shading Option (bevel / bump / env map)
// - No Glow Option
// - Softness is applied on both side of the outline
Shader "TextMeshPro/Mobile/Distance Field - Masking" {
Properties {
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineWidth ("Outline Thickness", Range(0,1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0,.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = .5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5
_ScaleX ("Scale X", float) = 1
_ScaleY ("Scale Y", float) = 1
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_MaskTex ("Mask Texture", 2D) = "white" {}
_MaskInverse ("Inverse", float) = 0
_MaskEdgeColor ("Edge Color", Color) = (1,1,1,1)
_MaskEdgeSoftness ("Edge Softness", Range(0, 1)) = 0.01
_MaskWipeControl ("Wipe Position", Range(0, 1)) = 0.5
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ OUTLINE_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
struct vertex_t {
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
float4 vertex : SV_POSITION;
fixed4 faceColor : COLOR;
fixed4 outlineColor : COLOR1;
float4 texcoord0 : TEXCOORD0; // Texture UV, Mask UV
half4 param : TEXCOORD1; // Scale(x), BiasIn(y), BiasOut(z), Bias(w)
half4 mask : TEXCOORD2; // Position in clip space(xy), Softness(zw)
#if (UNDERLAY_ON | UNDERLAY_INNER)
float4 texcoord1 : TEXCOORD3; // Texture UV, alpha, reserved
half2 underlayParam : TEXCOORD4; // Scale(x), Bias(y)
#endif
};
float _MaskWipeControl;
float _MaskEdgeSoftness;
fixed4 _MaskEdgeColor;
bool _MaskInverse;
pixel_t VertShader(vertex_t input)
{
float bold = step(input.texcoord1.y, 0);
float4 vert = input.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
if(UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
float layerScale = scale;
scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale);
float bias = (0.5 - weight) * scale - 0.5;
float outline = _OutlineWidth * _ScaleRatioA * 0.5 * scale;
float opacity = input.color.a;
#if (UNDERLAY_ON | UNDERLAY_INNER)
opacity = 1.0;
#endif
fixed4 faceColor = fixed4(input.color.rgb, opacity) * _FaceColor;
faceColor.rgb *= faceColor.a;
fixed4 outlineColor = _OutlineColor;
outlineColor.a *= opacity;
outlineColor.rgb *= outlineColor.a;
outlineColor = lerp(faceColor, outlineColor, sqrt(min(1.0, (outline * 2))));
#if (UNDERLAY_ON | UNDERLAY_INNER)
layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale);
float layerBias = (.5 - weight) * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale);
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 layerOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
// Structure for pixel shader
pixel_t output = {
vPosition,
faceColor,
outlineColor,
float4(input.texcoord0.x, input.texcoord0.y, maskUV.x, maskUV.y),
half4(scale, bias - outline, bias + outline, bias),
half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy)),
#if (UNDERLAY_ON | UNDERLAY_INNER)
float4(input.texcoord0 + layerOffset, input.color.a, 0),
half2(layerScale, layerBias),
#endif
};
return output;
}
// PIXEL SHADER
fixed4 PixShader(pixel_t input) : SV_Target
{
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
half4 c = input.faceColor * saturate(d - input.param.w);
#ifdef OUTLINE_ON
c = lerp(input.outlineColor, input.faceColor, saturate(d - input.param.z));
c *= saturate(d - input.param.y);
#endif
#if UNDERLAY_ON
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - input.underlayParam.y) * (1 - c.a);
#endif
#if UNDERLAY_INNER
half sd = saturate(d - input.param.z);
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - input.underlayParam.y)) * sd * (1 - c.a);
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
//#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * input.mask.zw);
c *= m.x * m.y;
//#endif
float a = abs(_MaskInverse - tex2D(_MaskTex, input.texcoord0.zw).a);
float t = a + (1 - _MaskWipeControl) * _MaskEdgeSoftness - _MaskWipeControl;
a = saturate(t / _MaskEdgeSoftness);
c.rgb = lerp(_MaskEdgeColor.rgb*c.a, c.rgb, a);
c *= a;
#if (UNDERLAY_ON | UNDERLAY_INNER)
c *= input.texcoord1.z;
#endif
#if UNITY_UI_ALPHACLIP
clip(c.a - 0.001);
#endif
return c;
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: bc1ede39bf3643ee8e493720e4259791
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,240 @@
// Simplified SDF shader:
// - No Shading Option (bevel / bump / env map)
// - No Glow Option
// - Softness is applied on both side of the outline
Shader "TextMeshPro/Mobile/Distance Field Overlay" {
Properties {
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineWidth ("Outline Thickness", Range(0,1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0,.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = .5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5
_ScaleX ("Scale X", float) = 1
_ScaleY ("Scale Y", float) = 1
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Overlay"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest Always
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ OUTLINE_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 vertex : SV_POSITION;
fixed4 faceColor : COLOR;
fixed4 outlineColor : COLOR1;
float4 texcoord0 : TEXCOORD0; // Texture UV, Mask UV
half4 param : TEXCOORD1; // Scale(x), BiasIn(y), BiasOut(z), Bias(w)
half4 mask : TEXCOORD2; // Position in clip space(xy), Softness(zw)
#if (UNDERLAY_ON | UNDERLAY_INNER)
float4 texcoord1 : TEXCOORD3; // Texture UV, alpha, reserved
half2 underlayParam : TEXCOORD4; // Scale(x), Bias(y)
#endif
};
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
if(UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
float layerScale = scale;
scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale);
float bias = (0.5 - weight) * scale - 0.5;
float outline = _OutlineWidth * _ScaleRatioA * 0.5 * scale;
float opacity = input.color.a;
#if (UNDERLAY_ON | UNDERLAY_INNER)
opacity = 1.0;
#endif
fixed4 faceColor = fixed4(input.color.rgb, opacity) * _FaceColor;
faceColor.rgb *= faceColor.a;
fixed4 outlineColor = _OutlineColor;
outlineColor.a *= opacity;
outlineColor.rgb *= outlineColor.a;
outlineColor = lerp(faceColor, outlineColor, sqrt(min(1.0, (outline * 2))));
#if (UNDERLAY_ON | UNDERLAY_INNER)
layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale);
float layerBias = (.5 - weight) * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale);
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 layerOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
// Populate structure for pixel shader
output.vertex = vPosition;
output.faceColor = faceColor;
output.outlineColor = outlineColor;
output.texcoord0 = float4(input.texcoord0.x, input.texcoord0.y, maskUV.x, maskUV.y);
output.param = half4(scale, bias - outline, bias + outline, bias);
output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
#if (UNDERLAY_ON || UNDERLAY_INNER)
output.texcoord1 = float4(input.texcoord0 + layerOffset, input.color.a, 0);
output.underlayParam = half2(layerScale, layerBias);
#endif
return output;
}
// PIXEL SHADER
fixed4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
half4 c = input.faceColor * saturate(d - input.param.w);
#ifdef OUTLINE_ON
c = lerp(input.outlineColor, input.faceColor, saturate(d - input.param.z));
c *= saturate(d - input.param.y);
#endif
#if UNDERLAY_ON
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - input.underlayParam.y) * (1 - c.a);
#endif
#if UNDERLAY_INNER
half sd = saturate(d - input.param.z);
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - input.underlayParam.y)) * sd * (1 - c.a);
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * input.mask.zw);
c *= m.x * m.y;
#endif
#if (UNDERLAY_ON | UNDERLAY_INNER)
c *= input.texcoord1.z;
#endif
#if UNITY_UI_ALPHACLIP
clip(c.a - 0.001);
#endif
return c;
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: a02a7d8c237544f1962732b55a9aebf1
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,106 @@
// Simplified SDF shader:
// - No Shading Option (bevel / bump / env map)
// - No Glow Option
// - Softness is applied on both side of the outline
Shader "TextMeshPro/Mobile/Distance Field SSD" {
Properties {
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineWidth ("Outline Thickness", Range(0,1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0,.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = .5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5
_ScaleX ("Scale X", float) = 1
_ScaleY ("Scale Y", float) = 1
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_MaskTex ("Mask Texture", 2D) = "white" {}
_MaskInverse ("Inverse", float) = 0
_MaskEdgeColor ("Edge Color", Color) = (1,1,1,1)
_MaskEdgeSoftness ("Edge Softness", Range(0, 1)) = 0.01
_MaskWipeControl ("Wipe Position", Range(0, 1)) = 0.5
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags {
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ OUTLINE_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
#include "TMPro_Mobile.cginc"
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c8d12adcee749c344b8117cf7c7eb912
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,240 @@
// Simplified SDF shader:
// - No Shading Option (bevel / bump / env map)
// - No Glow Option
// - Softness is applied on both side of the outline
Shader "TextMeshPro/Mobile/Distance Field" {
Properties {
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineWidth ("Outline Thickness", Range(0,1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0,.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = .5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5
_ScaleX ("Scale X", float) = 1
_ScaleY ("Scale Y", float) = 1
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ OUTLINE_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 vertex : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 vertex : SV_POSITION;
fixed4 faceColor : COLOR;
fixed4 outlineColor : COLOR1;
float4 texcoord0 : TEXCOORD0; // Texture UV, Mask UV
half4 param : TEXCOORD1; // Scale(x), BiasIn(y), BiasOut(z), Bias(w)
half4 mask : TEXCOORD2; // Position in clip space(xy), Softness(zw)
#if (UNDERLAY_ON | UNDERLAY_INNER)
float4 texcoord1 : TEXCOORD3; // Texture UV, alpha, reserved
half2 underlayParam : TEXCOORD4; // Scale(x), Bias(y)
#endif
};
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.vertex;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
if(UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
float layerScale = scale;
scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale);
float bias = (0.5 - weight) * scale - 0.5;
float outline = _OutlineWidth * _ScaleRatioA * 0.5 * scale;
float opacity = input.color.a;
#if (UNDERLAY_ON | UNDERLAY_INNER)
opacity = 1.0;
#endif
fixed4 faceColor = fixed4(input.color.rgb, opacity) * _FaceColor;
faceColor.rgb *= faceColor.a;
fixed4 outlineColor = _OutlineColor;
outlineColor.a *= opacity;
outlineColor.rgb *= outlineColor.a;
outlineColor = lerp(faceColor, outlineColor, sqrt(min(1.0, (outline * 2))));
#if (UNDERLAY_ON | UNDERLAY_INNER)
layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale);
float layerBias = (.5 - weight) * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale);
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 layerOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
// Populate structure for pixel shader
output.vertex = vPosition;
output.faceColor = faceColor;
output.outlineColor = outlineColor;
output.texcoord0 = float4(input.texcoord0.x, input.texcoord0.y, maskUV.x, maskUV.y);
output.param = half4(scale, bias - outline, bias + outline, bias);
output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
#if (UNDERLAY_ON || UNDERLAY_INNER)
output.texcoord1 = float4(input.texcoord0 + layerOffset, input.color.a, 0);
output.underlayParam = half2(layerScale, layerBias);
#endif
return output;
}
// PIXEL SHADER
fixed4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
half d = tex2D(_MainTex, input.texcoord0.xy).a * input.param.x;
half4 c = input.faceColor * saturate(d - input.param.w);
#ifdef OUTLINE_ON
c = lerp(input.outlineColor, input.faceColor, saturate(d - input.param.z));
c *= saturate(d - input.param.y);
#endif
#if UNDERLAY_ON
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - input.underlayParam.y) * (1 - c.a);
#endif
#if UNDERLAY_INNER
half sd = saturate(d - input.param.z);
d = tex2D(_MainTex, input.texcoord1.xy).a * input.underlayParam.x;
c += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - input.underlayParam.y)) * sd * (1 - c.a);
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * input.mask.zw);
c *= m.x * m.y;
#endif
#if (UNDERLAY_ON | UNDERLAY_INNER)
c *= input.texcoord1.z;
#endif
#if UNITY_UI_ALPHACLIP
clip(c.a - 0.001);
#endif
return c;
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: fe393ace9b354375a9cb14cdbbc28be4
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,138 @@
// Simplified version of the SDF Surface shader :
// - No support for Bevel, Bump or envmap
// - Diffuse only lighting
// - Fully supports only 1 directional light. Other lights can affect it, but it will be per-vertex/SH.
Shader "TextMeshPro/Mobile/Distance Field (Surface)" {
Properties {
_FaceTex ("Fill Texture", 2D) = "white" {}
[HDR]_FaceColor ("Fill Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineTex ("Outline Texture", 2D) = "white" {}
_OutlineWidth ("Outline Thickness", Range(0, 1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
[HDR]_GlowColor ("Color", Color) = (0, 1, 0, 0.5)
_GlowOffset ("Offset", Range(-1,1)) = 0
_GlowInner ("Inner", Range(0,1)) = 0.05
_GlowOuter ("Outer", Range(0,1)) = 0.05
_GlowPower ("Falloff", Range(1, 0)) = 0.75
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = 0.5
// Should not be directly exposed to the user
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5.0
_ScaleX ("Scale X", float) = 1.0
_ScaleY ("Scale Y", float) = 1.0
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_CullMode ("Cull Mode", Float) = 0
//_MaskCoord ("Mask Coords", vector) = (0,0,0,0)
//_MaskSoftness ("Mask Softness", float) = 0
}
SubShader {
Tags {
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
LOD 300
Cull [_CullMode]
CGPROGRAM
#pragma surface PixShader Lambert alpha:blend vertex:VertShader noforwardadd nolightmap nodirlightmap
#pragma target 3.0
#pragma shader_feature __ GLOW_ON
#include "TMPro_Properties.cginc"
#include "TMPro.cginc"
half _FaceShininess;
half _OutlineShininess;
struct Input
{
fixed4 color : COLOR;
float2 uv_MainTex;
float2 uv2_FaceTex;
float2 uv2_OutlineTex;
float2 param; // Weight, Scale
float3 viewDirEnv;
};
#include "TMPro_Surface.cginc"
ENDCG
// Pass to render object as a shadow caster
Pass
{
Name "Caster"
Tags { "LightMode" = "ShadowCaster" }
Offset 1, 1
Fog {Mode Off}
ZWrite On ZTest LEqual Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
float2 uv2 : TEXCOORD3;
float alphaClip : TEXCOORD2;
};
uniform float4 _MainTex_ST;
uniform float4 _OutlineTex_ST;
float _OutlineWidth;
float _FaceDilate;
float _ScaleRatioA;
v2f vert( appdata_base v )
{
v2f o;
TRANSFER_SHADOW_CASTER(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _OutlineTex);
o.alphaClip = o.alphaClip = (1.0 - _OutlineWidth * _ScaleRatioA - _FaceDilate * _ScaleRatioA) / 2;
return o;
}
uniform sampler2D _MainTex;
float4 frag(v2f i) : COLOR
{
fixed4 texcol = tex2D(_MainTex, i.uv).a;
clip(texcol.a - i.alphaClip);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 85187c2149c549c5b33f0cdb02836b17
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,158 @@
Shader "TextMeshPro/Distance Field (Surface)" {
Properties {
_FaceTex ("Fill Texture", 2D) = "white" {}
_FaceUVSpeedX ("Face UV Speed X", Range(-5, 5)) = 0.0
_FaceUVSpeedY ("Face UV Speed Y", Range(-5, 5)) = 0.0
[HDR]_FaceColor ("Fill Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineTex ("Outline Texture", 2D) = "white" {}
_OutlineUVSpeedX ("Outline UV Speed X", Range(-5, 5)) = 0.0
_OutlineUVSpeedY ("Outline UV Speed Y", Range(-5, 5)) = 0.0
_OutlineWidth ("Outline Thickness", Range(0, 1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
_Bevel ("Bevel", Range(0,1)) = 0.5
_BevelOffset ("Bevel Offset", Range(-0.5,0.5)) = 0
_BevelWidth ("Bevel Width", Range(-.5,0.5)) = 0
_BevelClamp ("Bevel Clamp", Range(0,1)) = 0
_BevelRoundness ("Bevel Roundness", Range(0,1)) = 0
_BumpMap ("Normalmap", 2D) = "bump" {}
_BumpOutline ("Bump Outline", Range(0,1)) = 0.5
_BumpFace ("Bump Face", Range(0,1)) = 0.5
_ReflectFaceColor ("Face Color", Color) = (0,0,0,1)
_ReflectOutlineColor ("Outline Color", Color) = (0,0,0,1)
_Cube ("Reflection Cubemap", Cube) = "black" { /* TexGen CubeReflect */ }
_EnvMatrixRotation ("Texture Rotation", vector) = (0, 0, 0, 0)
[HDR]_SpecColor ("Specular Color", Color) = (0,0,0,1)
_FaceShininess ("Face Shininess", Range(0,1)) = 0
_OutlineShininess ("Outline Shininess", Range(0,1)) = 0
[HDR]_GlowColor ("Color", Color) = (0, 1, 0, 0.5)
_GlowOffset ("Offset", Range(-1,1)) = 0
_GlowInner ("Inner", Range(0,1)) = 0.05
_GlowOuter ("Outer", Range(0,1)) = 0.05
_GlowPower ("Falloff", Range(1, 0)) = 0.75
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = 0.5
// Should not be directly exposed to the user
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5.0
_ScaleX ("Scale X", float) = 1.0
_ScaleY ("Scale Y", float) = 1.0
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_CullMode ("Cull Mode", Float) = 0
//_MaskCoord ("Mask Coords", vector) = (0,0,0,0)
//_MaskSoftness ("Mask Softness", float) = 0
}
SubShader {
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
LOD 300
Cull [_CullMode]
CGPROGRAM
#pragma surface PixShader BlinnPhong alpha:blend vertex:VertShader nolightmap nodirlightmap
#pragma target 3.0
#pragma shader_feature __ GLOW_ON
#pragma glsl
#include "TMPro_Properties.cginc"
#include "TMPro.cginc"
half _FaceShininess;
half _OutlineShininess;
struct Input
{
fixed4 color : COLOR;
float2 uv_MainTex;
float2 uv2_FaceTex;
float2 uv2_OutlineTex;
float2 param; // Weight, Scale
float3 viewDirEnv;
};
#define BEVEL_ON 1
#include "TMPro_Surface.cginc"
ENDCG
// Pass to render object as a shadow caster
Pass
{
Name "Caster"
Tags { "LightMode" = "ShadowCaster" }
Offset 1, 1
Fog {Mode Off}
ZWrite On
ZTest LEqual
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f {
V2F_SHADOW_CASTER;
float2 uv : TEXCOORD1;
float2 uv2 : TEXCOORD3;
float alphaClip : TEXCOORD2;
};
uniform float4 _MainTex_ST;
uniform float4 _OutlineTex_ST;
float _OutlineWidth;
float _FaceDilate;
float _ScaleRatioA;
v2f vert( appdata_base v )
{
v2f o;
TRANSFER_SHADOW_CASTER(o)
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv2 = TRANSFORM_TEX(v.texcoord, _OutlineTex);
o.alphaClip = (1.0 - _OutlineWidth * _ScaleRatioA - _FaceDilate * _ScaleRatioA) / 2;
return o;
}
uniform sampler2D _MainTex;
float4 frag(v2f i) : COLOR
{
fixed4 texcol = tex2D(_MainTex, i.uv).a;
clip(texcol.a - i.alphaClip);
SHADOW_CASTER_FRAGMENT(i)
}
ENDCG
}
}
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: f7ada0af4f174f0694ca6a487b8f543d
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,317 @@
Shader "TextMeshPro/Distance Field" {
Properties {
_FaceTex ("Face Texture", 2D) = "white" {}
_FaceUVSpeedX ("Face UV Speed X", Range(-5, 5)) = 0.0
_FaceUVSpeedY ("Face UV Speed Y", Range(-5, 5)) = 0.0
[HDR]_FaceColor ("Face Color", Color) = (1,1,1,1)
_FaceDilate ("Face Dilate", Range(-1,1)) = 0
[HDR]_OutlineColor ("Outline Color", Color) = (0,0,0,1)
_OutlineTex ("Outline Texture", 2D) = "white" {}
_OutlineUVSpeedX ("Outline UV Speed X", Range(-5, 5)) = 0.0
_OutlineUVSpeedY ("Outline UV Speed Y", Range(-5, 5)) = 0.0
_OutlineWidth ("Outline Thickness", Range(0, 1)) = 0
_OutlineSoftness ("Outline Softness", Range(0,1)) = 0
_Bevel ("Bevel", Range(0,1)) = 0.5
_BevelOffset ("Bevel Offset", Range(-0.5,0.5)) = 0
_BevelWidth ("Bevel Width", Range(-.5,0.5)) = 0
_BevelClamp ("Bevel Clamp", Range(0,1)) = 0
_BevelRoundness ("Bevel Roundness", Range(0,1)) = 0
_LightAngle ("Light Angle", Range(0.0, 6.2831853)) = 3.1416
[HDR]_SpecularColor ("Specular", Color) = (1,1,1,1)
_SpecularPower ("Specular", Range(0,4)) = 2.0
_Reflectivity ("Reflectivity", Range(5.0,15.0)) = 10
_Diffuse ("Diffuse", Range(0,1)) = 0.5
_Ambient ("Ambient", Range(1,0)) = 0.5
_BumpMap ("Normal map", 2D) = "bump" {}
_BumpOutline ("Bump Outline", Range(0,1)) = 0
_BumpFace ("Bump Face", Range(0,1)) = 0
_ReflectFaceColor ("Reflection Color", Color) = (0,0,0,1)
_ReflectOutlineColor("Reflection Color", Color) = (0,0,0,1)
_Cube ("Reflection Cubemap", Cube) = "black" { /* TexGen CubeReflect */ }
_EnvMatrixRotation ("Texture Rotation", vector) = (0, 0, 0, 0)
[HDR]_UnderlayColor ("Border Color", Color) = (0,0,0, 0.5)
_UnderlayOffsetX ("Border OffsetX", Range(-1,1)) = 0
_UnderlayOffsetY ("Border OffsetY", Range(-1,1)) = 0
_UnderlayDilate ("Border Dilate", Range(-1,1)) = 0
_UnderlaySoftness ("Border Softness", Range(0,1)) = 0
[HDR]_GlowColor ("Color", Color) = (0, 1, 0, 0.5)
_GlowOffset ("Offset", Range(-1,1)) = 0
_GlowInner ("Inner", Range(0,1)) = 0.05
_GlowOuter ("Outer", Range(0,1)) = 0.05
_GlowPower ("Falloff", Range(1, 0)) = 0.75
_WeightNormal ("Weight Normal", float) = 0
_WeightBold ("Weight Bold", float) = 0.5
_ShaderFlags ("Flags", float) = 0
_ScaleRatioA ("Scale RatioA", float) = 1
_ScaleRatioB ("Scale RatioB", float) = 1
_ScaleRatioC ("Scale RatioC", float) = 1
_MainTex ("Font Atlas", 2D) = "white" {}
_TextureWidth ("Texture Width", float) = 512
_TextureHeight ("Texture Height", float) = 512
_GradientScale ("Gradient Scale", float) = 5.0
_ScaleX ("Scale X", float) = 1.0
_ScaleY ("Scale Y", float) = 1.0
_PerspectiveFilter ("Perspective Correction", Range(0, 1)) = 0.875
_Sharpness ("Sharpness", Range(-1,1)) = 0
_VertexOffsetX ("Vertex OffsetX", float) = 0
_VertexOffsetY ("Vertex OffsetY", float) = 0
_MaskCoord ("Mask Coordinates", vector) = (0, 0, 32767, 32767)
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
_MaskSoftnessX ("Mask SoftnessX", float) = 0
_MaskSoftnessY ("Mask SoftnessY", float) = 0
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
}
SubShader {
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
ZWrite Off
Lighting Off
Fog { Mode Off }
ZTest [unity_GUIZTestMode]
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass {
CGPROGRAM
#pragma target 3.0
#pragma vertex VertShader
#pragma fragment PixShader
#pragma shader_feature __ BEVEL_ON
#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER
#pragma shader_feature __ GLOW_ON
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#include "TMPro_Properties.cginc"
#include "TMPro.cginc"
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 position : POSITION;
float3 normal : NORMAL;
fixed4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 position : SV_POSITION;
fixed4 color : COLOR;
float2 atlas : TEXCOORD0; // Atlas
float4 param : TEXCOORD1; // alphaClip, scale, bias, weight
float4 mask : TEXCOORD2; // Position in object space(xy), pixel Size(zw)
float3 viewDir : TEXCOORD3;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 texcoord2 : TEXCOORD4; // u,v, scale, bias
fixed4 underlayColor : COLOR1;
#endif
float4 textures : TEXCOORD5;
};
// Used by Unity internally to handle Texture Tiling and Offset.
float4 _FaceTex_ST;
float4 _OutlineTex_ST;
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input,output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.position;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(input.texcoord1.y) * _GradientScale * (_Sharpness + 1);
if (UNITY_MATRIX_P[3][3] == 0) scale = lerp(abs(scale) * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(input.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
float bias =(.5 - weight) + (.5 / scale);
float alphaClip = (1.0 - _OutlineWidth * _ScaleRatioA - _OutlineSoftness * _ScaleRatioA);
#if GLOW_ON
alphaClip = min(alphaClip, 1.0 - _GlowOffset * _ScaleRatioB - _GlowOuter * _ScaleRatioB);
#endif
alphaClip = alphaClip / 2.0 - ( .5 / scale) - weight;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 underlayColor = _UnderlayColor;
underlayColor.rgb *= underlayColor.a;
float bScale = scale;
bScale /= 1 + ((_UnderlaySoftness*_ScaleRatioC) * bScale);
float bBias = (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale);
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
float2 bOffset = float2(x, y);
#endif
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
// Support for texture tiling and offset
float2 textureUV = UnpackUV(input.texcoord1.x);
float2 faceUV = TRANSFORM_TEX(textureUV, _FaceTex);
float2 outlineUV = TRANSFORM_TEX(textureUV, _OutlineTex);
output.position = vPosition;
output.color = input.color;
output.atlas = input.texcoord0;
output.param = float4(alphaClip, scale, bias, weight);
output.mask = half4(vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + pixelSize.xy));
output.viewDir = mul((float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz);
#if (UNDERLAY_ON || UNDERLAY_INNER)
output.texcoord2 = float4(input.texcoord0 + bOffset, bScale, bBias);
output.underlayColor = underlayColor;
#endif
output.textures = float4(faceUV, outlineUV);
return output;
}
fixed4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float c = tex2D(_MainTex, input.atlas).a;
#ifndef UNDERLAY_ON
clip(c - input.param.x);
#endif
float scale = input.param.y;
float bias = input.param.z;
float weight = input.param.w;
float sd = (bias - c) * scale;
float outline = (_OutlineWidth * _ScaleRatioA) * scale;
float softness = (_OutlineSoftness * _ScaleRatioA) * scale;
half4 faceColor = _FaceColor;
half4 outlineColor = _OutlineColor;
faceColor.rgb *= input.color.rgb;
faceColor *= tex2D(_FaceTex, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y);
outlineColor *= tex2D(_OutlineTex, input.textures.zw + float2(_OutlineUVSpeedX, _OutlineUVSpeedY) * _Time.y);
faceColor = GetColor(sd, faceColor, outlineColor, outline, softness);
#if BEVEL_ON
float3 dxy = float3(0.5 / _TextureWidth, 0.5 / _TextureHeight, 0);
float3 n = GetSurfaceNormal(input.atlas, weight, dxy);
float3 bump = UnpackNormal(tex2D(_BumpMap, input.textures.xy + float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y)).xyz;
bump *= lerp(_BumpFace, _BumpOutline, saturate(sd + outline * 0.5));
n = normalize(n- bump);
float3 light = normalize(float3(sin(_LightAngle), cos(_LightAngle), -1.0));
float3 col = GetSpecular(n, light);
faceColor.rgb += col*faceColor.a;
faceColor.rgb *= 1-(dot(n, light)*_Diffuse);
faceColor.rgb *= lerp(_Ambient, 1, n.z*n.z);
fixed4 reflcol = texCUBE(_Cube, reflect(input.viewDir, -n));
faceColor.rgb += reflcol.rgb * lerp(_ReflectFaceColor.rgb, _ReflectOutlineColor.rgb, saturate(sd + outline * 0.5)) * faceColor.a;
#endif
#if UNDERLAY_ON
float d = tex2D(_MainTex, input.texcoord2.xy).a * input.texcoord2.z;
faceColor += input.underlayColor * saturate(d - input.texcoord2.w) * (1 - faceColor.a);
#endif
#if UNDERLAY_INNER
float d = tex2D(_MainTex, input.texcoord2.xy).a * input.texcoord2.z;
faceColor += input.underlayColor * (1 - saturate(d - input.texcoord2.w)) * saturate(1 - sd) * (1 - faceColor.a);
#endif
#if GLOW_ON
float4 glowColor = GetGlowColor(sd, scale);
faceColor.rgb += glowColor.rgb * glowColor.a;
#endif
// Alternative implementation to UnityGet2DClipping with support for softness.
#if UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * input.mask.zw);
faceColor *= m.x * m.y;
#endif
#if UNITY_UI_ALPHACLIP
clip(faceColor.a - 0.001);
#endif
return faceColor * input.color.a;
}
ENDCG
}
}
Fallback "TextMeshPro/Mobile/Distance Field"
CustomEditor "TMPro.EditorUtilities.TMP_SDFShaderGUI"
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 68e6db2ebdc24f95958faec2be5558d6
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,116 @@
Shader "TextMeshPro/Sprite"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_CullMode ("Cull Mode", Float) = 0
_ColorMask ("Color Mask", Float) = 15
_ClipRect ("Clip Rect", vector) = (-32767, -32767, 32767, 32767)
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull [_CullMode]
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile __ UNITY_UI_CLIP_RECT
#pragma multi_compile __ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
OUT.worldPosition = v.vertex;
OUT.vertex = UnityObjectToClipPos(OUT.worldPosition);
OUT.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
OUT.color = v.color * _Color;
return OUT;
}
fixed4 frag(v2f IN) : SV_Target
{
half4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
#ifdef UNITY_UI_CLIP_RECT
color.a *= UnityGet2DClipping(IN.worldPosition.xy, _ClipRect);
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
return color;
}
ENDCG
}
}
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: cf81c85f95fe47e1a27f6ae460cf182c
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,84 @@
float2 UnpackUV(float uv)
{
float2 output;
output.x = floor(uv / 4096);
output.y = uv - 4096 * output.x;
return output * 0.001953125;
}
fixed4 GetColor(half d, fixed4 faceColor, fixed4 outlineColor, half outline, half softness)
{
half faceAlpha = 1-saturate((d - outline * 0.5 + softness * 0.5) / (1.0 + softness));
half outlineAlpha = saturate((d + outline * 0.5)) * sqrt(min(1.0, outline));
faceColor.rgb *= faceColor.a;
outlineColor.rgb *= outlineColor.a;
faceColor = lerp(faceColor, outlineColor, outlineAlpha);
faceColor *= faceAlpha;
return faceColor;
}
float3 GetSurfaceNormal(float4 h, float bias)
{
bool raisedBevel = step(1, fmod(_ShaderFlags, 2));
h += bias+_BevelOffset;
float bevelWidth = max(.01, _OutlineWidth+_BevelWidth);
// Track outline
h -= .5;
h /= bevelWidth;
h = saturate(h+.5);
if(raisedBevel) h = 1 - abs(h*2.0 - 1.0);
h = lerp(h, sin(h*3.141592/2.0), _BevelRoundness);
h = min(h, 1.0-_BevelClamp);
h *= _Bevel * bevelWidth * _GradientScale * -2.0;
float3 va = normalize(float3(1.0, 0.0, h.y - h.x));
float3 vb = normalize(float3(0.0, -1.0, h.w - h.z));
return cross(va, vb);
}
float3 GetSurfaceNormal(float2 uv, float bias, float3 delta)
{
// Read "height field"
float4 h = {tex2D(_MainTex, uv - delta.xz).a,
tex2D(_MainTex, uv + delta.xz).a,
tex2D(_MainTex, uv - delta.zy).a,
tex2D(_MainTex, uv + delta.zy).a};
return GetSurfaceNormal(h, bias);
}
float3 GetSpecular(float3 n, float3 l)
{
float spec = pow(max(0.0, dot(n, l)), _Reflectivity);
return _SpecularColor.rgb * spec * _SpecularPower;
}
float4 GetGlowColor(float d, float scale)
{
float glow = d - (_GlowOffset*_ScaleRatioB) * 0.5 * scale;
float t = lerp(_GlowInner, (_GlowOuter * _ScaleRatioB), step(0.0, glow)) * 0.5 * scale;
glow = saturate(abs(glow/(1.0 + t)));
glow = 1.0-pow(glow, _GlowPower);
glow *= sqrt(min(1.0, t)); // Fade off glow thinner than 1 screen pixel
return float4(_GlowColor.rgb, saturate(_GlowColor.a * glow * 2));
}
float4 BlendARGB(float4 overlying, float4 underlying)
{
overlying.rgb *= overlying.a;
underlying.rgb *= underlying.a;
float3 blended = overlying.rgb + ((1-overlying.a)*underlying.rgb);
float alpha = underlying.a + (1-underlying.a)*overlying.a;
return float4(blended, alpha);
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 407bc68d299748449bbf7f48ee690f8d
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,157 @@
struct vertex_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
float4 position : POSITION;
float3 normal : NORMAL;
float4 color : COLOR;
float2 texcoord0 : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
};
struct pixel_t {
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
float4 position : SV_POSITION;
float4 faceColor : COLOR;
float4 outlineColor : COLOR1;
float4 texcoord0 : TEXCOORD0;
float4 param : TEXCOORD1; // weight, scaleRatio
float2 mask : TEXCOORD2;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 texcoord2 : TEXCOORD3;
float4 underlayColor : COLOR2;
#endif
};
float4 SRGBToLinear(float4 rgba) {
return float4(lerp(rgba.rgb / 12.92f, pow((rgba.rgb + 0.055f) / 1.055f, 2.4f), step(0.04045f, rgba.rgb)), rgba.a);
}
pixel_t VertShader(vertex_t input)
{
pixel_t output;
UNITY_INITIALIZE_OUTPUT(pixel_t, output);
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float bold = step(input.texcoord1.y, 0);
float4 vert = input.position;
vert.x += _VertexOffsetX;
vert.y += _VertexOffsetY;
float4 vPosition = UnityObjectToClipPos(vert);
float weight = lerp(_WeightNormal, _WeightBold, bold) / 4.0;
weight = (weight + _FaceDilate) * _ScaleRatioA * 0.5;
// Generate UV for the Masking Texture
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
float4 color = input.color;
#if (FORCE_LINEAR && !UNITY_COLORSPACE_GAMMA)
color = SRGBToLinear(input.color);
#endif
float opacity = color.a;
#if (UNDERLAY_ON | UNDERLAY_INNER)
opacity = 1.0;
#endif
float4 faceColor = float4(color.rgb, opacity) * _FaceColor;
faceColor.rgb *= faceColor.a;
float4 outlineColor = _OutlineColor;
outlineColor.a *= opacity;
outlineColor.rgb *= outlineColor.a;
output.position = vPosition;
output.faceColor = faceColor;
output.outlineColor = outlineColor;
output.texcoord0 = float4(input.texcoord0.xy, maskUV.xy);
output.param = float4(0.5 - weight, 1.3333 * _GradientScale * (_Sharpness + 1) / _TextureWidth, _OutlineWidth * _ScaleRatioA * 0.5, 0);
float2 mask = float2(0, 0);
#if UNITY_UI_CLIP_RECT
mask = vert.xy * 2 - clampedRect.xy - clampedRect.zw;
#endif
output.mask = mask;
#if (UNDERLAY_ON || UNDERLAY_INNER)
float4 underlayColor = _UnderlayColor;
underlayColor.rgb *= underlayColor.a;
float x = -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth;
float y = -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;
output.texcoord2 = float4(input.texcoord0 + float2(x, y), input.color.a, 0);
output.underlayColor = underlayColor;
#endif
return output;
}
float4 PixShader(pixel_t input) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float d = tex2D(_MainTex, input.texcoord0.xy).a;
float2 UV = input.texcoord0.xy;
float scale = rsqrt(abs(ddx(UV.x) * ddy(UV.y) - ddy(UV.x) * ddx(UV.y))) * input.param.y;
#if (UNDERLAY_ON | UNDERLAY_INNER)
float layerScale = scale;
layerScale /= 1 + ((_UnderlaySoftness * _ScaleRatioC) * layerScale);
float layerBias = input.param.x * layerScale - .5 - ((_UnderlayDilate * _ScaleRatioC) * .5 * layerScale);
#endif
scale /= 1 + (_OutlineSoftness * _ScaleRatioA * scale);
float4 faceColor = input.faceColor * saturate((d - input.param.x) * scale + 0.5);
#ifdef OUTLINE_ON
float4 outlineColor = lerp(input.faceColor, input.outlineColor, sqrt(min(1.0, input.param.z * scale * 2)));
faceColor = lerp(outlineColor, input.faceColor, saturate((d - input.param.x - input.param.z) * scale + 0.5));
faceColor *= saturate((d - input.param.x + input.param.z) * scale + 0.5);
#endif
#if UNDERLAY_ON
d = tex2D(_MainTex, input.texcoord2.xy).a * layerScale;
faceColor += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * saturate(d - layerBias) * (1 - faceColor.a);
#endif
#if UNDERLAY_INNER
float bias = input.param.x * scale - 0.5;
float sd = saturate(d * scale - bias - input.param.z);
d = tex2D(_MainTex, input.texcoord2.xy).a * layerScale;
faceColor += float4(_UnderlayColor.rgb * _UnderlayColor.a, _UnderlayColor.a) * (1 - saturate(d - layerBias)) * sd * (1 - faceColor.a);
#endif
#ifdef MASKING
float a = abs(_MaskInverse - tex2D(_MaskTex, input.texcoord0.zw).a);
float t = a + (1 - _MaskWipeControl) * _MaskEdgeSoftness - _MaskWipeControl;
a = saturate(t / _MaskEdgeSoftness);
faceColor.rgb = lerp(_MaskEdgeColor.rgb * faceColor.a, faceColor.rgb, a);
faceColor *= a;
#endif
// Alternative implementation to UnityGet2DClipping with support for softness
#if UNITY_UI_CLIP_RECT
float2 maskZW = 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) + (1 / scale));
float2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(input.mask.xy)) * maskZW);
faceColor *= m.x * m.y;
#endif
#if (UNDERLAY_ON | UNDERLAY_INNER)
faceColor *= input.texcoord2.z;
#endif
#if UNITY_UI_ALPHACLIP
clip(faceColor.a - 0.001);
#endif
return faceColor;
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: c334973cef89a9840b0b0c507e0377ab
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,85 @@
// UI Editable properties
uniform sampler2D _FaceTex; // Alpha : Signed Distance
uniform float _FaceUVSpeedX;
uniform float _FaceUVSpeedY;
uniform fixed4 _FaceColor; // RGBA : Color + Opacity
uniform float _FaceDilate; // v[ 0, 1]
uniform float _OutlineSoftness; // v[ 0, 1]
uniform sampler2D _OutlineTex; // RGBA : Color + Opacity
uniform float _OutlineUVSpeedX;
uniform float _OutlineUVSpeedY;
uniform fixed4 _OutlineColor; // RGBA : Color + Opacity
uniform float _OutlineWidth; // v[ 0, 1]
uniform float _Bevel; // v[ 0, 1]
uniform float _BevelOffset; // v[-1, 1]
uniform float _BevelWidth; // v[-1, 1]
uniform float _BevelClamp; // v[ 0, 1]
uniform float _BevelRoundness; // v[ 0, 1]
uniform sampler2D _BumpMap; // Normal map
uniform float _BumpOutline; // v[ 0, 1]
uniform float _BumpFace; // v[ 0, 1]
uniform samplerCUBE _Cube; // Cube / sphere map
uniform fixed4 _ReflectFaceColor; // RGB intensity
uniform fixed4 _ReflectOutlineColor;
//uniform float _EnvTiltX; // v[-1, 1]
//uniform float _EnvTiltY; // v[-1, 1]
uniform float3 _EnvMatrixRotation;
uniform float4x4 _EnvMatrix;
uniform fixed4 _SpecularColor; // RGB intensity
uniform float _LightAngle; // v[ 0,Tau]
uniform float _SpecularPower; // v[ 0, 1]
uniform float _Reflectivity; // v[ 5, 15]
uniform float _Diffuse; // v[ 0, 1]
uniform float _Ambient; // v[ 0, 1]
uniform fixed4 _UnderlayColor; // RGBA : Color + Opacity
uniform float _UnderlayOffsetX; // v[-1, 1]
uniform float _UnderlayOffsetY; // v[-1, 1]
uniform float _UnderlayDilate; // v[-1, 1]
uniform float _UnderlaySoftness; // v[ 0, 1]
uniform fixed4 _GlowColor; // RGBA : Color + Intesity
uniform float _GlowOffset; // v[-1, 1]
uniform float _GlowOuter; // v[ 0, 1]
uniform float _GlowInner; // v[ 0, 1]
uniform float _GlowPower; // v[ 1, 1/(1+4*4)]
// API Editable properties
uniform float _ShaderFlags;
uniform float _WeightNormal;
uniform float _WeightBold;
uniform float _ScaleRatioA;
uniform float _ScaleRatioB;
uniform float _ScaleRatioC;
uniform float _VertexOffsetX;
uniform float _VertexOffsetY;
//uniform float _UseClipRect;
uniform float _MaskID;
uniform sampler2D _MaskTex;
uniform float4 _MaskCoord;
uniform float4 _ClipRect; // bottom left(x,y) : top right(z,w)
//uniform float _MaskWipeControl;
//uniform float _MaskEdgeSoftness;
//uniform fixed4 _MaskEdgeColor;
//uniform bool _MaskInverse;
uniform float _MaskSoftnessX;
uniform float _MaskSoftnessY;
// Font Atlas properties
uniform sampler2D _MainTex;
uniform float _TextureWidth;
uniform float _TextureHeight;
uniform float _GradientScale;
uniform float _ScaleX;
uniform float _ScaleY;
uniform float _PerspectiveFilter;
uniform float _Sharpness;

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: 3997e2241185407d80309a82f9148466
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,101 @@
void VertShader(inout appdata_full v, out Input data)
{
v.vertex.x += _VertexOffsetX;
v.vertex.y += _VertexOffsetY;
UNITY_INITIALIZE_OUTPUT(Input, data);
float bold = step(v.texcoord1.y, 0);
// Generate normal for backface
float3 view = ObjSpaceViewDir(v.vertex);
v.normal *= sign(dot(v.normal, view));
#if USE_DERIVATIVE
data.param.y = 1;
#else
float4 vert = v.vertex;
float4 vPosition = UnityObjectToClipPos(vert);
float2 pixelSize = vPosition.w;
pixelSize /= float2(_ScaleX, _ScaleY) * mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy);
float scale = rsqrt(dot(pixelSize, pixelSize));
scale *= abs(v.texcoord1.y) * _GradientScale * (_Sharpness + 1);
scale = lerp(scale * (1 - _PerspectiveFilter), scale, abs(dot(UnityObjectToWorldNormal(v.normal.xyz), normalize(WorldSpaceViewDir(vert)))));
data.param.y = scale;
#endif
data.param.x = (lerp(_WeightNormal, _WeightBold, bold) / 4.0 + _FaceDilate) * _ScaleRatioA * 0.5; //
v.texcoord1.xy = UnpackUV(v.texcoord1.x);
data.viewDirEnv = mul((float3x3)_EnvMatrix, WorldSpaceViewDir(v.vertex));
}
void PixShader(Input input, inout SurfaceOutput o)
{
#if USE_DERIVATIVE
float2 pixelSize = float2(ddx(input.uv_MainTex.y), ddy(input.uv_MainTex.y));
pixelSize *= _TextureWidth * .75;
float scale = rsqrt(dot(pixelSize, pixelSize)) * _GradientScale * (_Sharpness + 1);
#else
float scale = input.param.y;
#endif
// Signed distance
float c = tex2D(_MainTex, input.uv_MainTex).a;
float sd = (.5 - c - input.param.x) * scale + .5;
float outline = _OutlineWidth*_ScaleRatioA * scale;
float softness = _OutlineSoftness*_ScaleRatioA * scale;
// Color & Alpha
float4 faceColor = _FaceColor;
float4 outlineColor = _OutlineColor;
faceColor *= input.color;
outlineColor.a *= input.color.a;
faceColor *= tex2D(_FaceTex, float2(input.uv2_FaceTex.x + _FaceUVSpeedX * _Time.y, input.uv2_FaceTex.y + _FaceUVSpeedY * _Time.y));
outlineColor *= tex2D(_OutlineTex, float2(input.uv2_OutlineTex.x + _OutlineUVSpeedX * _Time.y, input.uv2_OutlineTex.y + _OutlineUVSpeedY * _Time.y));
faceColor = GetColor(sd, faceColor, outlineColor, outline, softness);
faceColor.rgb /= max(faceColor.a, 0.0001);
#if BEVEL_ON
float3 delta = float3(1.0 / _TextureWidth, 1.0 / _TextureHeight, 0.0);
float4 smp4x = {tex2D(_MainTex, input.uv_MainTex - delta.xz).a,
tex2D(_MainTex, input.uv_MainTex + delta.xz).a,
tex2D(_MainTex, input.uv_MainTex - delta.zy).a,
tex2D(_MainTex, input.uv_MainTex + delta.zy).a };
// Face Normal
float3 n = GetSurfaceNormal(smp4x, input.param.x);
// Bumpmap
float3 bump = UnpackNormal(tex2D(_BumpMap, input.uv2_FaceTex.xy)).xyz;
bump *= lerp(_BumpFace, _BumpOutline, saturate(sd + outline * 0.5));
bump = lerp(float3(0, 0, 1), bump, faceColor.a);
n = normalize(n - bump);
// Cubemap reflection
fixed4 reflcol = texCUBE(_Cube, reflect(input.viewDirEnv, mul((float3x3)unity_ObjectToWorld, n)));
float3 emission = reflcol.rgb * lerp(_ReflectFaceColor.rgb, _ReflectOutlineColor.rgb, saturate(sd + outline * 0.5)) * faceColor.a;
#else
float3 n = float3(0, 0, -1);
float3 emission = float3(0, 0, 0);
#endif
#if GLOW_ON
float4 glowColor = GetGlowColor(sd, scale);
glowColor.a *= input.color.a;
emission += glowColor.rgb*glowColor.a;
faceColor = BlendARGB(glowColor, faceColor);
faceColor.rgb /= max(faceColor.a, 0.0001);
#endif
// Set Standard output structure
o.Albedo = faceColor.rgb;
o.Normal = -n;
o.Emission = emission;
o.Specular = lerp(_FaceShininess, _OutlineShininess, saturate(sd + outline * 0.5));
o.Gloss = 1;
o.Alpha = faceColor.a;
}

View File

@ -0,0 +1,9 @@
fileFormatVersion: 2
guid: d930090c0cd643c7b55f19a38538c162
ShaderImporter:
externalObjects: {}
defaultTextures: []
nonModifiableTextures: []
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: d0603b6d5186471b96c778c3949c7ce2
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,3 @@
This sample of beautiful emojis are provided by EmojiOne https://www.emojione.com/
Please visit their website to view the complete set of their emojis and review their licensing terms.

View File

@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: 381dcb09d5029d14897e55f98031fca5
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,156 @@
{"frames": [
{
"filename": "1f60a.png",
"frame": {"x":0,"y":0,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f60b.png",
"frame": {"x":128,"y":0,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f60d.png",
"frame": {"x":256,"y":0,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f60e.png",
"frame": {"x":384,"y":0,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f600.png",
"frame": {"x":0,"y":128,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f601.png",
"frame": {"x":128,"y":128,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f602.png",
"frame": {"x":256,"y":128,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f603.png",
"frame": {"x":384,"y":128,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f604.png",
"frame": {"x":0,"y":256,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f605.png",
"frame": {"x":128,"y":256,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f606.png",
"frame": {"x":256,"y":256,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f609.png",
"frame": {"x":384,"y":256,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f618.png",
"frame": {"x":0,"y":384,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "1f923.png",
"frame": {"x":128,"y":384,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "263a.png",
"frame": {"x":256,"y":384,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
},
{
"filename": "2639.png",
"frame": {"x":384,"y":384,"w":128,"h":128},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":128,"h":128},
"sourceSize": {"w":128,"h":128},
"pivot": {"x":0.5,"y":0.5}
}],
"meta": {
"app": "http://www.codeandweb.com/texturepacker",
"version": "1.0",
"image": "EmojiOne.png",
"format": "RGBA8888",
"size": {"w":512,"h":512},
"scale": "1",
"smartupdate": "$TexturePacker:SmartUpdate:196a26a2e149d875b91ffc8fa3581e76:fc928c7e275404b7e0649307410475cb:424723c3774975ddb2053fd5c4b85f6e$"
}
}

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 8f05276190cf498a8153f6cbe761d4e6
timeCreated: 1480316860
licenseType: Pro
TextScriptImporter:
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,431 @@
fileFormatVersion: 2
guid: dffef66376be4fa480fb02b19edbe903
TextureImporter:
fileIDToRecycleName:
21300000: EmojiOne_0
21300002: EmojiOne_1
21300004: EmojiOne_2
21300006: EmojiOne_3
21300008: EmojiOne_4
21300010: EmojiOne_6
21300012: EmojiOne_7
21300014: EmojiOne_8
21300016: EmojiOne_9
21300018: EmojiOne_10
21300020: EmojiOne_11
21300022: EmojiOne_12
21300024: EmojiOne_13
21300026: EmojiOne_5
21300028: EmojiOne_14
externalObjects: {}
serializedVersion: 5
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: -1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: -1
aniso: -1
mipBias: -1
wrapU: 1
wrapV: 1
wrapW: 1
nPOTScale: 0
lightmap: 0
compressionQuality: 50
spriteMode: 2
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spritePixelsToUnits: 100
alphaUsage: 1
alphaIsTransparency: 1
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
platformSettings:
- serializedVersion: 2
buildTarget: DefaultTexturePlatform
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Standalone
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: iPhone
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
- serializedVersion: 2
buildTarget: Android
maxTextureSize: 512
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
spriteSheet:
serializedVersion: 2
sprites:
- serializedVersion: 2
name: EmojiOne_0
rect:
serializedVersion: 2
x: 0
y: 384
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 4bcc36da2108f2c4ba3de5c921d25c3c
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_1
rect:
serializedVersion: 2
x: 128
y: 384
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: e9eea8093eaeaee4d901c4553f572c22
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_2
rect:
serializedVersion: 2
x: 256
y: 384
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 49451da35411dcc42a3692e39b0fde70
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_3
rect:
serializedVersion: 2
x: 384
y: 384
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: f65709664b924904790c850a50ca82bc
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_4
rect:
serializedVersion: 2
x: 0
y: 256
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 5b92c568a5ec9ad4b9ed90e271f1c9a8
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_6
rect:
serializedVersion: 2
x: 256
y: 256
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: b10f2b48b7281594bb8a24a6511a35af
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_7
rect:
serializedVersion: 2
x: 384
y: 256
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 10a600f9329dc2246a897e89f4d283cd
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_8
rect:
serializedVersion: 2
x: 0
y: 128
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 66cffa363b90ab14787d8a5b90cf4502
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_9
rect:
serializedVersion: 2
x: 128
y: 128
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 55cf3d409c9b89349b1e1bdc1cc224ad
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_10
rect:
serializedVersion: 2
x: 256
y: 128
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 2a9e58eaf96feef42bcefa1cf257193f
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_11
rect:
serializedVersion: 2
x: 384
y: 128
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 2489120affc155840ae6a7be2e93ce19
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_12
rect:
serializedVersion: 2
x: 0
y: 0
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: 412349a150598d14da4d7140df5c0286
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_13
rect:
serializedVersion: 2
x: 128
y: 0
width: 128
height: 128
alignment: 0
pivot: {x: 0.5, y: 0.5}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: a937464b42bb3634782dea34c6becb6c
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_5
rect:
serializedVersion: 2
x: 256
y: 0
width: 128
height: 128
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: b0f933b217682124dbfc5e6b89abe3d0
vertices: []
indices:
edges: []
weights: []
- serializedVersion: 2
name: EmojiOne_14
rect:
serializedVersion: 2
x: 128
y: 256
width: 128
height: 128
alignment: 0
pivot: {x: 0, y: 0}
border: {x: 0, y: 0, z: 0, w: 0}
outline: []
physicsShape: []
tessellationDetail: 0
bones: []
spriteID: f7235c763afe4434e8bb666750a41096
vertices: []
indices:
edges: []
weights: []
outline: []
physicsShape: []
bones: []
spriteID: 3e32d8f5477abfc43b19066e8ad5032e
vertices: []
indices:
edges: []
weights: []
spritePackingTag:
userData:
assetBundleName:
assetBundleVariant:

View File

@ -2,6 +2,7 @@
"dependencies": { "dependencies": {
"com.unity.xr.picoxr": "file:../../PICO-Unity-Integration-SDK-release_3.4.0", "com.unity.xr.picoxr": "file:../../PICO-Unity-Integration-SDK-release_3.4.0",
"com.unity.xr.management": "4.4.0", "com.unity.xr.management": "4.4.0",
"com.unity.textmeshpro": "3.0.6",
"com.unity.modules.androidjni": "1.0.0", "com.unity.modules.androidjni": "1.0.0",
"com.unity.modules.animation": "1.0.0", "com.unity.modules.animation": "1.0.0",
"com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.imageconversion": "1.0.0",

View File

@ -16,9 +16,18 @@
"dependencies": {}, "dependencies": {},
"url": "https://packages.unity.cn" "url": "https://packages.unity.cn"
}, },
"com.unity.textmeshpro": {
"version": "3.0.6",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.ugui": "1.0.0"
},
"url": "https://packages.unity.cn"
},
"com.unity.ugui": { "com.unity.ugui": {
"version": "1.0.0", "version": "1.0.0",
"depth": 2, "depth": 1,
"source": "builtin", "source": "builtin",
"dependencies": { "dependencies": {
"com.unity.modules.ui": "1.0.0", "com.unity.modules.ui": "1.0.0",
@ -39,14 +48,14 @@
"depth": 1, "depth": 1,
"source": "registry", "source": "registry",
"dependencies": { "dependencies": {
"com.unity.ugui": "1.0.0",
"com.unity.inputsystem": "1.7.0", "com.unity.inputsystem": "1.7.0",
"com.unity.mathematics": "1.2.6", "com.unity.mathematics": "1.2.6",
"com.unity.ugui": "1.0.0",
"com.unity.xr.core-utils": "2.2.3",
"com.unity.xr.legacyinputhelpers": "2.1.10",
"com.unity.modules.audio": "1.0.0", "com.unity.modules.audio": "1.0.0",
"com.unity.modules.imgui": "1.0.0", "com.unity.modules.imgui": "1.0.0",
"com.unity.modules.physics": "1.0.0" "com.unity.xr.core-utils": "2.2.3",
"com.unity.modules.physics": "1.0.0",
"com.unity.xr.legacyinputhelpers": "2.1.10"
}, },
"url": "https://packages.unity.cn" "url": "https://packages.unity.cn"
}, },
@ -134,7 +143,7 @@
}, },
"com.unity.modules.ui": { "com.unity.modules.ui": {
"version": "1.0.0", "version": "1.0.0",
"depth": 3, "depth": 2,
"source": "builtin", "source": "builtin",
"dependencies": {} "dependencies": {}
}, },

View File

@ -12,7 +12,7 @@ PlayerSettings:
targetDevice: 2 targetDevice: 2
useOnDemandResources: 0 useOnDemandResources: 0
accelerometerFrequency: 60 accelerometerFrequency: 60
companyName: DefaultCompany companyName: XR-RM
productName: XR-RM-PICO-UDP-Sender productName: XR-RM-PICO-UDP-Sender
defaultCursor: {fileID: 0} defaultCursor: {fileID: 0}
cursorHotspot: {x: 0, y: 0} cursorHotspot: {x: 0, y: 0}
@ -170,10 +170,10 @@ PlayerSettings:
iPhone: 0 iPhone: 0
tvOS: 0 tvOS: 0
overrideDefaultApplicationIdentifier: 1 overrideDefaultApplicationIdentifier: 1
AndroidBundleVersionCode: 1 AndroidBundleVersionCode: 3377155
AndroidMinSdkVersion: 29 AndroidMinSdkVersion: 29
AndroidTargetSdkVersion: 0 AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1 AndroidPreferredInstallLocation: 2
aotOptions: aotOptions:
stripEngineCode: 1 stripEngineCode: 1
iPhoneStrippingLevel: 0 iPhoneStrippingLevel: 0

View File

@ -8,11 +8,14 @@ Configured target:
```text ```text
Host = 192.168.9.99 Host = 192.168.9.99
Port = 15000 Port = 15000
Send Hz = 60 Send Hz = 90
Convert Unity To Project Coordinates = true Convert Unity To Project Coordinates = true
Package Name = com.local.xr_rm_udp_sender Package Name = com.local.xr_rm_udp_sender
``` ```
The default send rate is 90 Hz. If the network or headset performance is not
stable enough on site, lower it manually in the PICO panel to 60 Hz.
Open this folder with the Ubuntu editor installed at: Open this folder with the Ubuntu editor installed at:
```text ```text
@ -44,6 +47,14 @@ Runtime scene components:
disables PICO auto sleep, applies Android `KEEP_SCREEN_ON`, and restores disables PICO auto sleep, applies Android `KEEP_SCREEN_ON`, and restores
captured screen/sleep timeouts when sending stops. captured screen/sleep timeouts when sending stops.
Coordinate modes:
- `Project (+Z back)`: ROS-facing output. For `pose_source=pxr_predict`, the
measured PICO native pose is converted with `project.x=native.z`,
`project.y=native.y`, and `project.z=-native.x`.
- `Source raw`: sends the source pose without this conversion for field
diagnostics only.
The setup menu chooses the UDP target host from `XR_RM_UDP_TARGET_HOST` when The setup menu chooses the UDP target host from `XR_RM_UDP_TARGET_HOST` when
set. Otherwise it auto-detects a local IPv4 address; on this Ubuntu machine the set. Otherwise it auto-detects a local IPv4 address; on this Ubuntu machine the
current Wi-Fi address is `192.168.9.99`. current Wi-Fi address is `192.168.9.99`.

File diff suppressed because one or more lines are too long

View File

@ -18,6 +18,8 @@ from xr_rm_interfaces.msg import XrController
class UdpControllerReceiver(Node): class UdpControllerReceiver(Node):
"""将轻量级 XR UDP 数据包转换成 ROS2 左/右手柄消息。""" """将轻量级 XR UDP 数据包转换成 ROS2 左/右手柄消息。"""
_VALID_POSE_SOURCES = {"pxr_predict", "unity_xr", "none"}
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("udp_controller_receiver") super().__init__("udp_controller_receiver")
@ -127,7 +129,20 @@ class UdpControllerReceiver(Node):
msg.header.stamp = self.get_clock().now().to_msg() msg.header.stamp = self.get_clock().now().to_msg()
msg.header.frame_id = str(payload.get("frame_id", "xr_world")) msg.header.frame_id = str(payload.get("frame_id", "xr_world"))
msg.hand = hand msg.hand = hand
msg.grip = self._as_bool(payload.get("grip", payload.get("grip_button", False))) grip = self._as_bool(payload.get("grip", payload.get("grip_button", False)))
pose_valid = self._optional_bool(payload.get("pose_valid"), default=True)
pose_source = self._validate_pose_diagnostics(payload, hand)
if not pose_valid:
self.get_logger().warn(
f"{hand} XR pose_valid=false强制 grip=false"
f" source={pose_source or 'missing'}"
f" tracking_state={payload.get('tracking_state', 'missing')}"
f" controller_status={payload.get('controller_status', 'missing')}",
throttle_duration_sec=1.0,
)
grip = False
msg.grip = grip
msg.trigger = self._clamp_float(payload.get("trigger", 0.0), 0.0, 1.0) msg.trigger = self._clamp_float(payload.get("trigger", 0.0), 0.0, 1.0)
msg.pose.position.x = float(pos[0]) msg.pose.position.x = float(pos[0])
msg.pose.position.y = float(pos[1]) msg.pose.position.y = float(pos[1])
@ -144,6 +159,30 @@ class UdpControllerReceiver(Node):
msg.pose.orientation.w = float(w) msg.pose.orientation.w = float(w)
return msg return msg
def _validate_pose_diagnostics(self, payload: Mapping[str, Any], hand: str) -> str:
has_any_diagnostic = any(
key in payload for key in ("pose_valid", "pose_source", "tracking_state", "controller_status")
)
if has_any_diagnostic and "pose_valid" not in payload:
self.get_logger().warn(
f"{hand} XR 诊断字段缺少 pose_valid按旧协议处理 grip",
throttle_duration_sec=5.0,
)
if has_any_diagnostic and "pose_source" not in payload:
self.get_logger().warn(
f"{hand} XR 诊断字段缺少 pose_source",
throttle_duration_sec=5.0,
)
pose_source_value = payload.get("pose_source", "")
pose_source = str(pose_source_value).strip()
if pose_source and pose_source not in self._VALID_POSE_SOURCES:
self.get_logger().warn(
f"{hand} XR 未知 pose_source={pose_source!r}",
throttle_duration_sec=5.0,
)
return pose_source
@staticmethod @staticmethod
def _contains_pose(payload: Mapping[str, Any]) -> bool: def _contains_pose(payload: Mapping[str, Any]) -> bool:
return any(key in payload for key in ("pos", "position", "p", "pose")) return any(key in payload for key in ("pos", "position", "p", "pose"))
@ -203,6 +242,12 @@ class UdpControllerReceiver(Node):
return value.lower() in ("1", "true", "yes", "on") return value.lower() in ("1", "true", "yes", "on")
return bool(value) return bool(value)
@classmethod
def _optional_bool(cls, value: Any, default: bool) -> bool:
if value is None:
return default
return cls._as_bool(value)
@staticmethod @staticmethod
def _clamp_float(value: Any, low: float, high: float) -> float: def _clamp_float(value: Any, low: float, high: float) -> float:
number = float(value) number = float(value)