// Magica Cloth.
// Copyright (c) MagicaSoft, 2020-2022.
// https://magicasoft.jp
using System.Collections.Generic;
using UnityEngine;
namespace MagicaReductionMesh
{
public class Utility
{
///
/// 2つのインデックスを1つのUint型にパッキングする
/// 上位16ビット、下位16ビットにv0/v1番号を結合する
/// 番号が若いものが上位に来るように配置
///
///
///
///
public static uint PackPair(int v0, int v1)
{
if (v0 > v1)
{
return (uint)v1 << 16 | (uint)v0 & 0xffff;
}
else
{
return (uint)v0 << 16 | (uint)v1 & 0xffff;
}
}
///
/// パックデータを2つの番号(v0/v1)に分離する
///
///
///
///
public static void UnpackPair(uint pack, out int v0, out int v1)
{
// 辺の頂点分解
v0 = (int)((pack >> 16) & 0xffff);
v1 = (int)(pack & 0xffff);
}
///
/// 3つのインデックスを1つのulong型にパッキングする
/// 番号が若いものが上位に来るように配置
///
///
///
///
///
public static ulong PackTriple(int v0, int v1, int v2)
{
List indexList = new List();
indexList.Add((ulong)v0);
indexList.Add((ulong)v1);
indexList.Add((ulong)v2);
indexList.Sort();
ulong hash = (indexList[0] << 32) | (indexList[1] << 16) | (indexList[2]);
return hash;
}
///
/// パックデータを3つの番号に分離する
///
///
///
///
///
public static void UnpackTriple(ulong pack, out int v0, out int v1, out int v2)
{
v0 = (int)((pack >> 32) & 0xffff);
v1 = (int)((pack >> 16) & 0xffff);
v2 = (int)(pack & 0xffff);
}
///
/// 4つのインデックスを1つのulong型にパッキングする
/// 番号が若いものが上位に来るように配置
///
///
///
///
///
public static ulong PackQuater(int v0, int v1, int v2, int v3)
{
List indexList = new List();
indexList.Add((ulong)v0);
indexList.Add((ulong)v1);
indexList.Add((ulong)v2);
indexList.Add((ulong)v3);
indexList.Sort();
ulong hash = (indexList[0] << 48) | (indexList[1] << 32) | (indexList[2] << 16) | (indexList[3]);
return hash;
}
///
/// パックデータを4つの番号に分離する
///
///
///
///
///
public static void UnpackQuater(ulong pack, out int v0, out int v1, out int v2, out int v3)
{
v0 = (int)((pack >> 48) & 0xffff);
v1 = (int)((pack >> 32) & 0xffff);
v2 = (int)((pack >> 16) & 0xffff);
v3 = (int)(pack & 0xffff);
}
///
/// 2つのintを1つのuintにパッキングする
///
///
///
///
public static uint Pack16(int hi, int low)
{
return (uint)hi << 16 | (uint)low & 0xffff;
}
///
/// uintパックデータから上位16bitをintにして返す
///
///
///
public static int Unpack16Hi(uint pack)
{
return (int)((pack >> 16) & 0xffff);
}
///
/// uintパックデータから下位16bitをintにして返す
///
///
///
public static int Unpack16Low(uint pack)
{
return (int)(pack & 0xffff);
}
///
/// 2つのintを1つのulongにパッキングする
///
///
///
///
public static ulong Pack32(int hi, int low)
{
return (ulong)hi << 32 | (ulong)low & 0xffffffff;
}
///
/// ulongパックデータから上位データを返す
///
///
///
public static int Unpack32Hi(ulong pack)
{
return (int)((pack >> 32) & 0xffffffff);
}
///
/// ulongパックデータから下位データを返す
///
///
///
public static int Unpack32Low(ulong pack)
{
return (int)(pack & 0xffffffff);
}
//=========================================================================================
///
/// FinalDataの共有頂点座標/法線/接線をワールド座標変換して返す
///
///
///
///
///
public static void CalcFinalDataWorldPositionNormalTangent(FinalData final, out List wposList, out List wnorList, out List wtanList)
{
wposList = new List();
wnorList = new List();
wtanList = new List();
if (final.VertexCount == 0)
return;
if (final.BoneCount == 0)
return;
int vcnt = final.VertexCount;
if (final.IsSkinning == false)
{
// 通常メッシュ
Transform t = final.bones[0];
for (int i = 0; i < vcnt; i++)
{
Vector3 wpos = t.TransformPoint(final.vertices[i]);
wposList.Add(wpos);
Vector3 wnor = t.TransformDirection(final.normals[i]);
wnor.Normalize();
wnorList.Add(wnor);
Vector3 wtan = t.TransformDirection(final.tangents[i]);
wtan.Normalize();
wtanList.Add(new Vector4(wtan.x, wtan.y, wtan.z, final.tangents[i].w));
}
}
else
{
// スキンメッシュ
float[] weights = new float[4];
int[] boneIndexs = new int[4];
for (int i = 0; i < vcnt; i++)
{
Vector3 wpos = Vector3.zero;
Vector3 wnor = Vector3.zero;
Vector3 wtan = Vector3.zero;
// 頂点スキニング
weights[0] = final.boneWeights[i].weight0;
weights[1] = final.boneWeights[i].weight1;
weights[2] = final.boneWeights[i].weight2;
weights[3] = final.boneWeights[i].weight3;
boneIndexs[0] = final.boneWeights[i].boneIndex0;
boneIndexs[1] = final.boneWeights[i].boneIndex1;
boneIndexs[2] = final.boneWeights[i].boneIndex2;
boneIndexs[3] = final.boneWeights[i].boneIndex3;
for (int j = 0; j < 4; j++)
{
float w = weights[j];
if (w > 0.0f)
{
int bindex = boneIndexs[j];
Transform t = final.bones[bindex];
// position
Vector3 v = final.bindPoses[bindex].MultiplyPoint3x4(final.vertices[i]);
v = t.TransformPoint(v);
v *= w;
wpos += v;
// normal
v = final.bindPoses[bindex].MultiplyVector(final.normals[i]);
v = t.TransformVector(v);
wnor += v.normalized * w;
// tangent
v = final.bindPoses[bindex].MultiplyVector(final.tangents[i]);
v = t.TransformVector(v);
wtan += v.normalized * w;
}
}
wposList.Add(wpos);
wnorList.Add(wnor);
wtanList.Add(new Vector4(wtan.x, wtan.y, wtan.z, final.tangents[i].w));
}
}
}
///
/// FinalDataの子頂点座標/法線/接線をワールド座標変換して返す
///
///
///
///
///
public static void CalcFinalDataChildWorldPositionNormalTangent(
FinalData final, int meshIndex, List sposList, List snorList, List stanList,
out List wposList, out List wnorList, out List wtanList
)
{
wposList = new List();
wnorList = new List();
wtanList = new List();
// 回転を求める
List quatList = new List();
for (int i = 0; i < sposList.Count; i++)
{
var q = Quaternion.LookRotation(snorList[i], stanList[i]);
quatList.Add(q);
}
// 共有頂点からさらにスキニングする
var minfo = final.meshList[meshIndex];
float[] weights = new float[4];
int[] boneIndexs = new int[4];
for (int i = 0; i < minfo.VertexCount; i++)
{
Vector3 wpos = Vector3.zero;
Vector3 wnor = Vector3.zero;
Vector3 wtan = Vector3.zero;
// 頂点スキニング
weights[0] = minfo.boneWeights[i].weight0;
weights[1] = minfo.boneWeights[i].weight1;
weights[2] = minfo.boneWeights[i].weight2;
weights[3] = minfo.boneWeights[i].weight3;
boneIndexs[0] = minfo.boneWeights[i].boneIndex0;
boneIndexs[1] = minfo.boneWeights[i].boneIndex1;
boneIndexs[2] = minfo.boneWeights[i].boneIndex2;
boneIndexs[3] = minfo.boneWeights[i].boneIndex3;
for (int j = 0; j < 4; j++)
{
float w = weights[j];
if (w > 0.0f)
{
int bindex = boneIndexs[j];
var rot = quatList[bindex];
// position
Vector3 v = final.vertexBindPoses[bindex].MultiplyPoint3x4(minfo.vertices[i]);
v = rot * v + sposList[bindex];
v *= w;
wpos += v;
// normal
v = final.vertexBindPoses[bindex].MultiplyVector(minfo.normals[i]);
v = rot * v;
wnor += v.normalized * w;
// tangent
v = final.vertexBindPoses[bindex].MultiplyVector(minfo.tangents[i]);
v = rot * v;
wtan += v.normalized * w;
}
}
wposList.Add(wpos);
wnorList.Add(wnor);
wtanList.Add(new Vector4(wtan.x, wtan.y, wtan.z, -1));
}
}
///
/// 座標/法線/接線をローカル座標変換する
/// wposList/wnorList/wtanListの中身が書き換わります
///
///
///
///
///
public static void CalcLocalPositionNormalTangent(Transform root, List wposList, List wnorList, List wtanList)
{
for (int i = 0; i < wposList.Count; i++)
{
wposList[i] = root.InverseTransformPoint(wposList[i]);
}
for (int i = 0; i < wnorList.Count; i++)
{
wnorList[i] = root.InverseTransformDirection(wnorList[i]);
}
for (int i = 0; i < wtanList.Count; i++)
{
Vector3 v = wtanList[i];
float w = wtanList[i].w;
v = root.InverseTransformDirection(v);
wtanList[i] = new Vector4(v.x, v.y, v.z, w);
}
}
}
}