오큘러스에서 제공하는 손 스켈레탈 메시를
포저블 매시로 받아서 관절별로 회전하는걸 확인했는데
이제 블라즈 핸드 추론 결과로 어떻게 관절회전방향을 구하는가가 문제이다
일단 기존 mediapipepytorch 데모 코드에
정규화된 랜드마크를 따로 저장해서 다뤄보려고함
이 넘파이 파일을 한번띄워보면 과학표기법으로 나와서 알아보기힘들다.
np.set_printoptions(supress=True) 주면 과학 표기법이 꺼지고 알아보기 쉽게나옴
졍규화된 랜드마크 인줄알았는데
xy값은 정규화가 되어있지않다?
데모 코드에서 찍어보니
정규화된게 맞는데
세이브 되기전에 어디서 수정된듯하다.
세이브 시점을 저장눌렀을때가 아니라
추론 직후로 옮기고
flags2, handed2, normalized_landmarks2 = hand_regressor(img.to(gpu))
filename = 'normalized_landmarks2.npy'
np.save(filename, normalized_landmarks2)
landmarks2 = hand_regressor.denormalize_landmarks(normalized_landmarks2.cpu(), affine2)
이렇게 했을때 정상적으로 정규화된 값이 나온다.
일단 z축은 -256
xy축은 256으로만 반정규화시켜보면
이제 대충 원하던 형태로 값이 나온다.
이 포인트들을 맷플롭립으로 띄워보려는데 이상하게 나온다.
차원이 하나더 있어서였으므로 스퀴즈
대충 잘 나오긴한거같은데 알아보긴 힘들다
import numpy as np
import matplotlib.pyplot as plt
# 데이터 분리
x = denormalize_landmark[:, 0]
y = denormalize_landmark[:, 1]
z = denormalize_landmark[:, 2]
# 3차원 시각화
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(x, y, z)
# 축 레이블 설정
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
# 그래프 출력
plt.show()
화면을 돌리면서 보기위해
%matplotlib widget하는데
ipympl이없단다.
핍으로 설치뒤에
다시 %matplotlib widget 하면 ok
1~4는 엄지
5~6 검지
9~12 중지
13~16 약지
17~20 소지
이므로 색을 따로 줘서 플로팅하면
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib widget
# 색상 지정
colors = ['black'] + ['red'] * 4 + ['orange'] * 4 + ['yellow'] * 4 + ['green'] * 4 + ['blue'] * 4
# 3차원 산점도 그리기
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = denormalize_landmark[:, 0]
y = denormalize_landmark[:, 1]
z = denormalize_landmark[:, 2]
ax.scatter(x, y, z, c=colors)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib widget
# 3차원 산점도 그리기
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = denormalize_landmark[:, 0]
y = denormalize_landmark[:, 1]
z = denormalize_landmark[:, 2]
# 색상 지정
colors = ['black'] + ['red'] * 4 + ['orange'] * 4 + ['yellow'] * 4 + ['green'] * 4 + ['blue'] * 4
ax.scatter(x, y, z, c=colors)
#손가락
colors = ['red', 'orange', 'yellow', 'green', 'blue']
groups = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20]]
for i, group in enumerate(groups):
for j in range(len(group)-1):
plt.plot([x[group[j]], x[group[j+1]]], [y[group[j]], y[group[j+1]]], [z[group[j]], z[group[j+1]]], color=colors[i])
#손등
lines = [[0, 1], [0, 5], [0, 17], [5, 9], [9, 13], [13, 17]]
for line in lines:
ax.plot([x[line[0]], x[line[1]]], [y[line[0]], y[line[1]]], [z[line[0]], z[line[1]]], color='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
하면서보니 모양이 조금이상한것 같아서
z에 -1해줫는데
손목 z가 0을 기준으로 시작하는듯하다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib widget
# 3차원 산점도 그리기
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = denormalize_landmark[:, 0]
y = denormalize_landmark[:, 1]
z = denormalize_landmark[:, 2] * -1
# 색상 지정
colors = ['black'] + ['red'] * 4 + ['orange'] * 4 + ['yellow'] * 4 + ['green'] * 4 + ['blue'] * 4
ax.scatter(x, y, z, c=colors)
#손가락
colors = ['red', 'orange', 'yellow', 'green', 'blue']
groups = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20]]
for i, group in enumerate(groups):
for j in range(len(group)-1):
plt.plot([x[group[j]], x[group[j+1]]], [y[group[j]], y[group[j+1]]], [z[group[j]], z[group[j+1]]], color=colors[i])
#손등
lines = [[0, 1], [0, 5], [0, 17], [5, 9], [9, 13], [13, 17]]
for line in lines:
ax.plot([x[line[0]], x[line[1]]], [y[line[0]], y[line[1]]], [z[line[0]], z[line[1]]], color='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
이제 이점들로 오큘러스 핸드의 관절 회전각을 구해야한다.
일단 반정규화 랜드마크 z축을 -1 곱해놓긴하자
일단 검지 첫번째 관절부터 보자.
회전 상태를보면 롤피치요 다 거의 0,0,0
요 50도 정도 주면 이런식이된다.
근데 위의건 로컬 좌표계 기준이라 전역이랑 좀 다르다.
랜드마크로 방향을 구하면 전역 좌표계 기준 방향이 나올듯한데
전역계기준 저 포인트의 rpy를 계산해내야할듯
시작점과 끝점을 출력해보면 이런식인데
시작점으로 -시켜주면
시작점이 원점이 되고
끝점은 원점 기준으로 이동된다.
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib widget
pts = np.array([pt_start, pt_end])
# 3차원 산점도 그리기
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = pts[:, 0]
y = pts[:, 1]
z = pts[:, 2]
ax.scatter(x, y, z)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.show()
어캐할까 싶다가
먼저 xy 평면에서 각도를 계산해보면
-71도 정도가나온다.
이번에는 xy 놈이랑 z좌표로 각을 구하면 -5도 정도가 나오는데 이것도 대충 맞겟지 싶다.
대충 각도구하긴 했는데, 언리얼에선 롤피치요를 쓰지만
여기선 각도 2개를 구했다.
z방향 회전 각도는 y축으로 회전한것이니 피치?
xy 평면 회전 각도는 z축으로 회전한것이니 요? 쯤 되지않나싶다.
언리얼에서 관절을 돌려보긴하는데 뭔가 좀 이상하다.
위에서 보니 -70도라기보단 20도쯤 될거같은데
방향이 좀 달라서 그런듯싶다.
잠깐
기본손의 다음 뼈들의 RPY를 다 0으로 설정했을때 모습
보는데 matplotlib에서 x축 방향이 반대로되어있으니 햇갈려서 다시띄움
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
%matplotlib widget
# 3차원 산점도 그리기
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x = denormalize_landmark[:, 0]
y = denormalize_landmark[:, 1]
z = denormalize_landmark[:, 2]
# 색상 지정
colors = ['black'] + ['red'] * 4 + ['orange'] * 4 + ['yellow'] * 4 + ['green'] * 4 + ['blue'] * 4
ax.scatter(x, y, z, c=colors)
#손가락
colors = ['red', 'orange', 'yellow', 'green', 'blue']
groups = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12], [13, 14, 15, 16], [17, 18, 19, 20]]
for i, group in enumerate(groups):
for j in range(len(group)-1):
plt.plot([x[group[j]], x[group[j+1]]], [y[group[j]], y[group[j+1]]], [z[group[j]], z[group[j+1]]], color=colors[i])
#손등
lines = [[0, 1], [0, 5], [0, 17], [5, 9], [9, 13], [13, 17]]
for line in lines:
ax.plot([x[line[0]], x[line[1]]], [y[line[0]], y[line[1]]], [z[line[0]], z[line[1]]], color='gray')
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_zlabel('Z')
plt.gca().invert_xaxis()
plt.show()
y 방향이 아래로 가므로 -1해주면 70도로 맞긴한데
문제는 관절 rpy들을 0으로 했을때랑 다르다
저런 식으로 맞추려면
관절에선 전역계 요를 -20도로 찍어줘야함.
z방향 각도 그러니까 y축 회전 피치는
-5도 나오는데
언리얼 본에선 +방향으로 회전시켜줘야 손바닥 안쪽으로 들어온다.
아래 그림은 전역 피치 80줬을때 상황
정리해보면 일단
요는 계산결과 - 90
피치는 * -1
를 해주면 원하는 방향이 나올듯한데
소지의 경우도 봐야할듯하다.
소지(17, 18)는 계산 결과 131도가 나오는데
17번점 기준으로 xy평면에서 131도가 맞긴 하다.
90 - 131 = -41이 되는데
-41을 전역계 요
z축 회전각은 -10도 이므로
-10 * -1 = 10을 전역계 피치
를주면
피치는 맞는데 요가 좀잘못됨
-41이 아닌 41을 주면 원하는 각이나오긴하다.
전역계 요각 계산할때
보라색 y축 기준 각도로 해야될듯하다.
두점 사이 피치, 요 계산하는 코드는 이런식으로 정리
c++로 고친결과
void ADesktopGameModeBase::get_pitch_yaw(cv::Point3f pt_start, cv::Point3f pt_end, float& pitch, float& yaw) {
float dx = pt_end.x - pt_start.x;
float dy = pt_end.y - pt_start.y;
dy *= -1;
yaw = std::atan2(dy, dx) * 180 / CV_PI;
yaw = yaw - 90;
float dz = pt_end.z - pt_start.z;
float xy_norm = std::sqrt(dx * dx + dy * dy);
pitch = std::atan2(dz, xy_norm) * 180 / CV_PI;
pitch *= -1;
}
c++ 롤피치요 를 블루프린트 가져와서 쓸수있도록 코드수정
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "Blaze.h"
#include "Windows/AllowWindowsPlatformTypes.h"
#include <Windows.h>
#include "Windows/HideWindowsPlatformTypes.h"
#include "PreOpenCVHeaders.h"
#include <opencv2/opencv.hpp>
#include "PostOpenCVHeaders.h"
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "DesktopGameModeBase.generated.h"
/**
*
*/
UCLASS()
class HANDDESKTOP_API ADesktopGameModeBase : public AGameModeBase
{
GENERATED_BODY()
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
UFUNCTION(BlueprintCallable)
void ReadFrame();
int monitorWidth = 1920;
int monitorHeight = 1080;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* imageTextureScreen1;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* imageTextureScreen2;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* imageTextureScreen3;
cv::Mat imageScreen1;
cv::Mat imageScreen2;
cv::Mat imageScreen3;
void ScreensToCVMats();
void CVMatsToTextures();
int webcamWidth = 640;
int webcamHeight = 480;
cv::VideoCapture capture;
cv::Mat webcamImage;
UPROPERTY(EditAnywhere, BlueprintReadWrite)
UTexture2D* webcamTexture;
void MatToTexture2D(const cv::Mat InMat);
//var and functions with blaze
Blaze blaze;
cv::Mat img256;
cv::Mat img128;
float scale;
cv::Scalar pad;
// vars and funcs for rotator
int hand_conns_indexes[14] = {2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
void get_pitch_yaw(cv::Point3f pt_start, cv::Point3f pt_end, float& pitch, float& yaw);
void make_map_for_rotators(std::vector<cv::Mat> denorm_imgs_landmarks);
void make_map_bone();
UPROPERTY(BlueprintReadWrite, Category="RotatorMap")
TMap<int32, float> MapPitch;
UPROPERTY(BlueprintReadWrite, Category = "RotatorMap")
TMap<int32, float> MapYaw;
UPROPERTY(BlueprintReadWrite, Category = "RotatorMap")
TMap<int32, FString> MapBoneLeft;
UPROPERTY(BlueprintReadWrite, Category = "RotatorMap")
TMap<int32, FString> MapBoneRight;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "DesktopGameModeBase.h"
void ADesktopGameModeBase::BeginPlay()
{
Super::BeginPlay();
blaze = Blaze();
capture = cv::VideoCapture(0);
if (!capture.isOpened())
{
UE_LOG(LogTemp, Log, TEXT("Open Webcam failed"));
return;
}
else
{
UE_LOG(LogTemp, Log, TEXT("Open Webcam Success"));
}
capture.set(cv::CAP_PROP_FRAME_WIDTH, webcamWidth);
capture.set(cv::CAP_PROP_FRAME_HEIGHT, webcamHeight);
webcamTexture = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
imageScreen1 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
imageScreen2 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
imageScreen3 = cv::Mat(monitorHeight, monitorWidth, CV_8UC4);
imageTextureScreen1 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
imageTextureScreen2 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
imageTextureScreen3 = UTexture2D::CreateTransient(monitorWidth, monitorHeight, PF_B8G8R8A8);
make_map_bone();
}
void ADesktopGameModeBase::ReadFrame()
{
if (!capture.isOpened())
{
return;
}
capture.read(webcamImage);
/*
get filtered detections
*/
blaze.ResizeAndPad(webcamImage, img256, img128, scale, pad);
//UE_LOG(LogTemp, Log, TEXT("scale value: %f, pad value: (%f, %f)"), scale, pad[0], pad[1]);
std::vector<Blaze::PalmDetection> normDets = blaze.PredictPalmDetections(img128);
std::vector<Blaze::PalmDetection> denormDets = blaze.DenormalizePalmDetections(normDets, webcamWidth, webcamHeight, pad);
std::vector<Blaze::PalmDetection> filteredDets = blaze.FilteringDets(denormDets, webcamWidth, webcamHeight);
std::vector<cv::Rect> handRects = blaze.convertHandRects(filteredDets);
std::vector<cv::Mat> handImgs;
blaze.GetHandImages(webcamImage, handRects, handImgs);
std::vector<cv::Mat> imgs_landmarks = blaze.PredictHandDetections(handImgs);
std::vector<cv::Mat> denorm_imgs_landmarks = blaze.DenormalizeHandLandmarks(imgs_landmarks, handRects);
make_map_for_rotators(denorm_imgs_landmarks);
//draw hand rects/ plam detection/ dets info/ hand detection
blaze.DrawRects(webcamImage, handRects);
blaze.DrawPalmDetections(webcamImage, filteredDets);
blaze.DrawDetsInfo(webcamImage, filteredDets, normDets, denormDets);
blaze.DrawHandDetections(webcamImage, denorm_imgs_landmarks);
//cv::mat to utexture2d
MatToTexture2D(webcamImage);
/*
모니터 시각화
*/
ScreensToCVMats();
CVMatsToTextures();
}
void ADesktopGameModeBase::MatToTexture2D(const cv::Mat InMat)
{
if (InMat.type() == CV_8UC3)//example for pre-conversion of Mat
{
cv::Mat resizedImage;
cv::resize(InMat, resizedImage, cv::Size(monitorWidth, monitorHeight));
cv::Mat bgraImage;
//if the Mat is in BGR space, convert it to BGRA. There is no three channel texture in UE (at least with eight bit)
cv::cvtColor(resizedImage, bgraImage, cv::COLOR_BGR2BGRA);
//actually copy the data to the new texture
FTexture2DMipMap& Mip = webcamTexture->GetPlatformData()->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
FMemory::Memcpy(Data, bgraImage.data, bgraImage.total() * bgraImage.elemSize());//copy the data
Mip.BulkData.Unlock();
webcamTexture->PostEditChange();
webcamTexture->UpdateResource();
}
else if (InMat.type() == CV_8UC4)
{
//actually copy the data to the new texture
FTexture2DMipMap& Mip = webcamTexture->GetPlatformData()->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
FMemory::Memcpy(Data, InMat.data, InMat.total() * InMat.elemSize());//copy the data
Mip.BulkData.Unlock();
webcamTexture->PostEditChange();
webcamTexture->UpdateResource();
}
//if the texture hasnt the right pixel format, abort.
webcamTexture->PostEditChange();
webcamTexture->UpdateResource();
}
void ADesktopGameModeBase::ScreensToCVMats()
{
HDC hScreenDC = GetDC(NULL);
HDC hMemoryDC = CreateCompatibleDC(hScreenDC);
int screenWidth = GetDeviceCaps(hScreenDC, HORZRES);
int screenHeight = GetDeviceCaps(hScreenDC, VERTRES);
HBITMAP hBitmap = CreateCompatibleBitmap(hScreenDC, screenWidth, screenHeight);
HBITMAP hOldBitmap = (HBITMAP)SelectObject(hMemoryDC, hBitmap);
//screen 1
BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 0, 0, SRCCOPY);
GetBitmapBits(hBitmap, imageScreen1.total() * imageScreen1.elemSize(), imageScreen1.data);
//screen 2
BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 1920, 0, SRCCOPY);
GetBitmapBits(hBitmap, imageScreen2.total() * imageScreen2.elemSize(), imageScreen2.data);
//screen 3
BitBlt(hMemoryDC, 0, 0, screenWidth, screenHeight, hScreenDC, 3840, 0, SRCCOPY);
GetBitmapBits(hBitmap, imageScreen3.total() * imageScreen3.elemSize(), imageScreen3.data);
SelectObject(hMemoryDC, hOldBitmap);
DeleteDC(hScreenDC);
DeleteDC(hMemoryDC);
DeleteObject(hBitmap);
DeleteObject(hOldBitmap);
}
void ADesktopGameModeBase::CVMatsToTextures()
{
for (int i = 0; i < 3; i++)
{
if (i == 0)
{
FTexture2DMipMap& Mip = imageTextureScreen1->GetPlatformData()->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
FMemory::Memcpy(Data, imageScreen1.data, imageScreen1.total() * imageScreen1.elemSize());//copy the data
Mip.BulkData.Unlock();
imageTextureScreen1->PostEditChange();
imageTextureScreen1->UpdateResource();
}
else if (i == 1)
{
FTexture2DMipMap& Mip = imageTextureScreen2->GetPlatformData()->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
FMemory::Memcpy(Data, imageScreen2.data, imageScreen2.total() * imageScreen2.elemSize());//copy the data
Mip.BulkData.Unlock();
imageTextureScreen2->PostEditChange();
imageTextureScreen2->UpdateResource();
}
else if (i == 2)
{
FTexture2DMipMap& Mip = imageTextureScreen3->GetPlatformData()->Mips[0];
void* Data = Mip.BulkData.Lock(LOCK_READ_WRITE);//lock the texture data
FMemory::Memcpy(Data, imageScreen3.data, imageScreen3.total() * imageScreen3.elemSize());//copy the data
Mip.BulkData.Unlock();
imageTextureScreen3->PostEditChange();
imageTextureScreen3->UpdateResource();
}
}
}
void ADesktopGameModeBase::get_pitch_yaw(cv::Point3f pt_start, cv::Point3f pt_end, float& pitch, float& yaw) {
float dx = pt_end.x - pt_start.x;
float dy = pt_end.y - pt_start.y;
dy *= -1;
yaw = std::atan2(dy, dx) * 180 / CV_PI;
yaw = yaw - 90;
float dz = pt_end.z - pt_start.z;
float xy_norm = std::sqrt(dx * dx + dy * dy);
pitch = std::atan2(dz, xy_norm) * 180 / CV_PI;
pitch *= -1;
}
void ADesktopGameModeBase::make_map_for_rotators(std::vector<cv::Mat> denorm_imgs_landmarks)
{
for (auto& denorm_landmarks : denorm_imgs_landmarks)
{
std::vector<std::array<int, 2>> HAND_CONNECTIONS = blaze.HAND_CONNECTIONS;
for (auto& hand_conns_index : hand_conns_indexes)
{
std::array<int, 2> hand_conns = HAND_CONNECTIONS.at(hand_conns_index);
float pitch, yaw;
cv::Point3f pt_start, pt_end;
pt_start.x = denorm_landmarks.at<float>(hand_conns.at(0), 0);
pt_start.y = denorm_landmarks.at<float>(hand_conns.at(0), 1);
pt_start.z = denorm_landmarks.at<float>(hand_conns.at(0), 2);
pt_end.x = denorm_landmarks.at<float>(hand_conns.at(1), 0);
pt_end.y = denorm_landmarks.at<float>(hand_conns.at(1), 1);
pt_end.z = denorm_landmarks.at<float>(hand_conns.at(1), 2);
get_pitch_yaw(pt_start, pt_end, pitch, yaw);
MapPitch.Add(hand_conns_index, pitch);
MapYaw.Add(hand_conns_index, yaw);
}
}
}
/*
std::vector<std::array<int, 2>> HAND_CONNECTIONS = {
{0, 1}, {1, 2}, {2, 3}, {3, 4},
{5, 6}, {6, 7}, {7, 8},
{9, 10}, {10, 11}, {11, 12},
{13, 14}, {14, 15}, {15, 16},
{17, 18}, {18, 19}, {19, 20},
{0, 5}, {5, 9}, {9, 13}, {13, 17}, {0, 17}
};
*/
void ADesktopGameModeBase::make_map_bone()
{
MapBoneLeft.Add(2, FString("b_l_thumb2"));
MapBoneLeft.Add(3, FString("b_l_thumb3"));
MapBoneLeft.Add(4, FString("b_l_index1"));
MapBoneLeft.Add(5, FString("b_l_index2"));
MapBoneLeft.Add(6, FString("b_l_index3"));
MapBoneLeft.Add(7, FString("b_l_middle1"));
MapBoneLeft.Add(8, FString("b_l_middle2"));
MapBoneLeft.Add(9, FString("b_l_middle3"));
MapBoneLeft.Add(10, FString("b_l_ring1"));
MapBoneLeft.Add(11, FString("b_l_ring2"));
MapBoneLeft.Add(12, FString("b_l_ring3"));
MapBoneLeft.Add(13, FString("b_l_pinky1"));
MapBoneLeft.Add(14, FString("b_l_pinky2"));
MapBoneLeft.Add(15, FString("b_l_pinky3"));
MapBoneRight.Add(2, FString("b_r_thumb2"));
MapBoneRight.Add(3, FString("b_r_thumb3"));
MapBoneRight.Add(4, FString("b_r_index1"));
MapBoneRight.Add(5, FString("b_r_index2"));
MapBoneRight.Add(6, FString("b_r_index3"));
MapBoneRight.Add(7, FString("b_r_middle1"));
MapBoneRight.Add(8, FString("b_r_middle2"));
MapBoneRight.Add(9, FString("b_r_middle3"));
MapBoneRight.Add(10, FString("b_r_ring1"));
MapBoneRight.Add(11, FString("b_r_ring2"));
MapBoneRight.Add(12, FString("b_r_ring3"));
MapBoneRight.Add(13, FString("b_r_pinky1"));
MapBoneRight.Add(14, FString("b_r_pinky2"));
MapBoneRight.Add(15, FString("b_r_pinky3"));
}
출력 시켜봣는데 yaw는 좀 안맞는거같은데 피치는 대강 맞는듯하다
일단 본 회전하는걸 해봤는데
이유를 모르겠지만 되긴한다.
하지만 월드 공간 기준으로 회전해서 되지
컴포넌트 공간 기준 회전으론 잘 동작되진 않는다.
https://www.youtube.com/watch?v=ZmiZ6VkSJBE
https://www.youtube.com/watch?v=9dZjcFW3BRY
대충 손 모델 가져와서 스켈레탈 메시로 만들어 시도해보려 했으나 일단망함
'컴퓨터과학 > 언리얼' 카테고리의 다른 글
HandDesktop14 - 손 관절 가지고 놀기2 컴포넌트공간 관절회전 (0) | 2024.02.01 |
---|---|
HandDesktop13 - 손 관절 가지고 놀기 (0) | 2024.02.01 |
HandDesktop11 - 손 메시 제어하기 (0) | 2024.01.31 |
HandDesktop10 - blazehand c++, unreal 적용 (0) | 2024.01.29 |
HandDesktop09 - 기존 문제들 원인 찾음, blaze 갈아엎기, 블라즈팜 문제해결 (0) | 2024.01.26 |