<commit_before>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "OgreSkeletonAsset.h"
#include "LoggingFunctions.h"
#include "Framework.h"
#include "AssetAPI.h"
#include "UrhoRenderer.h"
#include "OgreMeshDefines.h"
#include "Math/float3.h"
#include "Math/Quat.h"

#include <Urho3D/Core/Profiler.h>
#include <Urho3D/Core/StringUtils.h>

#include <Urho3D/IO/MemoryBuffer.h>
#include <Urho3D/Graphics/Animation.h>
#include <stdexcept>

namespace Tundra
{

using namespace Ogre;

static uint currentLength;

static const unsigned short    HEADER_CHUNK_ID         = 0x1000;

static const uint MSTREAM_OVERHEAD_SIZE               = sizeof(u16) + sizeof(uint);
static const uint MSTREAM_BONE_SIZE_WITHOUT_SCALE     = MSTREAM_OVERHEAD_SIZE + sizeof(u16) + (sizeof(float) * 7);
static const uint MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8);

enum SkeletonChunkId
{
    SKELETON_HEADER                = 0x1000,
        // char* version           : Version number check
        SKELETON_BLENDMODE        = 0x1010, // optional
            // unsigned short blendmode        : SkeletonAnimationBlendMode
    SKELETON_BONE                = 0x2000,
    // Repeating section defining each bone in the system.
    // Bones are assigned indexes automatically based on their order of declaration
    // starting with 0.
        // char* name                       : name of the bone
        // unsigned short handle            : handle of the bone, should be contiguous & start at 0
        // Vector3 position                 : position of this bone relative to parent
        // Quaternion orientation           : orientation of this bone relative to parent
        // Vector3 scale                    : scale of this bone relative to parent
    SKELETON_BONE_PARENT        = 0x3000,
    // Record of the parent of a single bone, used to build the node tree
    // Repeating section, listed in Bone Index order, one per Bone
        // unsigned short handle             : child bone
        // unsigned short parentHandle   : parent bone
    SKELETON_ANIMATION            = 0x4000,
    // A single animation for this skeleton
        // char* name                       : Name of the animation
        // float length                      : Length of the animation in seconds
        SKELETON_ANIMATION_BASEINFO = 0x4010,
        // [Optional] base keyframe information
        // char* baseAnimationName (blank for self)
        // float baseKeyFrameTime
        SKELETON_ANIMATION_TRACK    = 0x4100,
        // A single animation track (relates to a single bone)
        // Repeating section (within SKELETON_ANIMATION)
            // unsigned short boneIndex     : Index of bone to apply to
            SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110,
            // A single keyframe within the track
            // Repeating section
                // float time                    : The time position (seconds)
                // Quaternion rotate            : Rotation to apply at this keyframe
                // Vector3 translate            : Translation to apply at this keyframe
                // Vector3 scale                : Scale to apply at this keyframe
    SKELETON_ANIMATION_LINK        = 0x5000
    // Link to another skeleton, to re-use its animations
        // char* skeletonName                    : name of skeleton to get animations from
        // float scale                            : scale to apply to trans/scale keys
};

static String ReadLine(Urho3D::Deserializer& stream)
{
    String str;
    while(!stream.IsEof())
    {
        char c = stream.ReadByte();
        if (c == '\n')
            break;
        str += c;
    }
    return str;
}

static u16 ReadHeader(Urho3D::Deserializer& stream, bool readLength = true)
{
    u16 id = stream.ReadUShort();
    if (readLength)
        currentLength = stream.ReadUInt();

    return id;
}

static Quat ReadQuat(Urho3D::Deserializer& stream)
{
    Quat result;
    stream.Read(&result.x, sizeof result);
    return result;
}

static void RollbackHeader(Urho3D::Deserializer& stream)
{
    stream.Seek(stream.GetPosition() - MSTREAM_OVERHEAD_SIZE);
}

static void SkipBytes(Urho3D::Deserializer& stream, uint numBytes)
{
    stream.Seek(stream.GetPosition() + numBytes);
}

static void ReadSkeleton(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadBone(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadBoneParent(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadSkeletonAnimation(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadSkeletonAnimationTrack(Urho3D::Deserializer& stream, Skeleton *skeleton, Animation *dest);
static void ReadSkeletonAnimationKeyFrame(Urho3D::Deserializer& stream, VertexAnimationTrack *dest);
static void ReadSkeletonAnimationLink(Urho3D::Deserializer& stream, Skeleton *skeleton);

static void ReadSkeleton(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    u16 id = ReadHeader(stream, false);
    if (id != HEADER_CHUNK_ID) {
        throw std::runtime_error("Invalid Ogre Skeleton file header.");
    }

    // This deserialization supports both versions of the skeleton spec
    String version = ReadLine(stream);
    /*
    if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1)
    {
        throw std::runtime_error(Formatter::format() << "Skeleton version " << version << " not supported by this importer."
            << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1);
    }
    */

    while (!stream.IsEof())
    {
        id = ReadHeader(stream);
        switch(id)
        {
            case SKELETON_BLENDMODE:
            {
                skeleton->blendMode = static_cast<Skeleton::BlendMode>(stream.ReadUShort());
                break;
            }
            case SKELETON_BONE:
            {
                ReadBone(stream, skeleton);
                break;
            }
            case SKELETON_BONE_PARENT:
            {
                ReadBoneParent(stream, skeleton);
                break;
            }
            case SKELETON_ANIMATION:
            {
                ReadSkeletonAnimation(stream, skeleton);
                break;
            }
            case SKELETON_ANIMATION_LINK:
            {
                ReadSkeletonAnimationLink(stream, skeleton);
                break;
            }
        }
    }

    // Calculate bone matrices for root bones. Recursively calculates their children.
    for (uint i=0, len=skeleton->bones.Size(); i<len; ++i)
    {
        Bone *bone = skeleton->bones[i];
        if (!bone->IsParented())
            bone->CalculateWorldMatrixAndDefaultPose(skeleton);
    }
}

static void ReadBone(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    Bone *bone = new Bone();
    bone->name = ReadLine(stream);
    bone->id = stream.ReadUShort();

    // Pos and rot
    bone->position = stream.ReadVector3();
    bone->rotation = ReadQuat(stream);

    // Scale (optional)
    if (currentLength > MSTREAM_BONE_SIZE_WITHOUT_SCALE)
        bone->scale = stream.ReadVector3();

    // Bone indexes need to start from 0 and be contiguous
    if (bone->id != skeleton->bones.Size()) {
        throw std::runtime_error("Ogre Skeleton bone indexes not contiguous.");
    }

    skeleton->bones.Push(bone);
}

static void ReadBoneParent(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    u16 childId = stream.ReadUShort();
    u16 parentId = stream.ReadUShort();

    Bone *child = skeleton->BoneById(childId);
    Bone *parent = skeleton->BoneById(parentId);

    if (child && parent)
        parent->AddChild(child);
    else
        LogWarning("Failed to find bones for parenting: Child id " + String(childId) + " for parent id " + String(parentId));
}

static void ReadSkeletonAnimation(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    Animation *anim = new Animation(skeleton);
    anim->name = ReadLine(stream);
    anim->length = stream.ReadFloat();

    if (!stream.IsEof())
    {
        u16 id = ReadHeader(stream);
        if (id == SKELETON_ANIMATION_BASEINFO)
        {
            anim->baseName = ReadLine(stream);
            anim->baseTime = stream.ReadFloat();

            // Advance to first track
            id = ReadHeader(stream);
        }

        while (!stream.IsEof() && id == SKELETON_ANIMATION_TRACK)
        {
            ReadSkeletonAnimationTrack(stream, skeleton, anim);

            if (!stream.IsEof())
                id = ReadHeader(stream);
        }
        if (!stream.IsEof())
            RollbackHeader(stream);
    }

    skeleton->animations.Push(anim);
}

static void ReadSkeletonAnimationTrack(Urho3D::Deserializer& stream, Skeleton * /*skeleton*/, Animation *dest)
{
    u16 boneId = stream.ReadUShort();
    Bone *bone = dest->parentSkeleton->BoneById(boneId);
    if (!bone) {
        throw std::runtime_error("Cannot read animation track, target bone not in target Skeleton");
    }

    VertexAnimationTrack track;
    track.type = VertexAnimationTrack::VAT_TRANSFORM;
    track.boneName = bone->name;

    u16 id = ReadHeader(stream);
    while (!stream.IsEof() && id == SKELETON_ANIMATION_TRACK_KEYFRAME)
    {
        ReadSkeletonAnimationKeyFrame(stream, &track);

        if (!stream.IsEof())
            id = ReadHeader(stream);
    }
    if (!stream.IsEof())
        RollbackHeader(stream);

    dest->tracks.Push(track);
}

static void ReadSkeletonAnimationKeyFrame(Urho3D::Deserializer& stream, VertexAnimationTrack *dest)
{
    TransformKeyFrame keyframe;
    keyframe.timePos = stream.ReadFloat();

    // Rot and pos
    keyframe.rotation = ReadQuat(stream);
    keyframe.position = stream.ReadVector3();

    // Scale (optional)
    if (currentLength > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE)
        keyframe.scale = stream.ReadVector3();

    // Note: transforms are now in Ogre's offset transform format. The bone's transform needs to be applied when converting to Urho animation
    dest->transformKeyFrames.Push(keyframe);
}

static void ReadSkeletonAnimationLink(Urho3D::Deserializer& stream, Skeleton * /*skeleton*/)
{
    // Skip bounds, not compatible with Assimp.
    ReadLine(stream); // skeleton name
    SkipBytes(stream, sizeof(float) * 3); // scale
}

OgreSkeletonAsset::OgreSkeletonAsset(AssetAPI *owner, const String &type_, const String &name_) :
    IAsset(owner, type_, name_)
{
}

OgreSkeletonAsset::~OgreSkeletonAsset()
{
}

bool OgreSkeletonAsset::DeserializeFromData(const u8 *data_, uint numBytes, bool /*allowAsynchronous*/)
{
    URHO3D_PROFILE(OgreSkeletonAsset_LoadFromFileInMemory);

    /// Force an unload of previous data first.
    Unload();

    Urho3D::MemoryBuffer buffer(data_, numBytes);

    SharedPtr<Ogre::Skeleton> ogreSkel(new Ogre::Skeleton());
    try
    {
        ReadSkeleton(buffer, ogreSkel);
    }
    catch (std::exception& e)
    {
        LogError("OgreSkeletonAsset::DeserializeFromData: " + String(e.what()));
        return false;
    }

    // Fill Urho bone structure
    Vector<Urho3D::Bone>& bones = skeleton.GetModifiableBones();
    bones.Resize(ogreSkel->bones.Size());
    for (uint i = 0; i < ogreSkel->bones.Size(); ++i)
    {
        bones[i].name_ = ogreSkel->bones[i]->name;
        bones[i].nameHash_ = StringHash(bones[i].name_);

        if (ogreSkel->bones[i]->IsParented())
            bones[i].parentIndex_ = ogreSkel->bones[i]->parentId;
        else
        {
            bones[i].parentIndex_ = i;
            skeleton.SetRootBoneIndex(i);
        }
        Urho3D::Matrix4 pose = ogreSkel->bones[i]->defaultPose;
        Urho3D::Vector3 pos, scale;
        Urho3D::Quaternion rot;
        pose.Decompose(pos, rot, scale);

        bones[i].animated_ = true;
        bones[i].initialPosition_ = pos;
        bones[i].initialRotation_ = rot;
        bones[i].initialScale_ = scale;
        bones[i].offsetMatrix_ = Urho3D::Matrix3x4(ogreSkel->bones[i]->worldMatrix).Inverse();
        // The skeleton can not know the vertex information necessary to calculate bone bounding boxes. Therefore that data
        // must be combined later from the mesh's data
    }

    // Create animations
    for (uint i = 0; i < ogreSkel->animations.Size(); ++i)
    {
        Ogre::Animation* ogreAnim = ogreSkel->animations[i];
        if (!ogreAnim)
            continue;
        String animName = ogreAnim->name;
        if (animName.Empty())
            continue;

        SharedPtr<Urho3D::Animation> urhoAnim(new Urho3D::Animation(context_));
        urhoAnim->SetLength(ogreAnim->length);
        // Set both animation & resource name, same for now
        urhoAnim->SetAnimationName(animName);
        urhoAnim->SetName(animName);

        for (uint j = 0; j < ogreAnim->tracks.Size(); ++j)
        {
            const Ogre::VertexAnimationTrack& ogreTrack = ogreAnim->tracks[j];
            if (ogreTrack.type != Ogre::VertexAnimationTrack::VAT_TRANSFORM)
                continue;
            Urho3D::AnimationTrack* urhoTrack = urhoAnim->CreateTrack(ogreTrack.boneName);
            urhoTrack->channelMask_ = Urho3D::CHANNEL_POSITION | Urho3D::CHANNEL_ROTATION;

            Urho3D::Bone* urhoBone = 0;
            for (uint k = 0; k < bones.Size(); ++k)
            {
                if (bones[k].name_ == urhoTrack->name_)
                {
                    urhoBone = &bones[k];
                    break;
                }
            }
            if (!urhoBone)
            {
                LogWarning("OgreSkeletonAsset::DeserializeFromData: found animation track referring to a non-existent bone " + urhoTrack->name_ + ", skipping");
                continue;
            }

            for (uint k = 0; k < ogreTrack.transformKeyFrames.Size(); ++k)
            {
                const Ogre::TransformKeyFrame& ogreKeyframe = ogreTrack.transformKeyFrames[k];
                Urho3D::AnimationKeyFrame urhoKeyframe;
                urhoKeyframe.time_ = ogreKeyframe.timePos;

                // Urho uses absolute bone poses in animation, while Ogre uses additive. Convert to absolute now.
                urhoKeyframe.position_ = urhoBone->initialPosition_ + urhoBone->initialRotation_ * ogreKeyframe.position;
                urhoKeyframe.rotation_ = urhoBone->initialRotation_ * ogreKeyframe.rotation;
                urhoKeyframe.scale_ = ogreKeyframe.scale;
                if (!ogreKeyframe.scale.Equals(float3(1.0f, 1.0f, 1.0f)))
                    urhoTrack->channelMask_ |= Urho3D::CHANNEL_SCALE;
                urhoTrack->keyFrames_.Push(urhoKeyframe);
            }

        }

        animations[animName] = urhoAnim;
    }

    // Inform load has finished.
    assetAPI->AssetLoadCompleted(Name());
    return true;
}

void OgreSkeletonAsset::DoUnload()
{
    skeleton = Urho3D::Skeleton();
    animations.Clear();
}

bool OgreSkeletonAsset::IsLoaded() const
{
    return skeleton.GetNumBones() > 0;
}

Urho3D::Animation* OgreSkeletonAsset::AnimationByName(const String& name) const
{
    HashMap<String, SharedPtr<Urho3D::Animation> >::ConstIterator i = animations.Find(name);
    return i != animations.End() ? i->second_.Get() : nullptr;
}

}
<commit_msg>Simplify and fix Ogre -> Urho skeleton & animation conversion.<commit_after>// For conditions of distribution and use, see copyright notice in LICENSE

#include "StableHeaders.h"
#include "OgreSkeletonAsset.h"
#include "LoggingFunctions.h"
#include "Framework.h"
#include "AssetAPI.h"
#include "UrhoRenderer.h"
#include "OgreMeshDefines.h"
#include "Math/float3.h"
#include "Math/Quat.h"

#include <Urho3D/Core/Profiler.h>
#include <Urho3D/Core/StringUtils.h>

#include <Urho3D/IO/MemoryBuffer.h>
#include <Urho3D/Graphics/Animation.h>
#include <stdexcept>

namespace Tundra
{

using namespace Ogre;

static uint currentLength;

static const unsigned short    HEADER_CHUNK_ID         = 0x1000;

static const uint MSTREAM_OVERHEAD_SIZE               = sizeof(u16) + sizeof(uint);
static const uint MSTREAM_BONE_SIZE_WITHOUT_SCALE     = MSTREAM_OVERHEAD_SIZE + sizeof(u16) + (sizeof(float) * 7);
static const uint MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE = MSTREAM_OVERHEAD_SIZE + (sizeof(float) * 8);

enum SkeletonChunkId
{
    SKELETON_HEADER                = 0x1000,
        // char* version           : Version number check
        SKELETON_BLENDMODE        = 0x1010, // optional
            // unsigned short blendmode        : SkeletonAnimationBlendMode
    SKELETON_BONE                = 0x2000,
    // Repeating section defining each bone in the system.
    // Bones are assigned indexes automatically based on their order of declaration
    // starting with 0.
        // char* name                       : name of the bone
        // unsigned short handle            : handle of the bone, should be contiguous & start at 0
        // Vector3 position                 : position of this bone relative to parent
        // Quaternion orientation           : orientation of this bone relative to parent
        // Vector3 scale                    : scale of this bone relative to parent
    SKELETON_BONE_PARENT        = 0x3000,
    // Record of the parent of a single bone, used to build the node tree
    // Repeating section, listed in Bone Index order, one per Bone
        // unsigned short handle             : child bone
        // unsigned short parentHandle   : parent bone
    SKELETON_ANIMATION            = 0x4000,
    // A single animation for this skeleton
        // char* name                       : Name of the animation
        // float length                      : Length of the animation in seconds
        SKELETON_ANIMATION_BASEINFO = 0x4010,
        // [Optional] base keyframe information
        // char* baseAnimationName (blank for self)
        // float baseKeyFrameTime
        SKELETON_ANIMATION_TRACK    = 0x4100,
        // A single animation track (relates to a single bone)
        // Repeating section (within SKELETON_ANIMATION)
            // unsigned short boneIndex     : Index of bone to apply to
            SKELETON_ANIMATION_TRACK_KEYFRAME = 0x4110,
            // A single keyframe within the track
            // Repeating section
                // float time                    : The time position (seconds)
                // Quaternion rotate            : Rotation to apply at this keyframe
                // Vector3 translate            : Translation to apply at this keyframe
                // Vector3 scale                : Scale to apply at this keyframe
    SKELETON_ANIMATION_LINK        = 0x5000
    // Link to another skeleton, to re-use its animations
        // char* skeletonName                    : name of skeleton to get animations from
        // float scale                            : scale to apply to trans/scale keys
};

static String ReadLine(Urho3D::Deserializer& stream)
{
    String str;
    while(!stream.IsEof())
    {
        char c = stream.ReadByte();
        if (c == '\n')
            break;
        str += c;
    }
    return str;
}

static u16 ReadHeader(Urho3D::Deserializer& stream, bool readLength = true)
{
    u16 id = stream.ReadUShort();
    if (readLength)
        currentLength = stream.ReadUInt();

    return id;
}

static Quat ReadQuat(Urho3D::Deserializer& stream)
{
    Quat result;
    stream.Read(&result.x, sizeof result);
    return result;
}

static void RollbackHeader(Urho3D::Deserializer& stream)
{
    stream.Seek(stream.GetPosition() - MSTREAM_OVERHEAD_SIZE);
}

static void SkipBytes(Urho3D::Deserializer& stream, uint numBytes)
{
    stream.Seek(stream.GetPosition() + numBytes);
}

static void ReadSkeleton(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadBone(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadBoneParent(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadSkeletonAnimation(Urho3D::Deserializer& stream, Skeleton *skeleton);
static void ReadSkeletonAnimationTrack(Urho3D::Deserializer& stream, Skeleton *skeleton, Animation *dest);
static void ReadSkeletonAnimationKeyFrame(Urho3D::Deserializer& stream, VertexAnimationTrack *dest);
static void ReadSkeletonAnimationLink(Urho3D::Deserializer& stream, Skeleton *skeleton);

static void ReadSkeleton(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    u16 id = ReadHeader(stream, false);
    if (id != HEADER_CHUNK_ID) {
        throw std::runtime_error("Invalid Ogre Skeleton file header.");
    }

    // This deserialization supports both versions of the skeleton spec
    String version = ReadLine(stream);
    /*
    if (version != SKELETON_VERSION_1_8 && version != SKELETON_VERSION_1_1)
    {
        throw std::runtime_error(Formatter::format() << "Skeleton version " << version << " not supported by this importer."
            << " Supported versions: " << SKELETON_VERSION_1_8 << " and " << SKELETON_VERSION_1_1);
    }
    */

    while (!stream.IsEof())
    {
        id = ReadHeader(stream);
        switch(id)
        {
            case SKELETON_BLENDMODE:
            {
                skeleton->blendMode = static_cast<Skeleton::BlendMode>(stream.ReadUShort());
                break;
            }
            case SKELETON_BONE:
            {
                ReadBone(stream, skeleton);
                break;
            }
            case SKELETON_BONE_PARENT:
            {
                ReadBoneParent(stream, skeleton);
                break;
            }
            case SKELETON_ANIMATION:
            {
                ReadSkeletonAnimation(stream, skeleton);
                break;
            }
            case SKELETON_ANIMATION_LINK:
            {
                ReadSkeletonAnimationLink(stream, skeleton);
                break;
            }
        }
    }

    // Calculate bone matrices for root bones. Recursively calculates their children.
    for (uint i=0, len=skeleton->bones.Size(); i<len; ++i)
    {
        Bone *bone = skeleton->bones[i];
        if (!bone->IsParented())
            bone->CalculateWorldMatrixAndDefaultPose(skeleton);
    }
}

static void ReadBone(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    Bone *bone = new Bone();
    bone->name = ReadLine(stream);
    bone->id = stream.ReadUShort();

    // Pos and rot
    bone->position = stream.ReadVector3();
    bone->rotation = ReadQuat(stream);

    // Scale (optional)
    if (currentLength > MSTREAM_BONE_SIZE_WITHOUT_SCALE)
        bone->scale = stream.ReadVector3();

    // Bone indexes need to start from 0 and be contiguous
    if (bone->id != skeleton->bones.Size()) {
        throw std::runtime_error("Ogre Skeleton bone indexes not contiguous.");
    }

    skeleton->bones.Push(bone);
}

static void ReadBoneParent(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    u16 childId = stream.ReadUShort();
    u16 parentId = stream.ReadUShort();

    Bone *child = skeleton->BoneById(childId);
    Bone *parent = skeleton->BoneById(parentId);

    if (child && parent)
        parent->AddChild(child);
    else
        LogWarning("Failed to find bones for parenting: Child id " + String(childId) + " for parent id " + String(parentId));
}

static void ReadSkeletonAnimation(Urho3D::Deserializer& stream, Skeleton *skeleton)
{
    Animation *anim = new Animation(skeleton);
    anim->name = ReadLine(stream);
    anim->length = stream.ReadFloat();

    if (!stream.IsEof())
    {
        u16 id = ReadHeader(stream);
        if (id == SKELETON_ANIMATION_BASEINFO)
        {
            anim->baseName = ReadLine(stream);
            anim->baseTime = stream.ReadFloat();

            // Advance to first track
            id = ReadHeader(stream);
        }

        while (!stream.IsEof() && id == SKELETON_ANIMATION_TRACK)
        {
            ReadSkeletonAnimationTrack(stream, skeleton, anim);

            if (!stream.IsEof())
                id = ReadHeader(stream);
        }
        if (!stream.IsEof())
            RollbackHeader(stream);
    }

    skeleton->animations.Push(anim);
}

static void ReadSkeletonAnimationTrack(Urho3D::Deserializer& stream, Skeleton * /*skeleton*/, Animation *dest)
{
    u16 boneId = stream.ReadUShort();
    Bone *bone = dest->parentSkeleton->BoneById(boneId);
    if (!bone) {
        throw std::runtime_error("Cannot read animation track, target bone not in target Skeleton");
    }

    VertexAnimationTrack track;
    track.type = VertexAnimationTrack::VAT_TRANSFORM;
    track.boneName = bone->name;

    u16 id = ReadHeader(stream);
    while (!stream.IsEof() && id == SKELETON_ANIMATION_TRACK_KEYFRAME)
    {
        ReadSkeletonAnimationKeyFrame(stream, &track);

        if (!stream.IsEof())
            id = ReadHeader(stream);
    }
    if (!stream.IsEof())
        RollbackHeader(stream);

    dest->tracks.Push(track);
}

static void ReadSkeletonAnimationKeyFrame(Urho3D::Deserializer& stream, VertexAnimationTrack *dest)
{
    TransformKeyFrame keyframe;
    keyframe.timePos = stream.ReadFloat();

    // Rot and pos
    keyframe.rotation = ReadQuat(stream);
    keyframe.position = stream.ReadVector3();

    // Scale (optional)
    if (currentLength > MSTREAM_KEYFRAME_SIZE_WITHOUT_SCALE)
        keyframe.scale = stream.ReadVector3();

    // Note: transforms are now in Ogre's offset transform format. The bone's transform needs to be applied when converting to Urho animation
    dest->transformKeyFrames.Push(keyframe);
}

static void ReadSkeletonAnimationLink(Urho3D::Deserializer& stream, Skeleton * /*skeleton*/)
{
    // Skip bounds, not compatible with Assimp.
    ReadLine(stream); // skeleton name
    SkipBytes(stream, sizeof(float) * 3); // scale
}

OgreSkeletonAsset::OgreSkeletonAsset(AssetAPI *owner, const String &type_, const String &name_) :
    IAsset(owner, type_, name_)
{
}

OgreSkeletonAsset::~OgreSkeletonAsset()
{
}

bool OgreSkeletonAsset::DeserializeFromData(const u8 *data_, uint numBytes, bool /*allowAsynchronous*/)
{
    URHO3D_PROFILE(OgreSkeletonAsset_LoadFromFileInMemory);

    /// Force an unload of previous data first.
    Unload();

    Urho3D::MemoryBuffer buffer(data_, numBytes);

    SharedPtr<Ogre::Skeleton> ogreSkel(new Ogre::Skeleton());
    try
    {
        ReadSkeleton(buffer, ogreSkel);
    }
    catch (std::exception& e)
    {
        LogError("OgreSkeletonAsset::DeserializeFromData: " + String(e.what()));
        return false;
    }

    // Fill Urho bone structure
    Vector<Urho3D::Bone>& bones = skeleton.GetModifiableBones();
    bones.Resize(ogreSkel->bones.Size());
    for (uint i = 0; i < ogreSkel->bones.Size(); ++i)
    {
        Ogre::Bone* ogreBone = ogreSkel->bones[i];

        bones[i].name_ = ogreBone->name;
        bones[i].nameHash_ = StringHash(bones[i].name_);

        if (ogreBone->IsParented())
            bones[i].parentIndex_ = ogreBone->parentId;
        else
        {
            bones[i].parentIndex_ = i;
            skeleton.SetRootBoneIndex(i);
        }

        bones[i].animated_ = true;
        bones[i].initialPosition_ = ogreBone->position;
        bones[i].initialRotation_ = ogreBone->rotation;
        bones[i].initialScale_ = ogreBone->scale;
        bones[i].offsetMatrix_ = Urho3D::Matrix3x4(ogreSkel->bones[i]->worldMatrix).Inverse();
        // The skeleton can not know the vertex information necessary to calculate bone bounding boxes. Therefore that data
        // must be combined later from the mesh's data
    }

    // Create animations
    for (uint i = 0; i < ogreSkel->animations.Size(); ++i)
    {
        Ogre::Animation* ogreAnim = ogreSkel->animations[i];
        if (!ogreAnim)
            continue;
        String animName = ogreAnim->name;
        if (animName.Empty())
            continue;

        SharedPtr<Urho3D::Animation> urhoAnim(new Urho3D::Animation(context_));
        urhoAnim->SetLength(ogreAnim->length);
        // Set both animation & resource name, same for now
        urhoAnim->SetAnimationName(animName);
        urhoAnim->SetName(animName);

        for (uint j = 0; j < ogreAnim->tracks.Size(); ++j)
        {
            const Ogre::VertexAnimationTrack& ogreTrack = ogreAnim->tracks[j];
            if (ogreTrack.type != Ogre::VertexAnimationTrack::VAT_TRANSFORM)
                continue;
            Urho3D::AnimationTrack* urhoTrack = urhoAnim->CreateTrack(ogreTrack.boneName);
            urhoTrack->channelMask_ = Urho3D::CHANNEL_POSITION | Urho3D::CHANNEL_ROTATION;

            Urho3D::Bone* urhoBone = 0;
            for (uint k = 0; k < bones.Size(); ++k)
            {
                if (bones[k].name_ == urhoTrack->name_)
                {
                    urhoBone = &bones[k];
                    break;
                }
            }
            if (!urhoBone)
            {
                LogWarning("OgreSkeletonAsset::DeserializeFromData: found animation track referring to a non-existent bone " + urhoTrack->name_ + ", skipping");
                continue;
            }

            for (uint k = 0; k < ogreTrack.transformKeyFrames.Size(); ++k)
            {
                const Ogre::TransformKeyFrame& ogreKeyframe = ogreTrack.transformKeyFrames[k];
                Urho3D::AnimationKeyFrame urhoKeyframe;
                urhoKeyframe.time_ = ogreKeyframe.timePos;

                // Urho uses absolute bone poses in animation, while Ogre uses additive. Convert to absolute now.
                urhoKeyframe.position_ = urhoBone->initialPosition_ + ogreKeyframe.position;
                urhoKeyframe.rotation_ = urhoBone->initialRotation_ * ogreKeyframe.rotation;
                urhoKeyframe.scale_ = ogreKeyframe.scale;
                if (!ogreKeyframe.scale.Equals(float3(1.0f, 1.0f, 1.0f)))
                    urhoTrack->channelMask_ |= Urho3D::CHANNEL_SCALE;
                urhoTrack->keyFrames_.Push(urhoKeyframe);
            }

        }

        animations[animName] = urhoAnim;
    }

    // Inform load has finished.
    assetAPI->AssetLoadCompleted(Name());
    return true;
}

void OgreSkeletonAsset::DoUnload()
{
    skeleton = Urho3D::Skeleton();
    animations.Clear();
}

bool OgreSkeletonAsset::IsLoaded() const
{
    return skeleton.GetNumBones() > 0;
}

Urho3D::Animation* OgreSkeletonAsset::AnimationByName(const String& name) const
{
    HashMap<String, SharedPtr<Urho3D::Animation> >::ConstIterator i = animations.Find(name);
    return i != animations.End() ? i->second_.Get() : nullptr;
}

}
<|endoftext|>


<commit_before>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>

namespace String
{
    namespace
    {
        const auto whitespace = " \t\n";
    }
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count)
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index-start_index));
        start_index = std::max(end_index, end_index + delim.size());
        ++split_count;
    }

    if(start_index < s.size())
    {
        result.push_back(s.substr(start_index));
    }
    else if(end_index < s.size())
    {
        result.push_back({}); // Original string ended with delimiter
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning)
{
    return (beginning.size() <= s.size()) && std::equal(beginning.begin(), beginning.end(), s.begin());
}

bool String::ends_with(const std::string& s, const std::string& ending)
{
    return (ending.size() <= s.size()) && std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}

std::string String::trim_outer_whitespace(const std::string& s)
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s)
{
    std::string result;

    for(auto c : trim_outer_whitespace(s))
    {
        if(contains(whitespace, c))
        {
            if(result.back() != ' ')
            {
                result.push_back(' ');
            }
        }
        else
        {
            result.push_back(c);
        }
    }

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment)
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::runtime_error("\"" + str + "\" is missing a comment delimiter: " + std::string{start} + std::string{end});
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::runtime_error("\"" + str + "\" contains bad comment delimiters: " + std::string{start} + std::string{end});
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
}

std::string String::lowercase(std::string s)
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}
<commit_msg>Add #includes for Windows compilation<commit_after>#include "Utility/String.h"

#include <string>
#include <vector>
#include <stdexcept>
#include <algorithm>
#include <cctype>

namespace String
{
    namespace
    {
        const auto whitespace = " \t\n";
    }
}

std::vector<std::string> String::split(std::string s, std::string delim, size_t count)
{
    if(delim.empty())
    {
        s = remove_extra_whitespace(s);
        delim = " ";
    }

    std::vector<std::string> result;
    size_t start_index = 0;
    size_t end_index = 0;
    size_t split_count = 0;
    while(end_index < s.size() && split_count < count)
    {
        end_index = s.find(delim, start_index);
        result.push_back(s.substr(start_index, end_index-start_index));
        start_index = std::max(end_index, end_index + delim.size());
        ++split_count;
    }

    if(start_index < s.size())
    {
        result.push_back(s.substr(start_index));
    }
    else if(end_index < s.size())
    {
        result.push_back({}); // Original string ended with delimiter
    }

    return result;
}

bool String::starts_with(const std::string& s, const std::string& beginning)
{
    return (beginning.size() <= s.size()) && std::equal(beginning.begin(), beginning.end(), s.begin());
}

bool String::ends_with(const std::string& s, const std::string& ending)
{
    return (ending.size() <= s.size()) && std::equal(ending.rbegin(), ending.rend(), s.rbegin());
}

std::string String::trim_outer_whitespace(const std::string& s)
{
    auto text_start = s.find_first_not_of(whitespace);
    if(text_start == std::string::npos)
    {
        return {};
    }

    auto text_end = s.find_last_not_of(whitespace);
    return s.substr(text_start, text_end - text_start + 1);
}

std::string String::remove_extra_whitespace(const std::string& s)
{
    std::string result;

    for(auto c : trim_outer_whitespace(s))
    {
        if(contains(whitespace, c))
        {
            if(result.back() != ' ')
            {
                result.push_back(' ');
            }
        }
        else
        {
            result.push_back(c);
        }
    }

    return result;
}

std::string String::strip_comments(const std::string& str, const std::string& comment)
{
    return trim_outer_whitespace(str.substr(0, str.find(comment)));
}

std::string String::strip_block_comment(const std::string& str, const std::string& start, const std::string& end)
{
    auto start_comment_index = str.find(start);
    auto end_comment_index = str.find(end);

    if(start_comment_index == std::string::npos && end_comment_index == std::string::npos)
    {
        return trim_outer_whitespace(str);
    }

    if(start_comment_index == std::string::npos || end_comment_index == std::string::npos)
    {
        throw std::runtime_error("\"" + str + "\" is missing a comment delimiter: " + std::string{start} + std::string{end});
    }

    if(start_comment_index >= end_comment_index)
    {
        throw std::runtime_error("\"" + str + "\" contains bad comment delimiters: " + std::string{start} + std::string{end});
    }

    auto first_part = str.substr(0, start_comment_index);
    auto last_part = str.substr(end_comment_index + end.size());
    return strip_block_comment(trim_outer_whitespace(first_part) + " " + trim_outer_whitespace(last_part), start, end);
}

std::string String::lowercase(std::string s)
{
    for(auto& c : s){ c = std::tolower(c); }
    return s;
}
<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>

using namespace std;

class Point3D {
  double x;
  double y;
  double z;

public:

  double getX() {
    return x;
  }

  double getY() {
    return y;
  }

  double getZ() {
    return z;
  }

  void setX(double newX) {
    x = newX;
  }

  void setY(double newY) {
    y = newY;
  }

  void setZ(double newZ) {
    z = newZ;
  }

  void translate(Point3D translationVector) {
    setX(getX() + translationVector.getX());
    setY(getY() + translationVector.getY());
    setZ(getZ() + translationVector.getZ());
  }

  double distanceTo(Point3D other) {
    return sqrt(pow(getX() - other.getX(), 2) +
                pow(getY() - other.getY(), 2) +
                pow(getZ() - other.getZ(), 2));
  }

  void print() {
    cout << '(' << getX() << ", " << getY() << ", " << getZ() << ')' << '\n';
  }
};

class Triangle {
  Point3D aVertex;
  Point3D bVertex;
  Point3D cVertex;

public:

  double getASide() {
    return bVertex.distanceTo(cVertex);
  }

  double getBSide() {
    return aVertex.distanceTo(cVertex);
  }

  double getCSide() {
    return aVertex.distanceTo(bVertex);
  }

  double perimeter() {
    return getASide() + getBSide() + getCSide();
  }

  double area() {
    double p = perimeter() / 2;

    return sqrt(p * (p - getASide()) * (p - getBSide()) * (p - getCSide()));
  }

  void translate(Point3D translationVector) {
    aVertex.translate(translationVector);
    bVertex.translate(translationVector);
    cVertex.translate(translationVector);
  }
};

int main() {

  return 0;
}<commit_msg>Add constructor for Triangle class. Triangle constructor implementation requires to add a default constructor for the Point3D class because we initialize the vertices in the body of the constructor instead of initialize them in the initialize list of the constructor in which way the default constructor will be called.<commit_after>#include <iostream>
#include <cmath>

using namespace std;

class Point3D {
  double x;
  double y;
  double z;

public:

  Point3D(double x = 0, double y = 0, double z = 0) {
    setX(x);
    setY(y);
    setZ(z);
  }

  double getX() {
    return x;
  }

  double getY() {
    return y;
  }

  double getZ() {
    return z;
  }

  void setX(double newX) {
    x = newX;
  }

  void setY(double newY) {
    y = newY;
  }

  void setZ(double newZ) {
    z = newZ;
  }

  void translate(Point3D translationVector) {
    setX(getX() + translationVector.getX());
    setY(getY() + translationVector.getY());
    setZ(getZ() + translationVector.getZ());
  }

  double distanceTo(Point3D other) {
    return sqrt(pow(getX() - other.getX(), 2) +
                pow(getY() - other.getY(), 2) +
                pow(getZ() - other.getZ(), 2));
  }

  void print() {
    cout << '(' << getX() << ", " << getY() << ", " << getZ() << ')' << '\n';
  }
};

class Triangle {
  Point3D aVertex;
  Point3D bVertex;
  Point3D cVertex;

public:

  Triangle(Point3D _aVertex, Point3D _bVertex, Point3D _cVertex) {
    aVertex = _aVertex;
    bVertex = _bVertex;
    cVertex = _cVertex;
  }

  double getASide() {
    return bVertex.distanceTo(cVertex);
  }

  double getBSide() {
    return aVertex.distanceTo(cVertex);
  }

  double getCSide() {
    return aVertex.distanceTo(bVertex);
  }

  double perimeter() {
    return getASide() + getBSide() + getCSide();
  }

  double area() {
    double p = perimeter() / 2;

    return sqrt(p * (p - getASide()) * (p - getBSide()) * (p - getCSide()));
  }

  void translate(Point3D translationVector) {
    aVertex.translate(translationVector);
    bVertex.translate(translationVector);
    cVertex.translate(translationVector);
  }
};

int main() {

  return 0;
}<|endoftext|>


<commit_before>#include "ValueProcessor.h"

#include <sstream>
#include <iostream>

template <class T>
inline std::string to_string (const T& t)
{
  std::stringstream ss;
  ss << t;
  return ss.str();
}

ValueProcessor::ValueProcessor() {}
ValueProcessor::~ValueProcessor() {}

TokenList* ValueProcessor::processValue(TokenList* value) {
  TokenList newvalue;
  Value* v;
  std::cout << *value->toString() << endl;
  while (value->size() > 0) {
    v = processStatement(value);

    if (v != NULL) {
      if (newvalue.size() > 0)
        newvalue.push(new Token(" ", Token::WHITESPACE));
      newvalue.push(v->getToken()->clone());
      delete v;
    } else if (value->size() > 0) {
      if (value->front()->type == Token::ATKEYWORD &&
               variables.count(value->front()->str)) {
        newvalue.push(variables[value->front()->str]);
        delete value->shift();
      } else {
        if (value->size() > 1) {
          TokenList* var = processDeepVariable(value->front(), value->at(1));
          if (var != NULL) {
            newvalue.push(var);
            delete value->shift();
            delete value->shift();
          } else
            newvalue.push(value->shift());
        } else
          newvalue.push(value->shift());
      }
    }
  }
  value->push(&newvalue);
  return value;
}
void ValueProcessor::putVariable(string key, TokenList* value) {
  variables[key] = value;
}

Value* ValueProcessor::processStatement(TokenList* value) {
  Value* op, *v = processConstant(value);

  if (v != NULL) {
    while ((op = processOperator(value, v)))
      v = op;
    return v;
  } else
    return NULL;
}

Value* ValueProcessor::processOperator(TokenList* value, Value* v1,
                                       Token* lastop) {
  Value* v2, *tmp;
  Token* op;
  string operators("+-*/");

  while (value->size() > 0 &&
         value->front()->type == Token::WHITESPACE) {
    delete value->shift();
  }
  if (value->size() == 0 ||
      operators.find(value->front()->str) == string::npos)
    return NULL;

  if (lastop != NULL &&
      operators.find(lastop->str) >
      operators.find(value->front()->str)) {
    return v1;
  }
  op = value->shift();
  v2 = processConstant(value);
  if (v2 == NULL) {
    if (value->size() > 0)
      throw new ParseException(value->front()->str, "Constant or @-variable");
    else
      throw new ParseException("end of line", "Constant or @-variable");
  }
  while ((tmp = processOperator(value, v2, op)))
    v2 = tmp;

  if (op->str == "+")
    v1->add(v2);
  else if (op->str == "-")
    v1->substract(v2);
  else if (op->str == "*")
    v1->multiply(v2);
  else if (op->str == "/")
    v1->divide(v2);
  delete v2;
  return v1;
}
Value* ValueProcessor::processConstant(TokenList* value) {
  Token* token;
  Value* ret;
  vector<Value*> arguments;

  while (value->size() > 0 &&
         value->front()->type == Token::WHITESPACE) {
    delete value->shift();
  }

  if (value->size() == 0)
    return NULL;

  token = value->front();
  switch(token->type) {
  case Token::HASH:
    // generate color from hex value
    return new Color(value->shift());
  case Token::NUMBER:
  case Token::PERCENTAGE:
  case Token::DIMENSION:
    return new Value(value->shift());

  case Token::FUNCTION:
    value->shift();
    if (value->front()->type != Token::PAREN_CLOSED)
      arguments.push_back(processConstant(value));

    while (value->front()->str == ",") {
      delete value->shift();
      arguments.push_back(processConstant(value));
    }
    if (value->front()->type == Token::PAREN_CLOSED)
      delete value->shift();

    return processFunction(token, arguments);

  case Token::ATKEYWORD:
    if (variables.count(token->str)) {
      ret = processConstant(variables[token->str]);
      delete value->shift();
      return ret;
    } else
      return NULL;

  case Token::PAREN_OPEN:
    delete value->shift();

    ret = processStatement(value);

    if (value->front()->type == Token::PAREN_CLOSED)
      delete value->shift();
    return ret;

  default:
    if (value->size() > 1) {
      TokenList* var = processDeepVariable(token, value->at(1));

      if (var != NULL) {
        ret = processStatement(var);
        if (ret != NULL) {
          delete value->shift();
          delete value->shift();
          return ret;
        }
      }
    }
    return NULL;
  }
}

TokenList* ValueProcessor::processDeepVariable (Token* token, Token* nexttoken) {
  TokenList* var;
  string key("@");

  if (token->type != Token::OTHER ||
      token->str != "@" ||
      nexttoken->type != Token::ATKEYWORD ||
      !variables.count(nexttoken->str))
    return NULL;

  var = variables[nexttoken->str];

  if (var->size() > 1 || var->front()->type != Token::STRING)
    return NULL;

  // generate key with '@' + var without quotes
  key.append(var->front()->
             str.substr(1, var->front()->str.size() - 2));

  if (!variables.count(key))
    return NULL;

  return variables[key];
}

Value* ValueProcessor::processFunction(Token* function,
                                   vector<Value*> arguments) {
  Color* color;
  string percentage;

  if(function->str == "rgb(") {
    if (arguments.size() == 3 &&
        arguments[0]->type == Value::NUMBER &&
        arguments[1]->type == Value::NUMBER &&
        arguments[2]->type == Value::NUMBER) {
      return new Color(0,0,0);
    }
    // Color rgb(@red: NUMBER, @green: NUMBER, @blue: NUMBER)
  } else if (function->str == "lighten(") {
    // Color lighten(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->lighten(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "darken(") {
    // Color darken(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->darken(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "saturate(") {
    // Color saturate(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->saturate(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "desaturate(") {
    // Color desaturate(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->desaturate(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "fadein(") {
    // Color fadein(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->fadein(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "fadeout(") {
    // Color fadeout(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->fadeout(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "spin(") {
    // Color fadein(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->spin(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "hsl(") {
    // Color hsl(PERCENTAGE, PERCENTAGE, PERCENTAGE)
    if (arguments.size() == 3 &&
        arguments[0]->type == Value::PERCENTAGE &&
        arguments[1]->type == Value::PERCENTAGE &&
        arguments[2]->type == Value::PERCENTAGE) {
      color = new Color(0,0,0);
      color->setHSL(arguments[0]->getPercent(),
                    arguments[1]->getPercent(),
                    arguments[2]->getPercent());
      return color;
    }
  } else if (function->str == "hue(") {
    // PERCENTAGE hue(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getHue()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }

  } else if (function->str == "saturation(") {
    // PERCENTAGE saturation(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getSaturation()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }

  } else if (function->str == "lightness(") {
    // PERCENTAGE lightness(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getLightness()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }
  } else
    return NULL;
  return NULL;
}
<commit_msg>Variables from ValueProcessor::variables are cloned before sending them through processConstant so they don't get deleted.<commit_after>#include "ValueProcessor.h"

#include <sstream>
#include <iostream>

template <class T>
inline std::string to_string (const T& t)
{
  std::stringstream ss;
  ss << t;
  return ss.str();
}

ValueProcessor::ValueProcessor() {}
ValueProcessor::~ValueProcessor() {}

TokenList* ValueProcessor::processValue(TokenList* value) {
  TokenList newvalue;
  Value* v;
  std::cout << *value->toString() << endl;
  while (value->size() > 0) {
    v = processStatement(value);

    if (v != NULL) {
      if (newvalue.size() > 0)
        newvalue.push(new Token(" ", Token::WHITESPACE));
      newvalue.push(v->getToken()->clone());
      delete v;
    } else if (value->size() > 0) {
      if (value->front()->type == Token::ATKEYWORD &&
               variables.count(value->front()->str)) {
        newvalue.push(variables[value->front()->str]);
        delete value->shift();
      } else {
        if (value->size() > 1) {
          TokenList* var = processDeepVariable(value->front(), value->at(1));
          if (var != NULL) {
            newvalue.push(var);
            delete value->shift();
            delete value->shift();
          } else
            newvalue.push(value->shift());
        } else
          newvalue.push(value->shift());
      }
    }
  }
  value->push(&newvalue);
  return value;
}
void ValueProcessor::putVariable(string key, TokenList* value) {
  variables[key] = value;
}

Value* ValueProcessor::processStatement(TokenList* value) {
  Value* op, *v = processConstant(value);

  if (v != NULL) {
    while ((op = processOperator(value, v)))
      v = op;
    return v;
  } else
    return NULL;
}

Value* ValueProcessor::processOperator(TokenList* value, Value* v1,
                                       Token* lastop) {
  Value* v2, *tmp;
  Token* op;
  string operators("+-*/");

  while (value->size() > 0 &&
         value->front()->type == Token::WHITESPACE) {
    delete value->shift();
  }
  if (value->size() == 0 ||
      operators.find(value->front()->str) == string::npos)
    return NULL;

  if (lastop != NULL &&
      operators.find(lastop->str) >
      operators.find(value->front()->str)) {
    return v1;
  }
  op = value->shift();
  v2 = processConstant(value);
  if (v2 == NULL) {
    if (value->size() > 0)
      throw new ParseException(value->front()->str, "Constant or @-variable");
    else
      throw new ParseException("end of line", "Constant or @-variable");
  }
  while ((tmp = processOperator(value, v2, op)))
    v2 = tmp;

  if (op->str == "+")
    v1->add(v2);
  else if (op->str == "-")
    v1->substract(v2);
  else if (op->str == "*")
    v1->multiply(v2);
  else if (op->str == "/")
    v1->divide(v2);
  delete v2;
  return v1;
}
Value* ValueProcessor::processConstant(TokenList* value) {
  Token* token;
  Value* ret;
  vector<Value*> arguments;

  while (value->size() > 0 &&
         value->front()->type == Token::WHITESPACE) {
    delete value->shift();
  }

  if (value->size() == 0)
    return NULL;

  token = value->front();
  switch(token->type) {
  case Token::HASH:
    // generate color from hex value
    return new Color(value->shift());
  case Token::NUMBER:
  case Token::PERCENTAGE:
  case Token::DIMENSION:
    return new Value(value->shift());

  case Token::FUNCTION:
    value->shift();
    if (value->front()->type != Token::PAREN_CLOSED)
      arguments.push_back(processConstant(value));

    while (value->front()->str == ",") {
      delete value->shift();
      arguments.push_back(processConstant(value));
    }
    if (value->front()->type == Token::PAREN_CLOSED)
      delete value->shift();

    return processFunction(token, arguments);

  case Token::ATKEYWORD:
    if (variables.count(token->str)) {
      TokenList* var = variables[token->str]->clone();
      ret = processConstant(var);
      delete var;
      delete value->shift();
      return ret;
    } else
      return NULL;

  case Token::PAREN_OPEN:
    delete value->shift();

    ret = processStatement(value);

    if (value->front()->type == Token::PAREN_CLOSED)
      delete value->shift();
    return ret;

  default:
    if (value->size() > 1) {
      TokenList* var = processDeepVariable(token, value->at(1));

      if (var != NULL) {
        ret = processStatement(var);
        if (ret != NULL) {
          delete value->shift();
          delete value->shift();
          return ret;
        }
      }
    }
    return NULL;
  }
}

TokenList* ValueProcessor::processDeepVariable (Token* token, Token* nexttoken) {
  TokenList* var;
  string key("@");

  if (token->type != Token::OTHER ||
      token->str != "@" ||
      nexttoken->type != Token::ATKEYWORD ||
      !variables.count(nexttoken->str))
    return NULL;

  var = variables[nexttoken->str];

  if (var->size() > 1 || var->front()->type != Token::STRING)
    return NULL;

  // generate key with '@' + var without quotes
  key.append(var->front()->
             str.substr(1, var->front()->str.size() - 2));

  if (!variables.count(key))
    return NULL;

  return variables[key];
}

Value* ValueProcessor::processFunction(Token* function,
                                   vector<Value*> arguments) {
  Color* color;
  string percentage;

  if(function->str == "rgb(") {
    if (arguments.size() == 3 &&
        arguments[0]->type == Value::NUMBER &&
        arguments[1]->type == Value::NUMBER &&
        arguments[2]->type == Value::NUMBER) {
      return new Color(0,0,0);
    }
    // Color rgb(@red: NUMBER, @green: NUMBER, @blue: NUMBER)
  } else if (function->str == "lighten(") {
    // Color lighten(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->lighten(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "darken(") {
    // Color darken(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->darken(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "saturate(") {
    // Color saturate(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->saturate(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "desaturate(") {
    // Color desaturate(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->desaturate(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "fadein(") {
    // Color fadein(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->fadein(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "fadeout(") {
    // Color fadeout(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->fadeout(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "spin(") {
    // Color fadein(Color, PERCENTAGE)
    if (arguments.size() == 2 &&
        arguments[0]->type == Value::COLOR &&
        arguments[1]->type == Value::PERCENTAGE) {
      static_cast<Color*>(arguments[0])->spin(arguments[1]->getPercent());
      return arguments[0];
    }

  } else if (function->str == "hsl(") {
    // Color hsl(PERCENTAGE, PERCENTAGE, PERCENTAGE)
    if (arguments.size() == 3 &&
        arguments[0]->type == Value::PERCENTAGE &&
        arguments[1]->type == Value::PERCENTAGE &&
        arguments[2]->type == Value::PERCENTAGE) {
      color = new Color(0,0,0);
      color->setHSL(arguments[0]->getPercent(),
                    arguments[1]->getPercent(),
                    arguments[2]->getPercent());
      return color;
    }
  } else if (function->str == "hue(") {
    // PERCENTAGE hue(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getHue()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }

  } else if (function->str == "saturation(") {
    // PERCENTAGE saturation(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getSaturation()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }

  } else if (function->str == "lightness(") {
    // PERCENTAGE lightness(Color)
    if (arguments.size() == 1 &&
        arguments[0]->type == Value::COLOR) {
      percentage.append(to_string(static_cast<Color*>(arguments[0])->getLightness()));
      percentage.append("%");
      return new Value(new Token(percentage, Token::PERCENTAGE));
    }
  } else
    return NULL;
  return NULL;
}
<|endoftext|>


<commit_before>#include "VehicleGLWidget.h"
#include <QKeyEvent>
#include <cmath>

// All values are half-size.
const int VEHICLE_LENGTH = 30;
const int VEHICLE_WIDTH = 15;
const int ARENA_SIZE = 500;
const int WALL_SIZE = 10;

VehicleGLWidget::VehicleGLWidget(QWidget* const parent) :
		QGLWidget(QGLFormat(QGL::SampleBuffers), parent),
		timer(this)
{
	setFocusPolicy(Qt::ClickFocus);
	connect(&timer, SIGNAL(timeout(const float&)), this, SLOT(tick(const float&)));
	connect(&timer, SIGNAL(timeout(const float&)), this, SLOT(updateGL()));
	timer.startOnShow(this);
}

void VehicleGLWidget::initializeGL()
{
	qglClearColor(Qt::black);
}

void VehicleGLWidget::tick(const float& elapsed)
{
	float accel = 0;
	if (pad.accelerator && pad.brake) {
		accel = -125;
	} else if (pad.accelerator) {
		accel = 250;
	} else if (abs(car.velocity) > 10) {
		const float dir = car.velocity > 0 ? -1 : 1;
		const float friction = pad.brake ? 1500 : 50;
		accel = dir * friction;
	} else {
		car.velocity = 0;
	}
	if (pad.left && !pad.right) {
		car.angle += M_PI * elapsed;
	}
	if (pad.right && !pad.left) {
		car.angle -= M_PI * elapsed;
	}
	float x = elapsed * cos(car.angle) * (.5 * accel * elapsed + car.velocity) + car.pos.x();
	float y = elapsed * sin(car.angle) * (.5 * accel * elapsed + car.velocity) + car.pos.y();
	car.velocity += accel * elapsed;
	car.pos.setX(x);
	car.pos.setY(y);
	const int range = ARENA_SIZE - VEHICLE_WIDTH;
	{
		const double diff = abs(x) - range;
		if (diff > 0) {
			car.velocity *= -1;
			if (x > 0) {
				// X is positive; head negative
				x = range - diff;
			} else {
				// X is negative; head positive
				x = -range + diff;
			}
		}
		car.pos.setX(x);
	}
	{
		const double diff = abs(y) - range;
		if (diff > 0) {
			car.velocity *= -1;
			if (y > 0) {
				// Y is positive; head negative
				y = range - diff;
			} else {
				// Y is negative; head positive
				y = -range + diff;
			}
		}
		car.pos.setY(y);
	}
}

void drawQuad(float left, float right, float bottom, float top)
{
	glVertex2f(right, bottom);
	glVertex2f(right, top);
	glVertex2f(left, top);
	glVertex2f(left, bottom);
}

void drawQuad(float width, float height)
{
	drawQuad(-width, width, height, -height);
}

void VehicleGLWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	// Render walls
	glBegin(GL_QUADS);
	glColor3ub(0xa3, 0xa5, 0x9f);
	drawQuad(ARENA_SIZE+WALL_SIZE, ARENA_SIZE+WALL_SIZE);
	glColor3ub(75, 54, 33);
	drawQuad(ARENA_SIZE, ARENA_SIZE);
	glEnd();
	// Render car
	glTranslatef(car.pos.x(), car.pos.y(), car.pos.z());
	glRotatef(car.angle * 180 / M_PI, 0, 0, 1);
	glBegin(GL_QUADS);
	// Body
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(VEHICLE_LENGTH, VEHICLE_WIDTH);
	// Windshields
	glColor3ub(35, 35, 35);
	drawQuad(
		-VEHICLE_LENGTH + 10,
		VEHICLE_LENGTH / 3,
		-VEHICLE_WIDTH + 2,
		VEHICLE_WIDTH - 2
		);
	// Front lights
	glColor3f(1, 1, .2);
	drawQuad(
		VEHICLE_LENGTH,
		VEHICLE_LENGTH - 3,
		-VEHICLE_WIDTH,
		VEHICLE_WIDTH
		);
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		VEHICLE_LENGTH,
		VEHICLE_LENGTH - 3,
		-VEHICLE_WIDTH + 5,
		VEHICLE_WIDTH - 5
		);
	// Rear lights
	glColor3f(.7, 0, 0);
	drawQuad(
		-VEHICLE_LENGTH,
		-VEHICLE_LENGTH + 2,
		-VEHICLE_WIDTH,
		VEHICLE_WIDTH
		);
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		-VEHICLE_LENGTH,
		-VEHICLE_LENGTH + 2,
		-VEHICLE_WIDTH + 7,
		VEHICLE_WIDTH - 7
		);
	// Top
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		-VEHICLE_LENGTH / 2 + 2,
		0,
		-VEHICLE_WIDTH + 4,
		VEHICLE_WIDTH - 4
		);
	glEnd();
}

void VehicleGLWidget::keyPressEvent(QKeyEvent* event)
{
	switch (event->key()) {
		case Qt::Key_Space:
			car.pos = QVector3D(0, 0, 0);
			car.velocity = 0;
			car.angle = 0;
			break;
		case Qt::Key_W: pad.accelerator = true; break;
		case Qt::Key_S: pad.brake = true; break;
		case Qt::Key_A: pad.left = true; break;
		case Qt::Key_D: pad.right = true; break;
		default:
			QGLWidget::keyPressEvent(event);
	}
}

void VehicleGLWidget::keyReleaseEvent(QKeyEvent* event)
{
	switch (event->key()) {
		case Qt::Key_W: pad.accelerator = false; break;
		case Qt::Key_S: pad.brake = false; break;
		case Qt::Key_A: pad.left = false; break;
		case Qt::Key_D: pad.right = false; break;
		default:
			QGLWidget::keyReleaseEvent(event);
	}
}

void VehicleGLWidget::resizeGL(int width, int height)
{
	int side = qMin(width, height);
	glViewport((width - side) / 2, (height - side) / 2, side, side);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(
		-ARENA_SIZE - WALL_SIZE, ARENA_SIZE + WALL_SIZE,
		-ARENA_SIZE - WALL_SIZE, ARENA_SIZE + WALL_SIZE,
		-100, 100);
	glMatrixMode(GL_MODELVIEW);
}
<commit_msg>Brake lights now operational<commit_after>#include "VehicleGLWidget.h"
#include <QKeyEvent>
#include <cmath>

// All values are half-size.
const int VEHICLE_LENGTH = 30;
const int VEHICLE_WIDTH = 15;
const int ARENA_SIZE = 500;
const int WALL_SIZE = 10;

VehicleGLWidget::VehicleGLWidget(QWidget* const parent) :
		QGLWidget(QGLFormat(QGL::SampleBuffers), parent),
		timer(this)
{
	setFocusPolicy(Qt::ClickFocus);
	connect(&timer, SIGNAL(timeout(const float&)), this, SLOT(tick(const float&)));
	connect(&timer, SIGNAL(timeout(const float&)), this, SLOT(updateGL()));
	timer.startOnShow(this);
}

void VehicleGLWidget::initializeGL()
{
	qglClearColor(Qt::black);
}

void VehicleGLWidget::tick(const float& elapsed)
{
	float accel = 0;
	if (pad.accelerator && pad.brake) {
		accel = -125;
	} else if (pad.accelerator) {
		accel = 250;
	} else if (abs(car.velocity) > 10) {
		const float dir = car.velocity > 0 ? -1 : 1;
		const float friction = pad.brake ? 1500 : 50;
		accel = dir * friction;
	} else {
		car.velocity = 0;
	}
	if (pad.left && !pad.right) {
		car.angle += M_PI * elapsed;
	}
	if (pad.right && !pad.left) {
		car.angle -= M_PI * elapsed;
	}
	float x = elapsed * cos(car.angle) * (.5 * accel * elapsed + car.velocity) + car.pos.x();
	float y = elapsed * sin(car.angle) * (.5 * accel * elapsed + car.velocity) + car.pos.y();
	car.velocity += accel * elapsed;
	car.pos.setX(x);
	car.pos.setY(y);
	const int range = ARENA_SIZE - VEHICLE_WIDTH;
	{
		const double diff = abs(x) - range;
		if (diff > 0) {
			car.velocity *= -1;
			if (x > 0) {
				// X is positive; head negative
				x = range - diff;
			} else {
				// X is negative; head positive
				x = -range + diff;
			}
		}
		car.pos.setX(x);
	}
	{
		const double diff = abs(y) - range;
		if (diff > 0) {
			car.velocity *= -1;
			if (y > 0) {
				// Y is positive; head negative
				y = range - diff;
			} else {
				// Y is negative; head positive
				y = -range + diff;
			}
		}
		car.pos.setY(y);
	}
}

void drawQuad(float left, float right, float bottom, float top)
{
	glVertex2f(right, bottom);
	glVertex2f(right, top);
	glVertex2f(left, top);
	glVertex2f(left, bottom);
}

void drawQuad(float width, float height)
{
	drawQuad(-width, width, height, -height);
}

void VehicleGLWidget::paintGL()
{
	glClear(GL_COLOR_BUFFER_BIT);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	// Render walls
	glBegin(GL_QUADS);
	glColor3ub(0xa3, 0xa5, 0x9f);
	drawQuad(ARENA_SIZE+WALL_SIZE, ARENA_SIZE+WALL_SIZE);
	glColor3ub(75, 54, 33);
	drawQuad(ARENA_SIZE, ARENA_SIZE);
	glEnd();
	// Render car
	glTranslatef(car.pos.x(), car.pos.y(), car.pos.z());
	glRotatef(car.angle * 180 / M_PI, 0, 0, 1);
	glBegin(GL_QUADS);
	// Body
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(VEHICLE_LENGTH, VEHICLE_WIDTH);
	// Windshields
	glColor3ub(35, 35, 35);
	drawQuad(
		-VEHICLE_LENGTH + 10,
		VEHICLE_LENGTH / 3,
		-VEHICLE_WIDTH + 2,
		VEHICLE_WIDTH - 2
		);
	// Front lights
	glColor3f(1, 1, .2);
	drawQuad(
		VEHICLE_LENGTH,
		VEHICLE_LENGTH - 3,
		-VEHICLE_WIDTH,
		VEHICLE_WIDTH
		);
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		VEHICLE_LENGTH,
		VEHICLE_LENGTH - 3,
		-VEHICLE_WIDTH + 5,
		VEHICLE_WIDTH - 5
		);
	// Rear lights
	if (pad.brake) {
		glColor3f(1, .2, .2);
	} else {
		glColor3f(.7, 0, 0);
	}
	drawQuad(
		-VEHICLE_LENGTH,
		-VEHICLE_LENGTH + 2,
		-VEHICLE_WIDTH,
		VEHICLE_WIDTH
		);
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		-VEHICLE_LENGTH,
		-VEHICLE_LENGTH + 2,
		-VEHICLE_WIDTH + 7,
		VEHICLE_WIDTH - 7
		);
	// Top
	glColor3ub(0, 0x5b, 0xa9);
	drawQuad(
		-VEHICLE_LENGTH / 2 + 2,
		0,
		-VEHICLE_WIDTH + 4,
		VEHICLE_WIDTH - 4
		);
	glEnd();
}

void VehicleGLWidget::keyPressEvent(QKeyEvent* event)
{
	switch (event->key()) {
		case Qt::Key_Space:
			car.pos = QVector3D(0, 0, 0);
			car.velocity = 0;
			car.angle = 0;
			break;
		case Qt::Key_W: pad.accelerator = true; break;
		case Qt::Key_S: pad.brake = true; break;
		case Qt::Key_A: pad.left = true; break;
		case Qt::Key_D: pad.right = true; break;
		default:
			QGLWidget::keyPressEvent(event);
	}
}

void VehicleGLWidget::keyReleaseEvent(QKeyEvent* event)
{
	switch (event->key()) {
		case Qt::Key_W: pad.accelerator = false; break;
		case Qt::Key_S: pad.brake = false; break;
		case Qt::Key_A: pad.left = false; break;
		case Qt::Key_D: pad.right = false; break;
		default:
			QGLWidget::keyReleaseEvent(event);
	}
}

void VehicleGLWidget::resizeGL(int width, int height)
{
	int side = qMin(width, height);
	glViewport((width - side) / 2, (height - side) / 2, side, side);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	glOrtho(
		-ARENA_SIZE - WALL_SIZE, ARENA_SIZE + WALL_SIZE,
		-ARENA_SIZE - WALL_SIZE, ARENA_SIZE + WALL_SIZE,
		-100, 100);
	glMatrixMode(GL_MODELVIEW);
}
<|endoftext|>


<commit_before>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_GDT_OPERATORS_WEIGHTED_L2_HH
#define DUNE_GDT_OPERATORS_WEIGHTED_L2_HH

#include <type_traits>

#include <dune/stuff/functions/interfaces.hh>
#include <dune/stuff/grid/layers.hh>
#include <dune/stuff/la/container.hh>

#include <dune/gdt/localevaluation/product.hh>
#include <dune/gdt/localoperator/integrals.hh>
#include <dune/gdt/operators/default.hh>
#include <dune/gdt/spaces/interface.hh>

namespace Dune {
namespace GDT {

// //////////////////////////// //
// WeightedL2LocalizableProduct //
// //////////////////////////// //

template <class WeightFunctionType, class GridView, class Range, class Source = Range,
          class Field                                                         = typename Range::RangeFieldType>
class WeightedL2LocalizableProduct : public LocalizableProductDefault<GridView, Range, Source, Field>
{
  typedef LocalizableProductDefault<GridView, Range, Source, Field> BaseType;
  typedef LocalVolumeIntegralOperator<LocalEvaluation::Product<WeightFunctionType>> LocalWeightedL2OperatorType;

public:
  template <class... Args>
  WeightedL2LocalizableProduct(const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(weight)
  {
    this->add(local_weighted_l2_operator_);
  }

  template <class... Args>
  WeightedL2LocalizableProduct(const size_t over_integrate, const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(over_integrate, weight)
  {
    this->add(local_weighted_l2_operator_);
  }

private:
  const LocalWeightedL2OperatorType local_weighted_l2_operator_;
}; // class WeightedL2LocalizableProduct

// //////////////////////////////////// //
// make_weighted_l2_localizable_product //
// //////////////////////////////////// //

/**
 * \sa WeightedL2LocalizableProduct
 */
template <class WeightFunctionType, class GridViewType, class RangeType, class SourceType>
typename std::enable_if<Stuff::is_localizable_function<WeightFunctionType>::value
                            && Stuff::Grid::is_grid_layer<GridViewType>::value
                            && Stuff::is_localizable_function<RangeType>::value
                            && Stuff::is_localizable_function<SourceType>::value,
                        std::unique_ptr<WeightedL2LocalizableProduct<WeightFunctionType, GridViewType, RangeType,
                                                                     SourceType>>>::type
make_weighted_l2_localizable_product(const WeightFunctionType& weight, const GridViewType& grid_view,
                                     const RangeType& range, const SourceType& source, const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2LocalizableProduct<WeightFunctionType, GridViewType, RangeType, SourceType>>(
      over_integrate, weight, grid_view, range, source);
}

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_OPERATORS_WEIGHTED_L2_HH
<commit_msg>[operators.weighted-l2] add WeightedL2MatrixOperator and factory<commit_after>// This file is part of the dune-gdt project:
//   http://users.dune-project.org/projects/dune-gdt
// Copyright holders: Felix Schindler
// License: BSD 2-Clause License (http://opensource.org/licenses/BSD-2-Clause)

#ifndef DUNE_GDT_OPERATORS_WEIGHTED_L2_HH
#define DUNE_GDT_OPERATORS_WEIGHTED_L2_HH

#include <type_traits>

#include <dune/stuff/functions/interfaces.hh>
#include <dune/stuff/grid/layers.hh>
#include <dune/stuff/la/container.hh>

#include <dune/gdt/localevaluation/product.hh>
#include <dune/gdt/localoperator/integrals.hh>
#include <dune/gdt/operators/default.hh>
#include <dune/gdt/spaces/interface.hh>

namespace Dune {
namespace GDT {

// //////////////////////////// //
// WeightedL2LocalizableProduct //
// //////////////////////////// //

template <class WeightFunctionType, class GridView, class Range, class Source = Range,
          class Field                                                         = typename Range::RangeFieldType>
class WeightedL2LocalizableProduct : public LocalizableProductDefault<GridView, Range, Source, Field>
{
  typedef LocalizableProductDefault<GridView, Range, Source, Field> BaseType;
  typedef LocalVolumeIntegralOperator<LocalEvaluation::Product<WeightFunctionType>> LocalWeightedL2OperatorType;

public:
  template <class... Args>
  WeightedL2LocalizableProduct(const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(weight)
  {
    this->add(local_weighted_l2_operator_);
  }

  template <class... Args>
  WeightedL2LocalizableProduct(const size_t over_integrate, const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(over_integrate, weight)
  {
    this->add(local_weighted_l2_operator_);
  }

private:
  const LocalWeightedL2OperatorType local_weighted_l2_operator_;
}; // class WeightedL2LocalizableProduct

// //////////////////////////////////// //
// make_weighted_l2_localizable_product //
// //////////////////////////////////// //

/**
 * \sa WeightedL2LocalizableProduct
 */
template <class WeightFunctionType, class GridViewType, class RangeType, class SourceType>
typename std::enable_if<Stuff::is_localizable_function<WeightFunctionType>::value
                            && Stuff::Grid::is_grid_layer<GridViewType>::value
                            && Stuff::is_localizable_function<RangeType>::value
                            && Stuff::is_localizable_function<SourceType>::value,
                        std::unique_ptr<WeightedL2LocalizableProduct<WeightFunctionType, GridViewType, RangeType,
                                                                     SourceType>>>::type
make_weighted_l2_localizable_product(const WeightFunctionType& weight, const GridViewType& grid_view,
                                     const RangeType& range, const SourceType& source, const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2LocalizableProduct<WeightFunctionType, GridViewType, RangeType, SourceType>>(
      over_integrate, weight, grid_view, range, source);
}

// //////////////////////// //
// WeightedL2MatrixOperator //
// //////////////////////// //

template <class WeightFunctionType, class RangeSpace,
          class Matrix   = typename Stuff::LA::Container<typename RangeSpace::RangeFieldType>::MatrixType,
          class GridView = typename RangeSpace::GridViewType, class SourceSpace = RangeSpace,
          class Field = typename RangeSpace::RangeFieldType>
class WeightedL2MatrixOperator
    : public MatrixOperatorDefault<Matrix, RangeSpace, GridView, SourceSpace, Field, ChoosePattern::volume>
{
  typedef MatrixOperatorDefault<Matrix, RangeSpace, GridView, SourceSpace, Field, ChoosePattern::volume> BaseType;
  typedef LocalVolumeIntegralOperator<LocalEvaluation::Product<WeightFunctionType>> LocalWeightedL2OperatorType;

public:
  template <class... Args>
  explicit WeightedL2MatrixOperator(const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(weight)
  {
    this->add(local_weighted_l2_operator_);
  }

  template <class... Args>
  explicit WeightedL2MatrixOperator(const size_t over_integrate, const WeightFunctionType& weight, Args&&... args)
    : BaseType(std::forward<Args>(args)...)
    , local_weighted_l2_operator_(over_integrate, weight)
  {
    this->add(local_weighted_l2_operator_);
  }

private:
  const LocalWeightedL2OperatorType local_weighted_l2_operator_;
}; // class WeightedL2MatrixOperator

// //////////////////////////////// //
// make_weighted_l2_matrix_operator //
// //////////////////////////////// //

// without matrix

/**
 * \brief Creates a weighted L2 matrix operator (MatrixType has to be supllied, a matrix is created automatically,
 *        source and range space are given by space, grid_view of the space is used).
 * \note  MatrixType has to be supplied, i.e., use like
\code
auto op = make_weighted_l2_matrix_operator< MatrixType >(weight, space);
\endcode
 */
template <class MatrixType, class WeightFunctionType, class SpaceType>
typename std::enable_if<Stuff::LA::is_matrix<MatrixType>::value
                            && Stuff::is_localizable_function<WeightFunctionType>::value && is_space<SpaceType>::value,
                        std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType>>>::type
make_weighted_l2_matrix_operator(const WeightFunctionType& weight, const SpaceType& space,
                                 const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType>>(
      over_integrate, weight, space);
}

/**
 * \brief Creates a weighted L2 matrix operator (MatrixType has to be supllied, a matrix is created automatically,
 *        source and range space are given by space).
 * \note  MatrixType has to be supplied, i.e., use like
\code
auto op = make_weighted_l2_matrix_operator< MatrixType >(weight, space, grid_view);
\endcode
 */
template <class MatrixType, class WeightFunctionType, class SpaceType, class GridViewType>
typename std::
    enable_if<Stuff::LA::is_matrix<MatrixType>::value && Stuff::is_localizable_function<WeightFunctionType>::value
                  && is_space<SpaceType>::value && Stuff::Grid::is_grid_layer<GridViewType>::value,
              std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType, GridViewType>>>::type
    make_weighted_l2_matrix_operator(const WeightFunctionType& weight, const SpaceType& space,
                                     const GridViewType& grid_view, const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType, GridViewType>>(
      over_integrate, weight, space, grid_view);
}

/**
 * \brief Creates a weighted L2 matrix operator (MatrixType has to be supllied, a matrix is created automatically).
 * \note  MatrixType has to be supplied, i.e., use like
\code
auto op = make_weighted_l2_matrix_operator< MatrixType >(weight, range_space, source_space, grid_view);
\endcode
 */
template <class MatrixType, class WeightFunctionType, class RangeSpaceType, class SourceSpaceType, class GridViewType>
typename std::enable_if<Stuff::LA::is_matrix<MatrixType>::value
                            && Stuff::is_localizable_function<WeightFunctionType>::value
                            && is_space<RangeSpaceType>::value && is_space<SourceSpaceType>::value
                            && Stuff::Grid::is_grid_layer<GridViewType>::value,
                        std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, RangeSpaceType, MatrixType,
                                                                 GridViewType, SourceSpaceType>>>::type
make_weighted_l2_matrix_operator(const WeightFunctionType& weight, const RangeSpaceType& range_space,
                                 const SourceSpaceType& source_space, const GridViewType& grid_view,
                                 const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType,
                                                   RangeSpaceType,
                                                   MatrixType,
                                                   GridViewType,
                                                   SourceSpaceType>>(
      over_integrate, weight, range_space, source_space, grid_view);
}

// with matrix

/**
 * \brief Creates a weighted L2 matrix operator (source and range space are given by space, grid_view of the space is
 *        used).
 */
template <class WeightFunctionType, class MatrixType, class SpaceType>
typename std::enable_if<Stuff::is_localizable_function<WeightFunctionType>::value
                            && Stuff::LA::is_matrix<MatrixType>::value && is_space<SpaceType>::value,
                        std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType>>>::type
make_weighted_l2_matrix_operator(const WeightFunctionType& weight, MatrixType& matrix, const SpaceType& space,
                                 const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType>>(
      over_integrate, weight, matrix, space);
}

/**
 * \brief Creates a weighted L2 matrix operator (source and range space are given by space).
 */
template <class WeightFunctionType, class MatrixType, class SpaceType, class GridViewType>
typename std::
    enable_if<Stuff::is_localizable_function<WeightFunctionType>::value && Stuff::LA::is_matrix<MatrixType>::value
                  && is_space<SpaceType>::value && Stuff::Grid::is_grid_layer<GridViewType>::value,
              std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType, GridViewType>>>::type
    make_weighted_l2_matrix_operator(const WeightFunctionType& weight, MatrixType& matrix, const SpaceType& space,
                                     const GridViewType& grid_view, const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType, SpaceType, MatrixType, GridViewType>>(
      over_integrate, weight, matrix, space, grid_view);
}

/**
 * \brief Creates a weighted L2 matrix operator.
 */
template <class WeightFunctionType, class MatrixType, class RangeSpaceType, class SourceSpaceType, class GridViewType>
typename std::enable_if<Stuff::is_localizable_function<WeightFunctionType>::value
                            && Stuff::LA::is_matrix<MatrixType>::value && is_space<RangeSpaceType>::value
                            && is_space<SourceSpaceType>::value && Stuff::Grid::is_grid_layer<GridViewType>::value,
                        std::unique_ptr<WeightedL2MatrixOperator<WeightFunctionType, RangeSpaceType, MatrixType,
                                                                 GridViewType, SourceSpaceType>>>::type
make_weighted_l2_matrix_operator(const WeightFunctionType& weight, MatrixType& matrix,
                                 const RangeSpaceType& range_space, const SourceSpaceType& source_space,
                                 const GridViewType& grid_view, const size_t over_integrate = 0)
{
  return DSC::make_unique<WeightedL2MatrixOperator<WeightFunctionType,
                                                   RangeSpaceType,
                                                   MatrixType,
                                                   GridViewType,
                                                   SourceSpaceType>>(
      over_integrate, weight, matrix, range_space, source_space, grid_view);
}

} // namespace GDT
} // namespace Dune

#endif // DUNE_GDT_OPERATORS_WEIGHTED_L2_HH
<|endoftext|>


<commit_before>#include "universe.h"
#include "core/blob.h"
#include "core/crc32.h"
#include "core/matrix.h"
#include "core/json_serializer.h"

namespace Lumix
{

static const int RESERVED_ENTITIES_COUNT = 5000;

Universe::~Universe()
{
}

Universe::Universe(IAllocator& allocator)
	: m_allocator(allocator)
	, m_name_to_id_map(m_allocator)
	, m_id_to_name_map(m_allocator)
	, m_transformations(m_allocator)
	, m_component_destroyed(m_allocator)
	, m_entity_created(m_allocator)
	, m_entity_destroyed(m_allocator)
	, m_entity_moved(m_allocator)
	, m_id_map(m_allocator)
	, m_first_free_slot(-1)
{
	m_transformations.reserve(RESERVED_ENTITIES_COUNT);
	m_id_map.reserve(RESERVED_ENTITIES_COUNT);
}

const Vec3& Universe::getPosition(Entity entity) const
{
	return m_transformations[m_id_map[entity]].position;
}

const Quat& Universe::getRotation(Entity entity) const
{
	return m_transformations[m_id_map[entity]].rotation;
}

void Universe::setRotation(Entity entity, const Quat& rot)
{
	m_transformations[m_id_map[entity]].rotation = rot;
	entityTransformed().invoke(entity);
}

void Universe::setRotation(Entity entity, float x, float y, float z, float w)
{
	m_transformations[m_id_map[entity]].rotation.set(x, y, z, w);
	entityTransformed().invoke(entity);
}

bool Universe::hasEntity(Entity entity) const
{
	return entity >= 0 && entity < m_id_map.size() && m_id_map[entity] >= 0;
}

void Universe::setMatrix(Entity entity, const Matrix& mtx)
{
	Quat rot;
	mtx.getRotation(rot);
	m_transformations[m_id_map[entity]].position = mtx.getTranslation();
	m_transformations[m_id_map[entity]].rotation = rot;
	entityTransformed().invoke(entity);
}

Matrix Universe::getPositionAndRotation(Entity entity) const
{
	Matrix mtx;
	auto& transform = m_transformations[m_id_map[entity]];
	transform.rotation.toMatrix(mtx);
	mtx.setTranslation(transform.position);
	return mtx;
}

Matrix Universe::getMatrix(Entity entity) const
{
	Matrix mtx;
	auto& transform = m_transformations[m_id_map[entity]];
	transform.rotation.toMatrix(mtx);
	mtx.setTranslation(transform.position);
	mtx.multiply3x3(transform.scale);
	return mtx;
}

void Universe::setPosition(Entity entity, float x, float y, float z)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.position.set(x, y, z);
	entityTransformed().invoke(entity);
}

void Universe::setPosition(Entity entity, const Vec3& pos)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.position = pos;
	entityTransformed().invoke(entity);
}

void Universe::setEntityName(Entity entity, const char* name)
{
	int name_index = m_id_to_name_map.find(entity);
	if (name_index >= 0)
	{
		uint32_t hash = crc32(m_id_to_name_map.at(name_index).c_str());
		m_name_to_id_map.erase(hash);
		m_id_to_name_map.eraseAt(name_index);
	}

	if (name && name[0] != '\0')
	{
		m_name_to_id_map.insert(crc32(name), entity);
		m_id_to_name_map.insert(entity, string(name, getAllocator()));
	}
}

const char* Universe::getEntityName(Entity entity) const
{
	int name_index = m_id_to_name_map.find(entity);
	return name_index < 0 ? "" : m_id_to_name_map.at(name_index).c_str();
}

void Universe::createEntity(Entity entity)
{
	ASSERT(entity >= 0);
	int id = m_first_free_slot;
	int prev_id = -1;
	while (id >= 0 && id != entity)
	{
		prev_id = id;
		id = -m_id_map[id];
	}

	ASSERT(id == entity);
	if (prev_id == -1)
	{
		m_first_free_slot = -m_id_map[entity];
	}
	else
	{
		m_id_map[prev_id] = m_id_map[entity];
	}
	m_id_map[entity] = m_transformations.size();

	Transformation& trans = m_transformations.pushEmpty();
	trans.position.set(0, 0, 0);
	trans.rotation.set(0, 0, 0, 1);
	trans.scale = 1;
	trans.id = entity;

	m_entity_created.invoke(entity);
}

Entity Universe::createEntity(const Vec3& position, const Quat& rotation)
{
	int global_id = 0;
	if (m_first_free_slot >= 0)
	{
		global_id = m_first_free_slot;
		m_first_free_slot = -m_id_map[m_first_free_slot];
		m_id_map[global_id] = m_transformations.size();
	}
	else
	{
		global_id = m_id_map.size();
		m_id_map.push(m_transformations.size());
	}

	Transformation& trans = m_transformations.pushEmpty();
	trans.position = position;
	trans.rotation = rotation;
	trans.scale = 1;
	trans.id = global_id;
	m_entity_created.invoke(global_id);

	return global_id;
}

void Universe::destroyEntity(Entity entity)
{
	if (entity < 0 || m_id_map[entity] < 0) return;

	int last_item_id = m_transformations.back().id;
	m_id_map[last_item_id] = m_id_map[entity];
	m_transformations.eraseFast(m_id_map[entity]);
	m_id_map[entity] = m_first_free_slot >= 0 ? -m_first_free_slot : INT_MIN;
	m_first_free_slot = entity;
	m_entity_destroyed.invoke(entity);
}

Entity Universe::getEntityFromDenseIdx(int idx)
{
	return m_transformations[idx].id;
}

Entity Universe::getFirstEntity()
{
	for (int i = 0; i < m_id_map.size(); ++i)
	{
		if (m_id_map[i] >= 0)
		{
			return i;
		}
	}
	return INVALID_ENTITY;
}

Entity Universe::getNextEntity(Entity entity)
{
	for (int i = entity + 1; i < m_id_map.size(); ++i)
	{
		if (m_id_map[i] >= 0)
		{
			return i;
		}
	}
	return INVALID_ENTITY;
}

void Universe::serialize(OutputBlob& serializer)
{
	serializer.write((int32_t)m_transformations.size());
	serializer.write(
		&m_transformations[0], sizeof(m_transformations[0]) * m_transformations.size());
	serializer.write((int32_t)m_id_to_name_map.size());
	for (int i = 0, c = m_id_to_name_map.size(); i < c; ++i)
	{
		serializer.write(m_id_to_name_map.getKey(i));
		serializer.writeString(m_id_to_name_map.at(i).c_str());
	}

	serializer.write(m_first_free_slot);
	serializer.write((int32_t)m_id_map.size());
	if (!m_id_map.empty())
	{
		serializer.write(&m_id_map[0], sizeof(m_id_map[0]) * m_id_map.size());
	}
}

void Universe::deserialize(InputBlob& serializer)
{
	int32_t count;
	serializer.read(count);
	m_transformations.resize(count);

	serializer.read(&m_transformations[0], sizeof(m_transformations[0]) * m_transformations.size());

	serializer.read(count);
	m_id_to_name_map.clear();
	m_name_to_id_map.clear();
	for (int i = 0; i < count; ++i)
	{
		uint32_t key;
		char name[50];
		serializer.read(key);
		serializer.readString(name, sizeof(name));
		m_id_to_name_map.insert(key, string(name, m_allocator));
		m_name_to_id_map.insert(crc32(name), key);
	}

	serializer.read(m_first_free_slot);
	serializer.read(count);
	m_id_map.resize(count);
	if (!m_id_map.empty())
	{
		serializer.read(&m_id_map[0], sizeof(m_id_map[0]) * count);
	}
}

void Universe::setScale(Entity entity, float scale)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.scale = scale;
	entityTransformed().invoke(entity);
}

float Universe::getScale(Entity entity)
{
	auto& transform = m_transformations[m_id_map[entity]];
	return transform.scale;
}

void Universe::destroyComponent(Entity entity, uint32_t component_type, IScene* scene, int index)
{
	m_component_destroyed.invoke(ComponentUID(entity, component_type, scene, index));
}

void Universe::addComponent(Entity entity, uint32_t component_type, IScene* scene, int index)
{
	ComponentUID cmp(entity, component_type, scene, index);
	if (m_component_added.isValid())
	{
		m_component_added.invoke(cmp);
	}
}

bool Universe::nameExists(const char* name) const
{
	return m_name_to_id_map.find(crc32(name)) != -1;
}

} // !namespace Lumix
<commit_msg>recently created entity does not have the name of the recently deleted entity - #566<commit_after>#include "universe.h"
#include "core/blob.h"
#include "core/crc32.h"
#include "core/matrix.h"
#include "core/json_serializer.h"

namespace Lumix
{

static const int RESERVED_ENTITIES_COUNT = 5000;

Universe::~Universe()
{
}

Universe::Universe(IAllocator& allocator)
	: m_allocator(allocator)
	, m_name_to_id_map(m_allocator)
	, m_id_to_name_map(m_allocator)
	, m_transformations(m_allocator)
	, m_component_destroyed(m_allocator)
	, m_entity_created(m_allocator)
	, m_entity_destroyed(m_allocator)
	, m_entity_moved(m_allocator)
	, m_id_map(m_allocator)
	, m_first_free_slot(-1)
{
	m_transformations.reserve(RESERVED_ENTITIES_COUNT);
	m_id_map.reserve(RESERVED_ENTITIES_COUNT);
}

const Vec3& Universe::getPosition(Entity entity) const
{
	return m_transformations[m_id_map[entity]].position;
}

const Quat& Universe::getRotation(Entity entity) const
{
	return m_transformations[m_id_map[entity]].rotation;
}

void Universe::setRotation(Entity entity, const Quat& rot)
{
	m_transformations[m_id_map[entity]].rotation = rot;
	entityTransformed().invoke(entity);
}

void Universe::setRotation(Entity entity, float x, float y, float z, float w)
{
	m_transformations[m_id_map[entity]].rotation.set(x, y, z, w);
	entityTransformed().invoke(entity);
}

bool Universe::hasEntity(Entity entity) const
{
	return entity >= 0 && entity < m_id_map.size() && m_id_map[entity] >= 0;
}

void Universe::setMatrix(Entity entity, const Matrix& mtx)
{
	Quat rot;
	mtx.getRotation(rot);
	m_transformations[m_id_map[entity]].position = mtx.getTranslation();
	m_transformations[m_id_map[entity]].rotation = rot;
	entityTransformed().invoke(entity);
}

Matrix Universe::getPositionAndRotation(Entity entity) const
{
	Matrix mtx;
	auto& transform = m_transformations[m_id_map[entity]];
	transform.rotation.toMatrix(mtx);
	mtx.setTranslation(transform.position);
	return mtx;
}

Matrix Universe::getMatrix(Entity entity) const
{
	Matrix mtx;
	auto& transform = m_transformations[m_id_map[entity]];
	transform.rotation.toMatrix(mtx);
	mtx.setTranslation(transform.position);
	mtx.multiply3x3(transform.scale);
	return mtx;
}

void Universe::setPosition(Entity entity, float x, float y, float z)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.position.set(x, y, z);
	entityTransformed().invoke(entity);
}

void Universe::setPosition(Entity entity, const Vec3& pos)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.position = pos;
	entityTransformed().invoke(entity);
}

void Universe::setEntityName(Entity entity, const char* name)
{
	int name_index = m_id_to_name_map.find(entity);
	if (name_index >= 0)
	{
		uint32_t hash = crc32(m_id_to_name_map.at(name_index).c_str());
		m_name_to_id_map.erase(hash);
		m_id_to_name_map.eraseAt(name_index);
	}

	if (name && name[0] != '\0')
	{
		m_name_to_id_map.insert(crc32(name), entity);
		m_id_to_name_map.insert(entity, string(name, getAllocator()));
	}
}

const char* Universe::getEntityName(Entity entity) const
{
	int name_index = m_id_to_name_map.find(entity);
	return name_index < 0 ? "" : m_id_to_name_map.at(name_index).c_str();
}

void Universe::createEntity(Entity entity)
{
	ASSERT(entity >= 0);
	int id = m_first_free_slot;
	int prev_id = -1;
	while (id >= 0 && id != entity)
	{
		prev_id = id;
		id = -m_id_map[id];
	}

	ASSERT(id == entity);
	if (prev_id == -1)
	{
		m_first_free_slot = -m_id_map[entity];
	}
	else
	{
		m_id_map[prev_id] = m_id_map[entity];
	}
	m_id_map[entity] = m_transformations.size();

	Transformation& trans = m_transformations.pushEmpty();
	trans.position.set(0, 0, 0);
	trans.rotation.set(0, 0, 0, 1);
	trans.scale = 1;
	trans.id = entity;

	m_entity_created.invoke(entity);
}

Entity Universe::createEntity(const Vec3& position, const Quat& rotation)
{
	int global_id = 0;
	if (m_first_free_slot >= 0)
	{
		global_id = m_first_free_slot;
		m_first_free_slot = -m_id_map[m_first_free_slot];
		m_id_map[global_id] = m_transformations.size();
	}
	else
	{
		global_id = m_id_map.size();
		m_id_map.push(m_transformations.size());
	}

	Transformation& trans = m_transformations.pushEmpty();
	trans.position = position;
	trans.rotation = rotation;
	trans.scale = 1;
	trans.id = global_id;
	m_entity_created.invoke(global_id);

	return global_id;
}

void Universe::destroyEntity(Entity entity)
{
	if (entity < 0 || m_id_map[entity] < 0) return;

	int last_item_id = m_transformations.back().id;
	m_id_map[last_item_id] = m_id_map[entity];
	m_transformations.eraseFast(m_id_map[entity]);
	m_id_map[entity] = m_first_free_slot >= 0 ? -m_first_free_slot : INT_MIN;

	int name_index = m_id_to_name_map.find(entity);
	if (name_index >= 0)
	{
		uint32_t name_hash = crc32(m_id_to_name_map.at(name_index).c_str());
		m_name_to_id_map.erase(name_hash);
		m_id_to_name_map.eraseAt(name_index);
	}

	m_first_free_slot = entity;
	m_entity_destroyed.invoke(entity);
}

Entity Universe::getEntityFromDenseIdx(int idx)
{
	return m_transformations[idx].id;
}

Entity Universe::getFirstEntity()
{
	for (int i = 0; i < m_id_map.size(); ++i)
	{
		if (m_id_map[i] >= 0)
		{
			return i;
		}
	}
	return INVALID_ENTITY;
}

Entity Universe::getNextEntity(Entity entity)
{
	for (int i = entity + 1; i < m_id_map.size(); ++i)
	{
		if (m_id_map[i] >= 0)
		{
			return i;
		}
	}
	return INVALID_ENTITY;
}

void Universe::serialize(OutputBlob& serializer)
{
	serializer.write((int32_t)m_transformations.size());
	serializer.write(
		&m_transformations[0], sizeof(m_transformations[0]) * m_transformations.size());
	serializer.write((int32_t)m_id_to_name_map.size());
	for (int i = 0, c = m_id_to_name_map.size(); i < c; ++i)
	{
		serializer.write(m_id_to_name_map.getKey(i));
		serializer.writeString(m_id_to_name_map.at(i).c_str());
	}

	serializer.write(m_first_free_slot);
	serializer.write((int32_t)m_id_map.size());
	if (!m_id_map.empty())
	{
		serializer.write(&m_id_map[0], sizeof(m_id_map[0]) * m_id_map.size());
	}
}

void Universe::deserialize(InputBlob& serializer)
{
	int32_t count;
	serializer.read(count);
	m_transformations.resize(count);

	serializer.read(&m_transformations[0], sizeof(m_transformations[0]) * m_transformations.size());

	serializer.read(count);
	m_id_to_name_map.clear();
	m_name_to_id_map.clear();
	for (int i = 0; i < count; ++i)
	{
		uint32_t key;
		char name[50];
		serializer.read(key);
		serializer.readString(name, sizeof(name));
		m_id_to_name_map.insert(key, string(name, m_allocator));
		m_name_to_id_map.insert(crc32(name), key);
	}

	serializer.read(m_first_free_slot);
	serializer.read(count);
	m_id_map.resize(count);
	if (!m_id_map.empty())
	{
		serializer.read(&m_id_map[0], sizeof(m_id_map[0]) * count);
	}
}

void Universe::setScale(Entity entity, float scale)
{
	auto& transform = m_transformations[m_id_map[entity]];
	transform.scale = scale;
	entityTransformed().invoke(entity);
}

float Universe::getScale(Entity entity)
{
	auto& transform = m_transformations[m_id_map[entity]];
	return transform.scale;
}

void Universe::destroyComponent(Entity entity, uint32_t component_type, IScene* scene, int index)
{
	m_component_destroyed.invoke(ComponentUID(entity, component_type, scene, index));
}

void Universe::addComponent(Entity entity, uint32_t component_type, IScene* scene, int index)
{
	ComponentUID cmp(entity, component_type, scene, index);
	if (m_component_added.isValid())
	{
		m_component_added.invoke(cmp);
	}
}

bool Universe::nameExists(const char* name) const
{
	return m_name_to_id_map.find(crc32(name)) != -1;
}

} // !namespace Lumix
<|endoftext|>