I have a question about generating point cloud from 3D model mesh in animation.
I try it following the steps below.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerControl : MonoBehaviour
{
// 移動スピード
public float speed;
// 前進or後退
bool FronFlag = true;
// 保存ファイル名
public string filename = "sample";
// 原点にあるオブジェクトの頂点を数えるか?
bool OriginSaveFlag = false;
// 時間計測のための変数
// private float time; //時間計測のための変数
// Start is called before the first frame update
void Start() {
if (OriginSaveFlag) {
// 頂点の数を数える
SkinnedMeshRenderer skin = GetComponentInChildren<SkinnedMeshRenderer>();
Mesh mesh = skin.sharedMesh;
int vtx_num = 0;
for (int i = 0; i < mesh.vertices.Length; i++) {
vtx_num++;
Vector3 item = mesh.vertices[i];
Debug.Log("No." + vtx_num + ", " + "Vertex Coordinates: " + item.ToString("F6"));
}
Debug.Log("Number of vertexes: " + vtx_num);
Vector3[] vtx_posi_array = new Vector3[vtx_num];
// 頂点座標を取得
int count = 0;
for(int i = 0; i < mesh.vertices.Length; i++) {
float x = mesh.vertices[i].x;
float y = mesh.vertices[i].y;
float z = mesh.vertices[i].z;
vtx_posi_array[count] = new Vector3(x, y, z);
count++;
}
// csvファイルに書き込む
try {
filename = filename + ".csv";
bool append = false;
Debug.Log("vtx_posi_array.Length: " + vtx_posi_array.Length);
using(var sw = new System.IO.StreamWriter(@filename, append)) {
for(int i = 0; i < vtx_posi_array.Length; ++i) {
sw.WriteLine("{0},{1},{2}", vtx_posi_array[i].x, vtx_posi_array[i].y, vtx_posi_array[i].z);
}
}
}
catch(System.Exception e) {
Debug.Log(e.Message);
}
}
}
// Update is called once per frame
void Update() {
// プレイヤの位置情報
Transform PlayerTransform = this.transform;
// 座標を取得
Vector3 pos = PlayerTransform.position;
// Debug.Log("座標: " + pos);
if (FronFlag) {
// 前に移動する
transform.position += transform.forward * speed * Time.deltaTime;
if (pos.z <= -10.0){
FronFlag = false;
}
} else {
// 後ろに移動する
transform.position -= transform.forward * speed * Time.deltaTime;
if (pos.z >= 0){
FronFlag = true;
}
}
SkinnedMeshRenderer skin = GetComponentInChildren<SkinnedMeshRenderer>();
Mesh mesh = skin.sharedMesh;
// Debug.Log("法線: " + mesh.uv.Length);
// Debug.Log("三角形: " + mesh.triangles.Length);
if (Input.GetKeyDown(KeyCode.S)) { // Game画面にて、キーボードを入力
Debug.Log("Since you put the S key, vertex coordinates are saved.");
// 頂点の数を数える
int vtx_num = 0;
for (int i = 0; i < mesh.vertices.Length; i++) {
vtx_num++;
Vector3 item = mesh.vertices[i];
Debug.Log("No." + vtx_num + ", " + "Vertex Coordinates: " + item.ToString("F6"));
}
Debug.Log("Number of vertexes: " + vtx_num);
Vector3[] vtx_posi_array = new Vector3[vtx_num];
// 頂点座標を取得
int count = 0;
for(int i = 0; i < mesh.vertices.Length; i++) {
float x = mesh.vertices[i].x;
float y = mesh.vertices[i].y;
float z = mesh.vertices[i].z;
vtx_posi_array[count] = new Vector3(x, y, z);
count++;
}
// csvファイルに書き込む
try {
filename = filename + ".csv";
bool append = false;
Debug.Log("vtx_posi_array.Length: " + vtx_posi_array.Length);
using(var sw = new System.IO.StreamWriter(@filename, append)) {
for(int i = 0; i < vtx_posi_array.Length; ++i) {
sw.WriteLine("{0},{1},{2}", vtx_posi_array[i].x, vtx_posi_array[i].y, vtx_posi_array[i].z);
}
}
}
catch(System.Exception e) {
Debug.Log(e.Message);
}
}
}
}
The method can generate static point cloud. In other words, the point cloud does not reflect a walking posture, e.g., movement of hands and feet, head.
In Step 4, I found a problem in that vertex coordinates had not changed when the 3D model is moving using Unity animation.
What should I do to solve the problem?
That is exactly the purpose of a SkinnedMeshRenderer
, that you can animate its bones without actually changing its underlying mesh data.
You could use BakeMesh
to create
a snapshot of SkinnedMeshRenderer and stores it in mesh.
The vertices are relative to the SkinnedMeshRenderer Transform component.
like e.g. (simplyfied a lot)
var targetMesh = new Mesh();
skin.BakeMesh(targetMesh);
be aware though that of course this is not the most performant method depending on the complexity of your mesh.
Regarding performance you have some other improvement potentials here like e.g.
private Mesh targetMesh = new Mesh();
so always the same one is re-usedskin
into a class field only oncealso note that this
int vtx_num = 0;
for (int i = 0; i < mesh.vertices.Length; i++)
{
vtx_num++;
Vector3 item = mesh.vertices[i];
Debug.Log("No." + vtx_num + ", " + "Vertex Coordinates: " + item.ToString("F6"));
}
Debug.Log("Number of vertexes: " + vtx_num);
Vector3[] vtx_posi_array = new Vector3[vtx_num];
// 頂点座標を取得
int count = 0;
for(int i = 0; i < mesh.vertices.Length; i++) {
float x = mesh.vertices[i].x;
float y = mesh.vertices[i].y;
float z = mesh.vertices[i].z;
vtx_posi_array[count] = new Vector3(x, y, z);
count++;
}
can/should be simplified to
var vtx_posi_array = mesh.vertices;
Debug.Log("Number of vertexes: " + vtx_posi_array.Length);
for(var i = 0; i < vtx_posi_array.Length; i++)
{
var item = vtx_posi_array[i];
Debug.Log($"No. {i}, Vertex Coordinates: {item.ToString("F6")}");
}
Reasons:
Vector3
is a struct and anyway always a copy by value, it is only more overhead to first store each component in a float
and then construct a Vector3
againi
already is your index. You have redundant vtx_num
and count
variablesmesh.vertices
is a property that every time returns a new copy of an Vector3[]
-> access it only once, then directly work on the returned array since it is just a copy