LoginSignup
8
6

【Unity】C#でQuaternionを32bitに圧縮する

Posted at

概要

unity3d-jp/MeshSyncにあるmuQuat32をベースに、
C#のBitVector32を用いてQuaternionを32bitに圧縮するコードを書いてみました。

実際に使用してみて、精度は悪くないと思います。

コード

Quat32.cs
using System;
using System.Collections.Specialized;
using UnityEngine;

struct Quat32
{
    private const float SR2 = 1.41421356237f;
    private const float RSR2 = 1.0f / 1.41421356237f;
    private const float C = (float)0x3ff;
    private const float R = 1.0f / (float)0x3ff;

    private static UInt32 Pack(float a)
    {
        return (UInt32)((a * SR2 + 1.0f) * 0.5f * C);
    }

    private static float Unpack(UInt32 a)
    {
        return ((a * R) * 2.0f - 1.0f) * RSR2;
    }

    private static float Square(float a)
    {
        return a * a;
    }

    private static int DropMax(float a, float b, float c, float d)
    {
        if (a > b && a > c && a > d) return 0;
        if (b > c && b > d) return 1;
        if (c > d) return 2;
        return 3;
    }

    private static readonly BitVector32.Section X0Section = BitVector32.CreateSection(0x3FF);
    private static readonly BitVector32.Section X1Section = BitVector32.CreateSection(0x3FF, X0Section);
    private static readonly BitVector32.Section X2Section = BitVector32.CreateSection(0x3FF, X1Section);
    private static readonly BitVector32.Section DropSection = BitVector32.CreateSection(0x3, X2Section);

    public BitVector32 Value;

    public static implicit operator Quat32(Quaternion q)
    {
        q.Normalize();

        var q32 = new Quat32();

        float a0, a1, a2;
        q32.Value[DropSection] = DropMax(Square(q.x), Square(q.y), Square(q.z), Square(q.w));
        if (q32.Value[DropSection] == 0)
        {
            float s = Mathf.Sign(q.x);
            a0 = q.y * s;
            a1 = q.z * s;
            a2 = q.w * s;
        }
        else if (q32.Value[DropSection] == 1)
        {
            float s = Mathf.Sign(q.y);
            a0 = q.x * s;
            a1 = q.z * s;
            a2 = q.w * s;
        }
        else if (q32.Value[DropSection] == 2)
        {
            float s = Mathf.Sign(q.z);
            a0 = q.x * s;
            a1 = q.y * s;
            a2 = q.w * s;
        }
        else
        {
            float s = Mathf.Sign(q.w);
            a0 = q.x * s;
            a1 = q.y * s;
            a2 = q.z * s;
        }

        q32.Value[X0Section] = (int)Pack(a0);
        q32.Value[X1Section] = (int)Pack(a1);
        q32.Value[X2Section] = (int)Pack(a2);

        return q32;
    }

    public static implicit operator Quat32(int q)
    {
        var q32 = new Quat32();
        q32.Value = new BitVector32(q);
        return q32;
    }

    public static implicit operator Quaternion(Quat32 q)
    {
        float a0 = Unpack((uint)q.Value[X0Section]);
        float a1 = Unpack((uint)q.Value[X1Section]);
        float a2 = Unpack((uint)q.Value[X2Section]);
        float iss = Mathf.Sqrt(1.0f - (Square(a0) + Square(a1) + Square(a2)));

        switch (q.Value[DropSection])
        {
            case 0: return new Quaternion(iss, a0, a1, a2);
            case 1: return new Quaternion(a0, iss, a1, a2);
            case 2: return new Quaternion(a0, a1, iss, a2);
            default: return new Quaternion(a0, a1, a2, iss);
        }
    }

    public static implicit operator int(Quat32 q)
    {
        return q.Value.Data;
    }
}

使用方法

Vector3 axis;
float angle;
Quaternion quaternion = Quaternion.AngleAxis(angle, axis);

// 圧縮 (Quaternion -> Quat32 -> int)
Quat32 quat32 = quaternion;
int compressed = quat32;

// 展開 (int -> Quat32 -> Quaternion)
quat32 = compressed;
Quaternion decompressed = quat32;

以上です。

8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6