Files
UnrealEngine/Engine/Source/Programs/Enterprise/Datasmith/DatasmithARCHICADExporter/Private/GeometryUtil.cpp
2025-05-18 13:04:45 +08:00

163 lines
5.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "GeometryUtil.h"
#include "Vector3D.hpp"
#include "Math/RotationMatrix.h"
BEGIN_NAMESPACE_UE_AC
// Extract the rotation from the matrix and return as a Quat
FQuat FGeometryUtil::GetRotationQuat(const double Matrix[3][4])
{
double RotAngle = 0.0;
Geometry::Vector3< double > RotAxis;
if (Geometry::IsNearZero(abs(Matrix[0][1] - Matrix[1][0])) &&
Geometry::IsNearZero(abs(Matrix[0][2] - Matrix[2][0])) &&
Geometry::IsNearZero(abs(Matrix[1][2] - Matrix[2][1])))
{
if (Geometry::IsNearZero(abs(Matrix[0][1] + Matrix[1][0]), 0.1) &&
Geometry::IsNearZero(abs(Matrix[0][2] + Matrix[2][0]), 0.1) &&
Geometry::IsNearZero(abs(Matrix[1][2] + Matrix[2][1]), 0.1) &&
Geometry::IsNearZero(abs(Matrix[0][0] + Matrix[1][1] + Matrix[2][2] - 3), 0.1))
{
// no rotation
RotAngle = 0.0;
}
else
{ // 180 degrees rotation
RotAngle = PI;
const double xx = (Matrix[0][0] + 1.0) * 0.5;
const double yy = (Matrix[1][1] + 1.0) * 0.5;
const double zz = (Matrix[2][2] + 1.0) * 0.5;
const double xy = (Matrix[0][1] + Matrix[1][0]) * 0.25;
const double xz = (Matrix[0][2] + Matrix[2][0]) * 0.25;
const double yz = (Matrix[1][2] + Matrix[2][1]) * 0.25;
if ((xx > yy) && (xx > zz))
{
if (Geometry::IsNearZero(xx))
{
RotAxis = Geometry::Vector3< double >(0.0, 0.7071, 0.7071);
}
else
{
RotAxis[0] = sqrt(xx);
RotAxis[1] = xy / RotAxis[0];
RotAxis[2] = xz / RotAxis[0];
}
}
else if (yy > zz)
{
if (Geometry::IsNearZero(yy))
{
RotAxis = Geometry::Vector3< double >(0.7071, 0.0, 0.7071);
}
else
{
RotAxis[1] = sqrt(yy);
RotAxis[0] = xy / RotAxis[1];
RotAxis[2] = yz / RotAxis[1];
}
}
else
{
if (Geometry::IsNearZero(zz))
{
RotAxis = Geometry::Vector3< double >(0.7071, 0.7071, 0.0);
}
else
{
RotAxis[2] = sqrt(zz);
RotAxis[0] = xz / RotAxis[2];
RotAxis[1] = yz / RotAxis[2];
}
}
}
}
else
{
RotAngle = acos((Matrix[0][0] + Matrix[1][1] + Matrix[2][2] - 1.0) * 0.5);
RotAxis = Geometry::Vector3< double >(
(Matrix[2][1] - Matrix[1][2]) / sqrt(sqr(Matrix[2][1] - Matrix[1][2]) + sqr(Matrix[0][2] - Matrix[2][0]) +
sqr(Matrix[1][0] - Matrix[0][1])),
(Matrix[0][2] - Matrix[2][0]) / sqrt(sqr(Matrix[2][1] - Matrix[1][2]) + sqr(Matrix[0][2] - Matrix[2][0]) +
sqr(Matrix[1][0] - Matrix[0][1])),
(Matrix[1][0] - Matrix[0][1]) / sqrt(sqr(Matrix[2][1] - Matrix[1][2]) + sqr(Matrix[0][2] - Matrix[2][0]) +
sqr(Matrix[1][0] - Matrix[0][1])));
}
RotAxis.NormalizeVector();
return FQuat(FVector(float(RotAxis.x), float(-RotAxis.y), float(RotAxis.z)), float(RotAngle)).Inverse();
}
// Convert Archicad direction vector to Unreal one
FVector GetDirectionVector(const ModelerAPI::Vector& Vec)
{
return FVector(Vec.x, -Vec.y, Vec.z);
}
// Return the Quat equivalent of rotation defined by Direction and Up vectors
FQuat FGeometryUtil::GetRotationQuat(const ModelerAPI::Vector& Direction, const ModelerAPI::Vector& Up)
{
FVector DirVec = GetDirectionVector(Direction);
if (!DirVec.Normalize())
{
return FQuat::Identity;
}
FVector UpVec = GetDirectionVector(Up);
if (!UpVec.Normalize())
{
return FQuat::Identity;
}
return FQuat(FRotationMatrix::MakeFromXZ(DirVec, UpVec));
}
// Convert Archicad camera rotation to an Unreal Quat
FQuat FGeometryUtil::GetRotationQuat(const double PitchInDegrees, const double YawInDegrees, const double RollInDegrees)
{
return FQuat(FRotator(float(-PitchInDegrees), float(180.0 - YawInDegrees), float(-RollInDegrees)));
}
// Extract Archicad translation from the matrix and return an Unreal one (in centimeters)
FVector FGeometryUtil::GetTranslationVector(const double Matrix[3][4])
{
return FVector(float(Matrix[0][3] * 100.0), -float(Matrix[1][3] * 100.0),
float(Matrix[2][3] * 100.0)); // The base unit is centimetre in Unreal
}
// Convert Archicad Vertex to Unreal one (in centimeters)
FVector FGeometryUtil::GetTranslationVector(const ModelerAPI::Vertex PosInMeters)
{
return FVector(float(PosInMeters.x * 100.0), -float(PosInMeters.y * 100.0), float(PosInMeters.z * 100.0));
}
// Return focal that fit ViewAngle in SensorWidth
float FGeometryUtil::GetCameraFocalLength(const double SensorWidth, const double ViewAngleInDegrees)
{
return float(SensorWidth / (2.0 * tan(ViewAngleInDegrees * (PI / 180.0) *
0.5))); // the sensor width and focal length are in millimetre
}
// Return the distance in 3d (input in meters, result in centimeters)
float FGeometryUtil::GetDistance3D(const double distanceZ, const double Distance2D)
{
return float(sqrt(distanceZ * distanceZ + Distance2D * Distance2D) * 100.0);
}
// Return the pitch in degrees
double FGeometryUtil::GetPitchAngle(const double CameraZ, const double TargetZ, const double Distance2D)
{
const double angleSign = (CameraZ < TargetZ) ? -1.0 : 1.0;
const double distanceZ = TargetZ - CameraZ;
const double realDistance = sqrt(distanceZ * distanceZ + Distance2D * Distance2D);
if (Geometry::IsNotNearZero(Distance2D - realDistance))
return acos(Distance2D / realDistance) * angleSign * (180.0 / PI);
else
return 0.0;
}
END_NAMESPACE_UE_AC