<commit_before>#ifndef NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP
#define NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP 1

#include <vector>

namespace nonstd
{
namespace predicate
{
namespace impl
{

/**
 * Default predicate always returning true.
 */
template <typename TObject>
struct default_predicate {
	bool
	operator()(const TObject&) const
	{
		return true;
	}
};

template <typename TPredicate, typename TObject>
bool
evaluate_predicates(const std::vector<TPredicate>& predicates,
	const TObject& object)
{
	for (typename std::vector<TPredicate>::const_iterator it =
		predicates.begin(); it != predicates.end(); ++it) {

		if ((*it)(object) != true) {
			return false;
		}
	}

	return true;
}

} // namespace impl.

/**
 * Composed predicate allows you to combine multiple predicates into single
 * one. Implements fluent interface, e.g.:
 *	composed_predicate.and_(predicate1).and_(predicate2);
 */
template <typename TObject, typename TPredicate1,
	typename TPredicate2 = impl::default_predicate<TObject> >
class composed_predicate {
public:
	/**
	 * Adds the specified predicate to composed predicate list. When
	 * operator()(TObject object) will be called, logical and will be applied
	 * to these predicates.
	 */
	composed_predicate<TObject, TPredicate1, TPredicate2>&
	and_(const TPredicate1& predicate)
	{
		this->predicates1_.push_back(predicate);
		return *this;
	}

	/**
	 * Adds the specified predicate to composed predicate list. When
	 * operator()(TObject object) will be called, logical and will be applied
	 * to these predicates.
	 */
	composed_predicate<TObject, TPredicate1, TPredicate2>&
	and_(const TPredicate2& predicate)
	{
		this->predicates2_.push_back(predicate);
		return *this;
	}

	bool
	operator()(const TObject& object) const
	{
		return impl::evaluate_predicates(this->predicates1_, object)
			&& impl::evaluate_predicates(this->predicates2_, object);
	}

private:
	std::vector<TPredicate1> predicates1_;
	std::vector<TPredicate2> predicates2_;
};

} // namespace predicate.
} // namespace nonstd.

#endif /* NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP */
<commit_msg>Added make_composed_predicate() factory.<commit_after>#ifndef NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP
#define NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP 1

#include <vector>

namespace nonstd
{
namespace predicate
{
namespace impl
{

/**
 * Default predicate always returning true.
 */
template <typename TObject>
struct default_predicate {
	bool
	operator()(const TObject&) const
	{
		return true;
	}
};

template <typename TPredicate, typename TObject>
bool
evaluate_predicates(const std::vector<TPredicate>& predicates,
	const TObject& object)
{
	for (typename std::vector<TPredicate>::const_iterator it =
		predicates.begin(); it != predicates.end(); ++it) {

		if ((*it)(object) != true) {
			return false;
		}
	}

	return true;
}

} // namespace impl.

/**
 * Composed predicate allows you to combine multiple predicates into single
 * one. Implements fluent interface, e.g.:
 *	composed_predicate.and_(predicate1).and_(predicate2);
 */
template <typename TObject, typename TPredicate1,
	typename TPredicate2 = impl::default_predicate<TObject> >
class composed_predicate {
public:
	/**
	 * Adds the specified predicate to composed predicate list. When
	 * operator()(TObject object) will be called, logical and will be applied
	 * to these predicates.
	 */
	composed_predicate<TObject, TPredicate1, TPredicate2>&
	and_(const TPredicate1& predicate)
	{
		this->predicates1_.push_back(predicate);
		return *this;
	}

	/**
	 * Adds the specified predicate to composed predicate list. When
	 * operator()(TObject object) will be called, logical and will be applied
	 * to these predicates.
	 */
	composed_predicate<TObject, TPredicate1, TPredicate2>&
	and_(const TPredicate2& predicate)
	{
		this->predicates2_.push_back(predicate);
		return *this;
	}

	bool
	operator()(const TObject& object) const
	{
		return impl::evaluate_predicates(this->predicates1_, object)
			&& impl::evaluate_predicates(this->predicates2_, object);
	}

private:
	std::vector<TPredicate1> predicates1_;
	std::vector<TPredicate2> predicates2_;
};

/**
 * Deduces predicate types from the specified arguments and creates appropriate
 * composed predicate.
 */
template <typename TObject, typename TPredicate1, typename TPredicate2>
composed_predicate<TObject, TPredicate1, TPredicate2>
make_composed_predicate(const TPredicate1& predicate1,
	const TPredicate2& predicate2)
{
	composed_predicate<TObject, TPredicate1, TPredicate2> retval;
	retval.and_(predicate1).and_(predicate2);

	return retval;
}

} // namespace predicate.
} // namespace nonstd.

#endif /* NONSTD_PREDICATE_COMPOSED_PREDICATE_HPP */
<|endoftext|>


<commit_before>#include "graphics/freetypefont.h"

#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif//__GNUC__
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#if defined(__GNUC__) && !defined(__clang__)
//#pragma GCC diagnostic pop
#endif//__GNUC__

static unsigned long ft_stream_read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count)
{
    ResourceStream* stream = static_cast<ResourceStream*>(rec->descriptor.pointer);
    if (stream->seek(offset) == int64_t(offset))
    {
        if (count > 0)
            return stream->read(reinterpret_cast<char*>(buffer), count);
        else
            return 0;
    }
    else
        return count > 0 ? 0 : 1; // error code is 0 if we're reading, or nonzero if we're seeking
}

void ft_stream_close(FT_Stream)
{
}

namespace sp {

FreetypeFont::FreetypeFont(P<ResourceStream> stream)
{
    ft_library = nullptr;
    ft_face = nullptr;

    font_resource_stream = stream;
    LOG(Info, "Loading font...");

    FT_Library library;
    if (FT_Init_FreeType(&library) != 0)
    {
        LOG(Error, "Failed to initialize freetype library");
        return;
    }

    FT_StreamRec* stream_rec = new FT_StreamRec;
    memset(stream_rec, 0, sizeof(FT_StreamRec));
    stream_rec->base = nullptr;
    stream_rec->size = stream->getSize();
    stream_rec->pos = 0;
    stream_rec->descriptor.pointer = *font_resource_stream;
    stream_rec->read = &ft_stream_read;
    stream_rec->close = &ft_stream_close;

    // Setup the FreeType callbacks that will read our stream
    FT_Open_Args args;
    args.flags  = FT_OPEN_STREAM;
    args.stream = stream_rec;
    args.driver = 0;

    // Load the new font face from the specified stream
    FT_Face face;
    if (FT_Open_Face(library, &args, 0, &face) != 0)
    {
        LOG(Error, "Failed to create font from stream...");
        FT_Done_FreeType(library);
        return;
    }

    if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
    {
        LOG(Error, "Failed to select unicode for font...");
        FT_Done_Face(face);
        delete stream_rec;
        FT_Done_FreeType(library);
        return;
    }

    ft_library = library;
    ft_stream_rec = stream_rec;
    ft_face = face;
}

FreetypeFont::~FreetypeFont()
{
    if (ft_face) FT_Done_Face(static_cast<FT_Face>(ft_face));
    if (ft_stream_rec) delete static_cast<FT_StreamRec*>(ft_stream_rec);
    if (ft_library) FT_Done_FreeType(static_cast<FT_Library>(ft_library));
}

Font::CharacterInfo FreetypeFont::getCharacterInfo(const char* str)
{
    Font::CharacterInfo info;
    //TODO: UTF8 support info.code = stringutil::utf8::decodeSingle(str, &info.consumed_bytes);
    info.code = *str;
    info.consumed_bytes = 1;
    return info;
}

bool FreetypeFont::getGlyphInfo(int char_code, int pixel_size, Font::GlyphInfo& info)
{
    std::unordered_map<int, GlyphInfo>& known_glyphs = loaded_glyphs[pixel_size];

    if (known_glyphs.find(char_code) == known_glyphs.end())
    {
        FT_Face face = static_cast<FT_Face>(ft_face);

        info.bounds = Rect({0, 0}, {0, 0});
        info.advance = 0;

        int glyph_index = FT_Get_Char_Index(face, char_code);
        if (glyph_index == 0 || FT_Load_Glyph(face, glyph_index, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0)
        {
            LOG(Warning, "Failed to find glyph in font:", "0x" + string::hex(char_code));
        }
        else
        {
            FT_Glyph glyph;
            if (FT_Get_Glyph(face->glyph, &glyph) == 0)
            {
                info.advance = float(face->glyph->metrics.horiAdvance) / float(1 << 6);
                info.bounds.position.x = float(face->glyph->metrics.horiBearingX) / float(1 << 6);
                info.bounds.position.y = float(face->glyph->metrics.horiBearingY) / float(1 << 6);
                info.bounds.size.x = float(face->glyph->metrics.width) / float(1 << 6);
                info.bounds.size.y = float(face->glyph->metrics.height) / float(1 << 6);

                FT_Done_Glyph(glyph);
            }
        }

        known_glyphs[char_code] = info;
    }
    info = known_glyphs[char_code];
    return true;
}

Image FreetypeFont::drawGlyph(int char_code, int pixel_size)
{
    FT_Face face = static_cast<FT_Face>(ft_face);

    int glyph_index = FT_Get_Char_Index(face, char_code);
    if (glyph_index != 0 && FT_Load_Glyph(face, glyph_index, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) == 0)
    {
        FT_Glyph glyph;
        if (FT_Get_Glyph(face->glyph, &glyph) == 0)
        {
            FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
            FT_Bitmap& bitmap = FT_BitmapGlyph(glyph)->bitmap;

            const uint8_t* src_pixels = bitmap.buffer;
            //We make a full white image, and then copy the alpha from the freetype render
            std::vector<glm::u8vec4> image_pixels;
            image_pixels.resize(bitmap.width * bitmap.rows, {255,255,255,255});
            if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
            {
                assert(false); //TODO
            }
            else
            {
                glm::u8vec4* dst_pixels = image_pixels.data();
                for(unsigned int y=0; y<bitmap.rows; y++)
                {
                    for(unsigned int x=0; x<bitmap.width; x++)
                        dst_pixels[x + y * bitmap.width].a = *src_pixels++;
                    src_pixels += bitmap.pitch - bitmap.width;
                }
            }
            Image image({bitmap.width, bitmap.rows}, std::move(image_pixels));

            FT_Done_Glyph(glyph);

            return image;
        }
    }
    Image image({0, 0});
    return image;
}

float FreetypeFont::getLineSpacing(int pixel_size)
{
    if (static_cast<FT_Face>(ft_face)->size->metrics.x_ppem != pixel_size)
    {
        FT_Set_Pixel_Sizes(static_cast<FT_Face>(ft_face), 0, pixel_size);
    }
    return float(static_cast<FT_Face>(ft_face)->size->metrics.height) / float(1 << 6);
}

float FreetypeFont::getBaseline(int pixel_size)
{
    return getLineSpacing(pixel_size) * 0.6;
}

float FreetypeFont::getKerning(int previous_char_code, int current_char_code)
{
    // Apply the kerning offset
    FT_Face face = static_cast<FT_Face>(ft_face);
    if (FT_HAS_KERNING(face))
    {
        FT_Vector kerning;
        FT_Get_Kerning(face, FT_Get_Char_Index(face, previous_char_code), FT_Get_Char_Index(face, current_char_code), FT_KERNING_DEFAULT, &kerning);
        if (!FT_IS_SCALABLE(face))
            return float(kerning.x);
        else
            return float(kerning.x) / float(1 << 6);
    }
    return 0;
}

}//namespace sp
<commit_msg>Implement utf8 decoding for freetypefont<commit_after>#include "graphics/freetypefont.h"

#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif//__GNUC__
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#if defined(__GNUC__) && !defined(__clang__)
//#pragma GCC diagnostic pop
#endif//__GNUC__

static unsigned long ft_stream_read(FT_Stream rec, unsigned long offset, unsigned char* buffer, unsigned long count)
{
    ResourceStream* stream = static_cast<ResourceStream*>(rec->descriptor.pointer);
    if (stream->seek(offset) == int64_t(offset))
    {
        if (count > 0)
            return stream->read(reinterpret_cast<char*>(buffer), count);
        else
            return 0;
    }
    else
        return count > 0 ? 0 : 1; // error code is 0 if we're reading, or nonzero if we're seeking
}

void ft_stream_close(FT_Stream)
{
}

namespace sp {

FreetypeFont::FreetypeFont(P<ResourceStream> stream)
{
    ft_library = nullptr;
    ft_face = nullptr;

    font_resource_stream = stream;
    LOG(Info, "Loading font...");

    FT_Library library;
    if (FT_Init_FreeType(&library) != 0)
    {
        LOG(Error, "Failed to initialize freetype library");
        return;
    }

    FT_StreamRec* stream_rec = new FT_StreamRec;
    memset(stream_rec, 0, sizeof(FT_StreamRec));
    stream_rec->base = nullptr;
    stream_rec->size = stream->getSize();
    stream_rec->pos = 0;
    stream_rec->descriptor.pointer = *font_resource_stream;
    stream_rec->read = &ft_stream_read;
    stream_rec->close = &ft_stream_close;

    // Setup the FreeType callbacks that will read our stream
    FT_Open_Args args;
    args.flags  = FT_OPEN_STREAM;
    args.stream = stream_rec;
    args.driver = 0;

    // Load the new font face from the specified stream
    FT_Face face;
    if (FT_Open_Face(library, &args, 0, &face) != 0)
    {
        LOG(Error, "Failed to create font from stream...");
        FT_Done_FreeType(library);
        return;
    }

    if (FT_Select_Charmap(face, FT_ENCODING_UNICODE) != 0)
    {
        LOG(Error, "Failed to select unicode for font...");
        FT_Done_Face(face);
        delete stream_rec;
        FT_Done_FreeType(library);
        return;
    }

    ft_library = library;
    ft_stream_rec = stream_rec;
    ft_face = face;
}

FreetypeFont::~FreetypeFont()
{
    if (ft_face) FT_Done_Face(static_cast<FT_Face>(ft_face));
    if (ft_stream_rec) delete static_cast<FT_StreamRec*>(ft_stream_rec);
    if (ft_library) FT_Done_FreeType(static_cast<FT_Library>(ft_library));
}

Font::CharacterInfo FreetypeFont::getCharacterInfo(const char* str)
{
    Font::CharacterInfo info;
    info.code = *str;
    info.consumed_bytes = 1;
    if ((str[0] & 0xe0) == 0xc0)
    {
        if ((str[1] & 0xc0) == 0x80)
        {
            info.consumed_bytes = 2;
            info.code = ((str[0] & 0x1f) << 6) | (str[1] & 0x3f);
        }
    }
    else if ((str[0] & 0xf0) == 0xe0)
    {
        if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80)
        {
            info.consumed_bytes = 3;
            info.code = ((str[0] & 0x0f) << 12) | ((str[1] & 0x3f) << 6) | (str[2] & 0x3f);
        }
    }
    else if ((str[0] & 0xf8) == 0xf0)
    {
        if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 && (str[3] & 0xc0) == 0x80)
        {
            info.consumed_bytes = 4;
            info.code = ((str[0] & 0x0f) << 18) | ((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) | (str[3] & 0x3f);
        }
    }
    return info;
}

bool FreetypeFont::getGlyphInfo(int char_code, int pixel_size, Font::GlyphInfo& info)
{
    std::unordered_map<int, GlyphInfo>& known_glyphs = loaded_glyphs[pixel_size];

    if (known_glyphs.find(char_code) == known_glyphs.end())
    {
        FT_Face face = static_cast<FT_Face>(ft_face);

        info.bounds = Rect({0, 0}, {0, 0});
        info.advance = 0;

        int glyph_index = FT_Get_Char_Index(face, char_code);
        if (glyph_index == 0 || FT_Load_Glyph(face, glyph_index, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) != 0)
        {
            LOG(Warning, "Failed to find glyph in font:", "0x" + string::hex(char_code));
        }
        else
        {
            FT_Glyph glyph;
            if (FT_Get_Glyph(face->glyph, &glyph) == 0)
            {
                info.advance = float(face->glyph->metrics.horiAdvance) / float(1 << 6);
                info.bounds.position.x = float(face->glyph->metrics.horiBearingX) / float(1 << 6);
                info.bounds.position.y = float(face->glyph->metrics.horiBearingY) / float(1 << 6);
                info.bounds.size.x = float(face->glyph->metrics.width) / float(1 << 6);
                info.bounds.size.y = float(face->glyph->metrics.height) / float(1 << 6);

                FT_Done_Glyph(glyph);
            }
        }

        known_glyphs[char_code] = info;
    }
    info = known_glyphs[char_code];
    return true;
}

Image FreetypeFont::drawGlyph(int char_code, int pixel_size)
{
    FT_Face face = static_cast<FT_Face>(ft_face);

    int glyph_index = FT_Get_Char_Index(face, char_code);
    if (glyph_index != 0 && FT_Load_Glyph(face, glyph_index, FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT) == 0)
    {
        FT_Glyph glyph;
        if (FT_Get_Glyph(face->glyph, &glyph) == 0)
        {
            FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
            FT_Bitmap& bitmap = FT_BitmapGlyph(glyph)->bitmap;

            const uint8_t* src_pixels = bitmap.buffer;
            //We make a full white image, and then copy the alpha from the freetype render
            std::vector<glm::u8vec4> image_pixels;
            image_pixels.resize(bitmap.width * bitmap.rows, {255,255,255,255});
            if (bitmap.pixel_mode == FT_PIXEL_MODE_MONO)
            {
                assert(false); //TODO
            }
            else
            {
                glm::u8vec4* dst_pixels = image_pixels.data();
                for(unsigned int y=0; y<bitmap.rows; y++)
                {
                    for(unsigned int x=0; x<bitmap.width; x++)
                        dst_pixels[x + y * bitmap.width].a = *src_pixels++;
                    src_pixels += bitmap.pitch - bitmap.width;
                }
            }
            Image image({bitmap.width, bitmap.rows}, std::move(image_pixels));

            FT_Done_Glyph(glyph);

            return image;
        }
    }
    Image image({0, 0});
    return image;
}

float FreetypeFont::getLineSpacing(int pixel_size)
{
    if (static_cast<FT_Face>(ft_face)->size->metrics.x_ppem != pixel_size)
    {
        FT_Set_Pixel_Sizes(static_cast<FT_Face>(ft_face), 0, pixel_size);
    }
    return float(static_cast<FT_Face>(ft_face)->size->metrics.height) / float(1 << 6);
}

float FreetypeFont::getBaseline(int pixel_size)
{
    return getLineSpacing(pixel_size) * 0.6;
}

float FreetypeFont::getKerning(int previous_char_code, int current_char_code)
{
    // Apply the kerning offset
    FT_Face face = static_cast<FT_Face>(ft_face);
    if (FT_HAS_KERNING(face))
    {
        FT_Vector kerning;
        FT_Get_Kerning(face, FT_Get_Char_Index(face, previous_char_code), FT_Get_Char_Index(face, current_char_code), FT_KERNING_DEFAULT, &kerning);
        if (!FT_IS_SCALABLE(face))
            return float(kerning.x);
        else
            return float(kerning.x) / float(1 << 6);
    }
    return 0;
}

}//namespace sp
<|endoftext|>


<commit_before>// Filename: auto_bind.cxx
// Created by:  drose (23Feb99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "auto_bind.h"
#include "animBundleNode.h"
#include "partBundleNode.h"
#include "config_chan.h"
#include "string_utils.h"
#include "partGroup.h"

typedef pset<AnimBundleNode *> AnimNodes;
typedef pmap<string, AnimNodes> Anims;

typedef pset<PartBundleNode *> PartNodes;
typedef pmap<string, PartNodes> Parts;

////////////////////////////////////////////////////////////////////
//     Function: bind_anims
//  Description: A support function for auto_bind(), below.  Given a
//               set of AnimBundles and a set of PartBundles that all
//               share the same name, perform whatever bindings make
//               sense.
////////////////////////////////////////////////////////////////////
static void
bind_anims(const PartNodes &parts, const AnimNodes &anims,
           AnimControlCollection &controls,
           int hierarchy_match_flags) {
  if (chan_cat.is_debug()) {
    chan_cat.debug() << "bind_anims\n";
  }
  PartNodes::const_iterator pni;

  for (pni = parts.begin(); pni != parts.end(); ++pni) {
    PartBundle *part = (*pni)->get_bundle();
    if (chan_cat.is_debug()) {
      chan_cat.debug() << "got part " << *part << "\n";
    }
    AnimNodes::const_iterator ani;
    for (ani = anims.begin(); ani != anims.end(); ++ani) {
      AnimBundle *anim = (*ani)->get_bundle();
      if (chan_cat.is_debug()) {
        chan_cat.debug() << "got anim " << *anim << "\n";
      }
      if (chan_cat.is_info()) {
        chan_cat.info()
          << "Attempting to bind " << *part << " to " << *anim << "\n";
      }

      PT(AnimControl) control =
        part->bind_anim(anim, hierarchy_match_flags);
      string name = (*ani)->get_name();
      if (name.empty()) {
        name = anim->get_name();
      }
      if (control != (AnimControl *)NULL) {
        if (controls.find_anim(name) != (AnimControl *)NULL) {
          // That name's already used; synthesize another one.
          int index = 0;
          string new_name;
          do {
            index++;
            new_name = name + '.' + format_string(index);
          } while (controls.find_anim(new_name) != (AnimControl *)NULL);
          name = new_name;
        }

        controls.store_anim(control, name);
      }

      if (chan_cat.is_info()) {
        if (control == (AnimControl *)NULL) {
          chan_cat.info()
            << "Bind failed.\n";
        } else {
          chan_cat.info()
            << "Bind succeeded, index "
            << control->get_channel_index() << "; accessible as "
            << name << "\n";
        }
      }
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: r_find_bundles
//  Description: A support function for auto_bind(), below.  Walks
//               through the hierarchy and finds all of the
//               PartBundles and AnimBundles.
////////////////////////////////////////////////////////////////////
static void
r_find_bundles(PandaNode *node, Anims &anims, Parts &parts) {
  if (node->is_of_type(AnimBundleNode::get_class_type())) {
    AnimBundleNode *bn = DCAST(AnimBundleNode, node);
    anims[bn->get_bundle()->get_name()].insert(bn);
  } else if (node->is_of_type(PartBundleNode::get_class_type())) {
    PartBundleNode *bn = DCAST(PartBundleNode, node);
    parts[bn->get_bundle()->get_name()].insert(bn);
  }

  PandaNode::Children cr = node->get_children();
  int num_children = cr.get_num_children();
  for (int i = 0; i < num_children; i++) {
    r_find_bundles(cr.get_child(i), anims, parts);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: auto_bind
//  Description: Walks the scene graph or subgraph beginning at the
//               indicated node, and attempts to bind any AnimBundles
//               found to their matching PartBundles, when possible.
//
//               The list of all resulting AnimControls created is
//               filled into controls.
////////////////////////////////////////////////////////////////////
void
auto_bind(PandaNode *root_node, AnimControlCollection &controls,
          int hierarchy_match_flags) {
  // First, locate all the bundles in the subgraph.
  Anims anims; AnimNodes extraAnims;
  Parts parts; PartNodes extraParts;
  r_find_bundles(root_node, anims, parts);

  if (chan_cat.is_debug()) {
    int anim_count = 0;
    Anims::const_iterator ai;
    for (ai = anims.begin(); ai != anims.end(); ++ai) {
      anim_count += (int)(*ai).second.size();
    }
    chan_cat.debug()
      << "Found " << anim_count << " anims:\n";
    for (ai = anims.begin(); ai != anims.end(); ++ai) {
      chan_cat.debug(false)
        << " " << (*ai).first;
      if ((*ai).second.size() != 1) {
        chan_cat.debug(false)
          << "*" << ((*ai).second.size());
      }
    }
    chan_cat.debug(false)
      << "\n";

    int part_count = 0;
    Parts::const_iterator pi;
    for (pi = parts.begin(); pi != parts.end(); ++pi) {
      part_count += (int)(*pi).second.size();
    }
    chan_cat.debug()
      << "Found " << part_count << " parts:\n";
    for (pi = parts.begin(); pi != parts.end(); ++pi) {
      chan_cat.debug(false)
        << " " << (*pi).first;
      if ((*pi).second.size() != 1) {
        chan_cat.debug(false)
          << "*" << ((*pi).second.size());
      }
    }
    chan_cat.debug(false)
      << "\n";
  }

  // Now, match up the bundles by name.

  Anims::const_iterator ai = anims.begin();
  Parts::const_iterator pi = parts.begin();

  while (ai != anims.end() && pi != parts.end()) {
    if ((*ai).first < (*pi).first) {
      // Here's an anim with no matching parts.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
        AnimNodes::const_iterator ani;
        for (ani = (*ai).second.begin(); ani != (*ai).second.end(); ++ani)
          extraAnims.insert(*ani);
      }
      ++ai;

    } else if ((*pi).first < (*ai).first) {
      // And here's a part with no matching anims.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
        PartNodes::const_iterator pni;
        for (pni = (*pi).second.begin(); pni != (*pi).second.end(); ++pni)
          extraParts.insert(*pni);
      }
      ++pi;

    } else {
      // But here we have (at least one) match!
      bind_anims((*pi).second, (*ai).second, controls,
                   hierarchy_match_flags);
      ++pi;

      // We don't increment the anim counter yet.  That way, the same
      // anim may bind to multiple parts, if they all share the same
      // name.
    }
  }

  if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
    // Continue searching through the remaining anims and parts.

    while (ai != anims.end()) {
      // Here's an anim with no matching parts.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
	AnimNodes::const_iterator ani;
	for (ani = (*ai).second.begin(); ani != (*ai).second.end(); ++ani)
	  extraAnims.insert(*ani);
      }
      ++ai;
    }

    while (pi != parts.end()) {
      // And here's a part with no matching anims.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
	PartNodes::const_iterator pni;
	for (pni = (*pi).second.begin(); pni != (*pi).second.end(); ++pni)
	  extraParts.insert(*pni);
      }
      ++pi;
    }

    bind_anims(extraParts, extraAnims, controls,
               hierarchy_match_flags);
  }
}

<commit_msg>remove accidental debug messages<commit_after>// Filename: auto_bind.cxx
// Created by:  drose (23Feb99)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "auto_bind.h"
#include "animBundleNode.h"
#include "partBundleNode.h"
#include "config_chan.h"
#include "string_utils.h"
#include "partGroup.h"

typedef pset<AnimBundleNode *> AnimNodes;
typedef pmap<string, AnimNodes> Anims;

typedef pset<PartBundleNode *> PartNodes;
typedef pmap<string, PartNodes> Parts;

////////////////////////////////////////////////////////////////////
//     Function: bind_anims
//  Description: A support function for auto_bind(), below.  Given a
//               set of AnimBundles and a set of PartBundles that all
//               share the same name, perform whatever bindings make
//               sense.
////////////////////////////////////////////////////////////////////
static void
bind_anims(const PartNodes &parts, const AnimNodes &anims,
           AnimControlCollection &controls,
           int hierarchy_match_flags) {
  PartNodes::const_iterator pni;

  for (pni = parts.begin(); pni != parts.end(); ++pni) {
    PartBundle *part = (*pni)->get_bundle();
    AnimNodes::const_iterator ani;
    for (ani = anims.begin(); ani != anims.end(); ++ani) {
      AnimBundle *anim = (*ani)->get_bundle();
      if (chan_cat.is_info()) {
        chan_cat.info()
          << "Attempting to bind " << *part << " to " << *anim << "\n";
      }

      PT(AnimControl) control =
        part->bind_anim(anim, hierarchy_match_flags);
      string name = (*ani)->get_name();
      if (name.empty()) {
        name = anim->get_name();
      }
      if (control != (AnimControl *)NULL) {
        if (controls.find_anim(name) != (AnimControl *)NULL) {
          // That name's already used; synthesize another one.
          int index = 0;
          string new_name;
          do {
            index++;
            new_name = name + '.' + format_string(index);
          } while (controls.find_anim(new_name) != (AnimControl *)NULL);
          name = new_name;
        }

        controls.store_anim(control, name);
      }

      if (chan_cat.is_info()) {
        if (control == (AnimControl *)NULL) {
          chan_cat.info()
            << "Bind failed.\n";
        } else {
          chan_cat.info()
            << "Bind succeeded, index "
            << control->get_channel_index() << "; accessible as "
            << name << "\n";
        }
      }
    }
  }
}

////////////////////////////////////////////////////////////////////
//     Function: r_find_bundles
//  Description: A support function for auto_bind(), below.  Walks
//               through the hierarchy and finds all of the
//               PartBundles and AnimBundles.
////////////////////////////////////////////////////////////////////
static void
r_find_bundles(PandaNode *node, Anims &anims, Parts &parts) {
  if (node->is_of_type(AnimBundleNode::get_class_type())) {
    AnimBundleNode *bn = DCAST(AnimBundleNode, node);
    anims[bn->get_bundle()->get_name()].insert(bn);
  } else if (node->is_of_type(PartBundleNode::get_class_type())) {
    PartBundleNode *bn = DCAST(PartBundleNode, node);
    parts[bn->get_bundle()->get_name()].insert(bn);
  }

  PandaNode::Children cr = node->get_children();
  int num_children = cr.get_num_children();
  for (int i = 0; i < num_children; i++) {
    r_find_bundles(cr.get_child(i), anims, parts);
  }
}

////////////////////////////////////////////////////////////////////
//     Function: auto_bind
//  Description: Walks the scene graph or subgraph beginning at the
//               indicated node, and attempts to bind any AnimBundles
//               found to their matching PartBundles, when possible.
//
//               The list of all resulting AnimControls created is
//               filled into controls.
////////////////////////////////////////////////////////////////////
void
auto_bind(PandaNode *root_node, AnimControlCollection &controls,
          int hierarchy_match_flags) {
  // First, locate all the bundles in the subgraph.
  Anims anims; AnimNodes extraAnims;
  Parts parts; PartNodes extraParts;
  r_find_bundles(root_node, anims, parts);

  if (chan_cat.is_debug()) {
    int anim_count = 0;
    Anims::const_iterator ai;
    for (ai = anims.begin(); ai != anims.end(); ++ai) {
      anim_count += (int)(*ai).second.size();
    }
    chan_cat.debug()
      << "Found " << anim_count << " anims:\n";
    for (ai = anims.begin(); ai != anims.end(); ++ai) {
      chan_cat.debug(false)
        << " " << (*ai).first;
      if ((*ai).second.size() != 1) {
        chan_cat.debug(false)
          << "*" << ((*ai).second.size());
      }
    }
    chan_cat.debug(false)
      << "\n";

    int part_count = 0;
    Parts::const_iterator pi;
    for (pi = parts.begin(); pi != parts.end(); ++pi) {
      part_count += (int)(*pi).second.size();
    }
    chan_cat.debug()
      << "Found " << part_count << " parts:\n";
    for (pi = parts.begin(); pi != parts.end(); ++pi) {
      chan_cat.debug(false)
        << " " << (*pi).first;
      if ((*pi).second.size() != 1) {
        chan_cat.debug(false)
          << "*" << ((*pi).second.size());
      }
    }
    chan_cat.debug(false)
      << "\n";
  }

  // Now, match up the bundles by name.

  Anims::const_iterator ai = anims.begin();
  Parts::const_iterator pi = parts.begin();

  while (ai != anims.end() && pi != parts.end()) {
    if ((*ai).first < (*pi).first) {
      // Here's an anim with no matching parts.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
        AnimNodes::const_iterator ani;
        for (ani = (*ai).second.begin(); ani != (*ai).second.end(); ++ani)
          extraAnims.insert(*ani);
      }
      ++ai;

    } else if ((*pi).first < (*ai).first) {
      // And here's a part with no matching anims.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
        PartNodes::const_iterator pni;
        for (pni = (*pi).second.begin(); pni != (*pi).second.end(); ++pni)
          extraParts.insert(*pni);
      }
      ++pi;

    } else {
      // But here we have (at least one) match!
      bind_anims((*pi).second, (*ai).second, controls,
                   hierarchy_match_flags);
      ++pi;

      // We don't increment the anim counter yet.  That way, the same
      // anim may bind to multiple parts, if they all share the same
      // name.
    }
  }

  if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
    // Continue searching through the remaining anims and parts.

    while (ai != anims.end()) {
      // Here's an anim with no matching parts.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
	AnimNodes::const_iterator ani;
	for (ani = (*ai).second.begin(); ani != (*ai).second.end(); ++ani)
	  extraAnims.insert(*ani);
      }
      ++ai;
    }

    while (pi != parts.end()) {
      // And here's a part with no matching anims.
      if (hierarchy_match_flags & PartGroup::HMF_ok_wrong_root_name) {
	PartNodes::const_iterator pni;
	for (pni = (*pi).second.begin(); pni != (*pi).second.end(); ++pni)
	  extraParts.insert(*pni);
      }
      ++pi;
    }

    bind_anims(extraParts, extraAnims, controls,
               hierarchy_match_flags);
  }
}

<|endoftext|>


<commit_before>// Copyright (c) 2011, Cornell University
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of HyperDex nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// POSIX
#include <poll.h>

// Configuration
#include <configuration/coordinatorlink.h>

hyperdex :: coordinatorlink :: coordinatorlink(const po6::net::location& coordinator,
                                               const std::string& announce)
    : m_coordinator(coordinator)
    , m_announce(announce + "\n")
    , m_shutdown(false)
    , m_acknowledged(true)
    , m_config_valid(true)
    , m_config()
    , m_sock()
    , m_pfd()
    , m_buffer()
{
    reset();
}

hyperdex :: coordinatorlink :: ~coordinatorlink() throw ()
{
    shutdown();
}

bool
hyperdex :: coordinatorlink :: unacknowledged() const
{
    return !m_acknowledged;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: acknowledge()
{
    if (m_shutdown)
    {
        return SHUTDOWN;
    }

    returncode ret = send_to_coordinator("ACK\n", 4);

    if (ret == SUCCESS)
    {
        reset_config();
    }

    return ret;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: connect()
{
    if (m_sock.get() >= 0)
    {
        return SUCCESS;
    }

    reset_config();
    m_sock.reset(m_coordinator.address.family(), SOCK_STREAM, IPPROTO_TCP);

    try
    {
        m_sock.connect(m_coordinator);
        return send_to_coordinator(m_announce.c_str(), m_announce.size());
    }
    catch (po6::error& e)
    {
        return CONNECTFAIL;
    }
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: loop(size_t iterations, int timeout)
{
    size_t iter = 0;

    while (m_acknowledged && iter < iterations)
    {
        if (m_shutdown)
        {
            return SHUTDOWN;
        }

        if (m_sock.get() < 0)
        {
            reset();
            return DISCONNECT;
        }

        int polled = poll(&m_pfd, 1, timeout);

        if (polled < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                reset();
                return DISCONNECT;
            }
        }

        size_t ret;

        try
        {
            ret = e::read(&m_sock, &m_buffer, 2048);
        }
        catch (po6::error& e)
        {
            reset();
            return DISCONNECT;
        }

        if (ret == 0)
        {
            reset();
            return DISCONNECT;
        }

        size_t index;

        while ((index = m_buffer.index('\n')) < m_buffer.size())
        {
            std::string line(static_cast<const char*>(m_buffer.get()), index);
            m_buffer.trim_prefix(index + 1);

            if (line == "end\tof\tline")
            {
                if (m_config_valid)
                {
                    m_acknowledged = false;
                    return SUCCESS;
                }
                else
                {
                    reset_config();
                    returncode code = send_to_coordinator("BAD\n", 4);

                    if (code != SUCCESS)
                    {
                        return code;
                    }
                }
            }
            else
            {
                m_config_valid &= m_config.add_line(line);
            }
        }
    }

    return SUCCESS;
}

void
hyperdex :: coordinatorlink :: shutdown()
{
    if (!m_shutdown)
    {
        m_shutdown = true;
    }
}

const hyperdex::configuration&
hyperdex :: coordinatorlink :: config() const
{
    return m_config;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: send_to_coordinator(const char* msg, size_t len)
{
    try
    {
        if (m_sock.xwrite(msg, len) != len)
        {
            reset();
            return DISCONNECT;
        }
    }
    catch (po6::error& e)
    {
        reset();
        return DISCONNECT;
    }

    return SUCCESS;
}

void
hyperdex :: coordinatorlink :: reset()
{
    reset_config();
    m_sock.close();
    m_pfd.fd = -1;
    m_pfd.events = 0;
    m_buffer.clear();
}

void
hyperdex :: coordinatorlink :: reset_config()
{
    m_acknowledged = true;
    m_config_valid = true;
    m_config = configuration();
}
<commit_msg>Set the coordinatorlink pollfd on new connection.<commit_after>// Copyright (c) 2011, Cornell University
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of HyperDex nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// POSIX
#include <poll.h>

// Configuration
#include <configuration/coordinatorlink.h>

hyperdex :: coordinatorlink :: coordinatorlink(const po6::net::location& coordinator,
                                               const std::string& announce)
    : m_coordinator(coordinator)
    , m_announce(announce + "\n")
    , m_shutdown(false)
    , m_acknowledged(true)
    , m_config_valid(true)
    , m_config()
    , m_sock()
    , m_pfd()
    , m_buffer()
{
    reset();
}

hyperdex :: coordinatorlink :: ~coordinatorlink() throw ()
{
    shutdown();
}

bool
hyperdex :: coordinatorlink :: unacknowledged() const
{
    return !m_acknowledged;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: acknowledge()
{
    if (m_shutdown)
    {
        return SHUTDOWN;
    }

    returncode ret = send_to_coordinator("ACK\n", 4);

    if (ret == SUCCESS)
    {
        reset_config();
    }

    return ret;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: connect()
{
    if (m_sock.get() >= 0)
    {
        return SUCCESS;
    }

    reset_config();
    m_sock.reset(m_coordinator.address.family(), SOCK_STREAM, IPPROTO_TCP);

    try
    {
        m_sock.connect(m_coordinator);
        m_pfd.fd = m_sock.get();
        m_pfd.events = POLLIN;
        return send_to_coordinator(m_announce.c_str(), m_announce.size());
    }
    catch (po6::error& e)
    {
        return CONNECTFAIL;
    }
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: loop(size_t iterations, int timeout)
{
    size_t iter = 0;

    while (m_acknowledged && iter < iterations)
    {
        if (m_shutdown)
        {
            return SHUTDOWN;
        }

        if (m_sock.get() < 0)
        {
            reset();
            return DISCONNECT;
        }

        int polled = poll(&m_pfd, 1, timeout);

        if (polled < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                reset();
                return DISCONNECT;
            }
        }

        size_t ret;

        try
        {
            ret = e::read(&m_sock, &m_buffer, 2048);
        }
        catch (po6::error& e)
        {
            reset();
            return DISCONNECT;
        }

        if (ret == 0)
        {
            reset();
            return DISCONNECT;
        }

        size_t index;

        while ((index = m_buffer.index('\n')) < m_buffer.size())
        {
            std::string line(static_cast<const char*>(m_buffer.get()), index);
            m_buffer.trim_prefix(index + 1);

            if (line == "end\tof\tline")
            {
                if (m_config_valid)
                {
                    m_acknowledged = false;
                    return SUCCESS;
                }
                else
                {
                    reset_config();
                    returncode code = send_to_coordinator("BAD\n", 4);

                    if (code != SUCCESS)
                    {
                        return code;
                    }
                }
            }
            else
            {
                m_config_valid &= m_config.add_line(line);
            }
        }
    }

    return SUCCESS;
}

void
hyperdex :: coordinatorlink :: shutdown()
{
    if (!m_shutdown)
    {
        m_shutdown = true;
    }
}

const hyperdex::configuration&
hyperdex :: coordinatorlink :: config() const
{
    return m_config;
}

hyperdex::coordinatorlink::returncode
hyperdex :: coordinatorlink :: send_to_coordinator(const char* msg, size_t len)
{
    try
    {
        if (m_sock.xwrite(msg, len) != len)
        {
            reset();
            return DISCONNECT;
        }
    }
    catch (po6::error& e)
    {
        reset();
        return DISCONNECT;
    }

    return SUCCESS;
}

void
hyperdex :: coordinatorlink :: reset()
{
    reset_config();
    m_sock.close();
    m_pfd.fd = -1;
    m_pfd.events = 0;
    m_buffer.clear();
}

void
hyperdex :: coordinatorlink :: reset_config()
{
    m_acknowledged = true;
    m_config_valid = true;
    m_config = configuration();
}
<|endoftext|>


<commit_before>#include "sstream.hpp"
#include "c4/string.hpp"

#include "c4/archetypes.hpp"
#include <iostream>

C4_BEGIN_NAMESPACE(c4)

template< class T >
struct exvec3
{
    T x, y, z;
    bool operator== (exvec3 const& that) const
    {
        return x == that.x && y == that.y && z == that.z;
    }
};
template< class String, class T >
sstream< String >& operator<< (sstream< String >& ss, exvec3<T> const& v)
{
    using char_type = typename sstream< String >::char_type;
    ss.printp(C4_TXTTY("({},{},{})", char_type), v.x, v.y, v.z);
    return ss;
}
template< class String, class T >
sstream< String >& operator>> (sstream< String >& ss, exvec3<T> & v)
{
    using char_type = typename sstream< String >::char_type;
    ss.scanp(C4_TXTTY("({},{},{})", char_type), v.x, v.y, v.z);
    return ss;
}

//-----------------------------------------------------------------------------
template <class T>
struct RoundTripTest_c4string : public ::testing::Test
{
    sstream< c4::string > ss;
};
template <class T>
struct RoundTripTest_c4wstring : public ::testing::Test
{
    sstream< c4::wstring > ss;
};
template <class T>
struct RoundTripTest_c4substring : public ::testing::Test
{
    c4::string str;
    sstream< c4::substring > ss;
    RoundTripTest_c4substring() : str(512), ss(str.data(), str.size())
    {
    }
};
template <class T>
struct RoundTripTest_c4wsubstring : public ::testing::Test
{
    c4::wstring str;
    sstream< c4::wsubstring > ss;
    RoundTripTest_c4wsubstring() : str(512), ss(str.data(), str.size())
    {
    }
};
template <class T>
struct RoundTripTest_stdstring : public ::testing::Test
{
    sstream< std::string > ss;
};
template <class T>
struct RoundTripTest_stdwstring : public ::testing::Test
{
    sstream< std::wstring > ss;
};
TYPED_TEST_CASE_P(RoundTripTest_c4string);
TYPED_TEST_CASE_P(RoundTripTest_c4wstring);
TYPED_TEST_CASE_P(RoundTripTest_c4substring);
TYPED_TEST_CASE_P(RoundTripTest_c4wsubstring);
TYPED_TEST_CASE_P(RoundTripTest_stdstring);
TYPED_TEST_CASE_P(RoundTripTest_stdwstring);

//-----------------------------------------------------------------------------
#define _testrtrip3(which, strtype)                                 \
                                                                    \
    which<strtype, TypeParam>(this->ss, 65, 66, 67);                \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, TypeParam>(this->ss, 97, 98, 99);                \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, TypeParam>(this->ss, 123, 124, 125);             \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, exvec3<TypeParam>>(this->ss, {65,66,67},{68,69,70},{71,72,73}); \
    this->ss.reset();

//-----------------------------------------------------------------------------

template< class String, class T >
void do_round_trip_chevron(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss << val1 << char_type(' ') << val2 << char_type(' ') << val3;
    char_type c;
    ss >> v1 >> c >> v2 >> c >> v3;
    EXPECT_EQ(v1, val1) << C4_PRETTY_FUNC;
    EXPECT_EQ(v2, val2) << C4_PRETTY_FUNC;
    EXPECT_EQ(v3, val3) << C4_PRETTY_FUNC;
}
TYPED_TEST_P(RoundTripTest_c4string, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_printp(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;

    T v1, v2, v3;
    ss.printp(C4_TXTTY("{} {} {}", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} {} {}", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);

    ss.reset(); v1 = v2 = v3 = {};
    ss.printp(C4_TXTTY("{} aaaaaaaaa {} bbbb {} ccc", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} aaaaaaaaa {} bbbb {} ccc", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);

    ss.reset(); v1 = v2 = v3 = {};
    ss.printp(C4_TXTTY("{} aaaaaaaaa_{} bbbb_{} ccc", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} aaaaaaaaa_{} bbbb_{} ccc", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, printp)
{
    _testrtrip3(do_round_trip_printp, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, printp)
{
    _testrtrip3(do_round_trip_printp, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, printp)
{
    _testrtrip3(do_round_trip_printp, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_cat(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss.cat(val1, char_type(' '), val2, char_type(' '), val3);
    char_type c;
    ss.uncat(v1, c, v2, c, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, cat)
{
    _testrtrip3(do_round_trip_cat, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, cat)
{
    _testrtrip3(do_round_trip_cat, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, cat)
{
    _testrtrip3(do_round_trip_cat, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_catsep(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss.catsep(char_type(' '), val1, val2, val3);
    ss.uncatsep(char_type(' '), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, std::wstring);
}

REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4string,     chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4wstring,    chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4substring,  chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4wsubstring, chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_stdstring,    chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_stdwstring,   chevron, printp, cat, catsep);

INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4string,     c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4wstring,    c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4substring,  c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4wsubstring, c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_stdstring,    c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_stdwstring,   c4::archetypes::scalars);

C4_END_NAMESPACE(c4)
<commit_msg>moved exvec3<T> to the archetype tests<commit_after>#include "sstream.hpp"
#include "c4/string.hpp"

#include "c4/test.hpp"
#include "c4/archetypes.hpp"
#include <iostream>

C4_BEGIN_NAMESPACE(c4)

//-----------------------------------------------------------------------------
template <class T>
struct RoundTripTest_c4string : public ::testing::Test
{
    sstream< c4::string > ss;
};
template <class T>
struct RoundTripTest_c4wstring : public ::testing::Test
{
    sstream< c4::wstring > ss;
};
template <class T>
struct RoundTripTest_c4substring : public ::testing::Test
{
    c4::string str;
    sstream< c4::substring > ss;
    RoundTripTest_c4substring() : str(512), ss(str.data(), str.size())
    {
    }
};
template <class T>
struct RoundTripTest_c4wsubstring : public ::testing::Test
{
    c4::wstring str;
    sstream< c4::wsubstring > ss;
    RoundTripTest_c4wsubstring() : str(512), ss(str.data(), str.size())
    {
    }
};
template <class T>
struct RoundTripTest_stdstring : public ::testing::Test
{
    sstream< std::string > ss;
};
template <class T>
struct RoundTripTest_stdwstring : public ::testing::Test
{
    sstream< std::wstring > ss;
};
TYPED_TEST_CASE_P(RoundTripTest_c4string);
TYPED_TEST_CASE_P(RoundTripTest_c4wstring);
TYPED_TEST_CASE_P(RoundTripTest_c4substring);
TYPED_TEST_CASE_P(RoundTripTest_c4wsubstring);
TYPED_TEST_CASE_P(RoundTripTest_stdstring);
TYPED_TEST_CASE_P(RoundTripTest_stdwstring);

//-----------------------------------------------------------------------------
#define _testrtrip3(which, strtype)                                 \
                                                                    \
    which<strtype, TypeParam>(this->ss, 65, 66, 67);                \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, TypeParam>(this->ss, 97, 98, 99);                \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, TypeParam>(this->ss, 123, 124, 125);             \
    this->ss.reset();                                               \
                                                                    \
    which<strtype, exvec3<TypeParam>>(this->ss, {65,66,67},{68,69,70},{71,72,73}); \
    this->ss.reset();

//-----------------------------------------------------------------------------

template< class String, class T >
void do_round_trip_chevron(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss << val1 << char_type(' ') << val2 << char_type(' ') << val3;
    char_type c;
    ss >> v1 >> c >> v2 >> c >> v3;
    EXPECT_EQ(v1, val1) << C4_PRETTY_FUNC;
    EXPECT_EQ(v2, val2) << C4_PRETTY_FUNC;
    EXPECT_EQ(v3, val3) << C4_PRETTY_FUNC;
}
TYPED_TEST_P(RoundTripTest_c4string, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, chevron)
{
    _testrtrip3(do_round_trip_chevron, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_printp(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;

    T v1, v2, v3;
    ss.printp(C4_TXTTY("{} {} {}", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} {} {}", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);

    ss.reset(); v1 = v2 = v3 = {};
    ss.printp(C4_TXTTY("{} aaaaaaaaa {} bbbb {} ccc", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} aaaaaaaaa {} bbbb {} ccc", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);

    ss.reset(); v1 = v2 = v3 = {};
    ss.printp(C4_TXTTY("{} aaaaaaaaa_{} bbbb_{} ccc", char_type), val1, val2, val3);
    ss.scanp(C4_TXTTY("{} aaaaaaaaa_{} bbbb_{} ccc", char_type), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, printp)
{
    _testrtrip3(do_round_trip_printp, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, printp)
{
    _testrtrip3(do_round_trip_printp, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, printp)
{
    _testrtrip3(do_round_trip_printp, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, printp)
{
    _testrtrip3(do_round_trip_printp, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_cat(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss.cat(val1, char_type(' '), val2, char_type(' '), val3);
    char_type c;
    ss.uncat(v1, c, v2, c, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, cat)
{
    _testrtrip3(do_round_trip_cat, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, cat)
{
    _testrtrip3(do_round_trip_cat, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, cat)
{
    _testrtrip3(do_round_trip_cat, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, cat)
{
    _testrtrip3(do_round_trip_cat, std::wstring);
}

//-----------------------------------------------------------------------------
template< class String, class T >
void do_round_trip_catsep(sstream<String> &ss, T const& val1, T const& val2, T const& val3)
{
    using char_type = typename sstream< String >::char_type;
    T v1, v2, v3;
    ss.catsep(char_type(' '), val1, val2, val3);
    ss.uncatsep(char_type(' '), v1, v2, v3);
    EXPECT_EQ(v1, val1);
    EXPECT_EQ(v2, val2);
    EXPECT_EQ(v3, val3);
}
TYPED_TEST_P(RoundTripTest_c4string, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::string);
}
TYPED_TEST_P(RoundTripTest_c4wstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::wstring);
}
TYPED_TEST_P(RoundTripTest_c4substring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::substring);
}
TYPED_TEST_P(RoundTripTest_c4wsubstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, c4::wsubstring);
}
TYPED_TEST_P(RoundTripTest_stdstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, std::string);
}
TYPED_TEST_P(RoundTripTest_stdwstring, catsep)
{
    _testrtrip3(do_round_trip_catsep, std::wstring);
}

REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4string,     chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4wstring,    chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4substring,  chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_c4wsubstring, chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_stdstring,    chevron, printp, cat, catsep);
REGISTER_TYPED_TEST_CASE_P(RoundTripTest_stdwstring,   chevron, printp, cat, catsep);

INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4string,     c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4wstring,    c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4substring,  c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_c4wsubstring, c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_stdstring,    c4::archetypes::scalars);
INSTANTIATE_TYPED_TEST_CASE_P(sstream, RoundTripTest_stdwstring,   c4::archetypes::scalars);

C4_END_NAMESPACE(c4)
<|endoftext|>


<commit_before>#include "Base.h"
#include "MeshSkin.h"
#include "Node.h"
#include "StringUtil.h"
#include "Mesh.h"
#include "GPBFile.h"
#include "Animations.h"
#include "Transform.h"
#include "../../gameplay/src/Curve.h"
#include "Matrix.h"

namespace gameplay
{

MeshSkin::MeshSkin(void) :
    _vertexInfluenceCount(0)
{
    Matrix::setIdentity(_bindShape);
}

MeshSkin::~MeshSkin(void)
{
}

unsigned int MeshSkin::getTypeId(void) const
{
    return MESHPART_ID;
}

const char* MeshSkin::getElementName(void) const
{
    return "MeshSkin";
}

void MeshSkin::writeBinary(FILE* file)
{
    Object::writeBinary(file);
    write(_bindShape, 16, file);
    write(_joints.size(), file);
    for (std::vector<Node*>::const_iterator i = _joints.begin(); i != _joints.end(); i++)
    {
        (*i)->writeBinaryXref(file);
    }
    write(_bindPoses.size() * 16, file);
    for (std::vector<Matrix>::const_iterator i = _bindPoses.begin(); i != _bindPoses.end(); i++)
    {
        write(i->m, 16, file);
    }

    /*
    // Write joint bounding spheres
    write((unsigned int)_jointBounds.size(), file);
    for (unsigned int i = 0; i < _jointBounds.size(); ++i)
    {
        BoundingVolume& v = _jointBounds[i];
        write(v.center.x, file);
        write(v.center.y, file);
        write(v.center.z, file);
        write(v.radius, file);
    }
    */
}

void MeshSkin::writeText(FILE* file)
{
    fprintElementStart(file);
    fprintf(file, "<bindShape>");
    fprintfMatrix4f(file, _bindShape);
    fprintf(file, "</bindShape>");
    fprintf(file, "<joints>");
    for (std::vector<std::string>::const_iterator i = _jointNames.begin(); i != _jointNames.end(); i++)
    {
        fprintf(file, "%s ", i->c_str());
    }
    fprintf(file, "</joints>\n");
    fprintf(file, "<bindPoses count=\"%lu\">", _bindPoses.size() * 16);
    for (std::vector<Matrix>::const_iterator i = _bindPoses.begin(); i != _bindPoses.end(); i++)
    {
        for (unsigned int j = 0; j < 16; ++j)
        {
            fprintf(file, "%f ", i->m[j]);
        }
    }
    fprintf(file, "</bindPoses>\n");

    fprintElementEnd(file);
}

void MeshSkin::setBindShape(const float data[])
{
    for (int i = 0; i < 16; ++i)
    {
        _bindShape[i] = data[i];
    }
}

void MeshSkin::setVertexInfluenceCount(unsigned int count)
{
    _vertexInfluenceCount = count;
}

void MeshSkin::setJointNames(const std::vector<std::string>& list)
{
    _jointNames = list;
}

const std::vector<std::string>& MeshSkin::getJointNames()
{
    return _jointNames;
}

void MeshSkin::setJoints(const std::vector<Node*>& list)
{
    _joints = list;
}

void MeshSkin::setBindPoses(std::vector<Matrix>& list)
{
    for (std::vector<Matrix>::iterator i = list.begin(); i != list.end(); ++i)
    {
        _bindPoses.push_back(*i);
    }
}

bool MeshSkin::hasJoint(const char* id)
{
    for (std::vector<std::string>::iterator i = _jointNames.begin(); i != _jointNames.end(); i++)
    {
        if (equals(*i, id))
        {
            return true;
        }
    }
    return false;
}

void MeshSkin::computeBounds()
{
    // Find the offset of the blend indices and blend weights within the mesh vertices
    int blendIndexOffset = -1;
    int blendWeightOffset = -1;
    for (unsigned int i = 0, count = _mesh->getVertexElementCount(); i < count; ++i)
    {
        const VertexElement& e = _mesh->getVertexElement(i);
        switch (e.usage)
        {
        case BLENDINDICES:
            blendIndexOffset = i;
            break;
        case BLENDWEIGHTS:
            blendWeightOffset = i;
            break;
        }
    }
    if (blendIndexOffset == -1 || blendWeightOffset == -1)
    {
        // Need blend indices and blend weights to calculate skinned bounding volume
        return;
    }

    DEBUGPRINT_VARG("\nComputing bounds for skin of mesh: %s\n", _mesh->getId().c_str());

    Node* joint;

    // Get the root joint
    Node* rootJoint = _joints[0];
    Node* parent = rootJoint->getParent();
    while (parent)
    {
        // Is this parent in the list of joints that form the skeleton?
        // If not, then it's simply a parent node to the root joint
        if (find(_joints.begin(), _joints.end(), parent) != _joints.end())
        {
            rootJoint = parent;
        }
        parent = parent->getParent();
    }

    // If the root joint has a parent node, temporarily detach it so that its transform is
    // not included in the bounding volume calculation below
    Node* rootJointParent = rootJoint->getParent();
    if (rootJointParent)
    {
        rootJointParent->removeChild(rootJoint);
    }

    unsigned int jointCount = _joints.size();
    unsigned int vertexCount = _mesh->getVertexCount();

    DEBUGPRINT_VARG("> %d joints found.\n", jointCount);

    std::vector<AnimationChannel*> channels;
    std::vector<Node*> channelTargets;
    std::vector<Curve*> curves;
    std::vector<Vector3> vertices;
    _jointBounds.resize(jointCount);

    // Construct a list of all animation channels that target the joints affecting this mesh skin
    DEBUGPRINT("> Collecting animations...\n");
    DEBUGPRINT("> 0%%\r");
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        joint = _joints[i];

        // Find all animations that target this joint
        Animations* animations = GPBFile::getInstance()->getAnimations();
        for (unsigned int j = 0, animationCount = animations->getAnimationCount(); j < animationCount; ++j)
        {
            Animation* animation = animations->getAnimation(j);
            for (unsigned int k = 0, channelCount = animation->getAnimationChannelCount(); k < channelCount; ++k)
            {
                AnimationChannel* channel = animation->getAnimationChannel(k);
                if (channel->getTargetId() == joint->getId())
                {
                    if (find(channels.begin(), channels.end(), channel) == channels.end())
                    {
                        channels.push_back(channel);
                        channelTargets.push_back(joint);
                    }
                }
            }
        }

        // Calculate the local bounding volume for this joint
        vertices.clear();
        BoundingVolume jointBounds;
        jointBounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
        jointBounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
        for (unsigned int j = 0; j < vertexCount; ++j)
        {
            const Vertex& v = _mesh->getVertex(j);

            if ((v.blendIndices.x == i && !ISZERO(v.blendWeights.x)) ||
                (v.blendIndices.y == i && !ISZERO(v.blendWeights.y)) ||
                (v.blendIndices.z == i && !ISZERO(v.blendWeights.z)) ||
                (v.blendIndices.w == i && !ISZERO(v.blendWeights.w)))
            {
                vertices.push_back(v.position);
                // Update box min/max
                if (v.position.x < jointBounds.min.x)
                    jointBounds.min.x = v.position.x;
                if (v.position.y < jointBounds.min.y)
                    jointBounds.min.y = v.position.y;
                if (v.position.z < jointBounds.min.z)
                    jointBounds.min.z = v.position.z;
                if (v.position.x > jointBounds.max.x)
                    jointBounds.max.x = v.position.x;
                if (v.position.y > jointBounds.max.y)
                    jointBounds.max.y = v.position.y;
                if (v.position.z > jointBounds.max.z)
                    jointBounds.max.z = v.position.z;
            }
        }
        if (vertices.size() > 0)
        {
            // Compute center point
            Vector3::add(jointBounds.min, jointBounds.max, &jointBounds.center);
            jointBounds.center.scale(0.5f);
            // Compute radius
            for (unsigned int j = 0, jointVertexCount = vertices.size(); j < jointVertexCount; ++j)
            {
                float d = jointBounds.center.distanceSquared(vertices[j]);
                if (d > jointBounds.radius)
                    jointBounds.radius = d;
            }
            jointBounds.radius = sqrt(jointBounds.radius);
        }
        _jointBounds[i] = jointBounds;

        DEBUGPRINT_VARG("> %d%%\r", (int)((float)(i+1) / (float)jointCount * 100.0f));
    }
    DEBUGPRINT("\n");

    unsigned int channelCount = channels.size();

    // Create a Curve for each animation channel
    float maxDuration = 0.0f;
    DEBUGPRINT("> Building animation curves...\n");
    DEBUGPRINT("> 0%%\r");
    for (unsigned int i = 0; i < channelCount; ++i)
    {
        AnimationChannel* channel = channels[i];

        const std::vector<float>& keyTimes = channel->getKeyTimes();
        unsigned int keyCount = keyTimes.size();
        if (keyCount == 0)
            continue;

        // Create a curve for this animation channel
        Curve* curve = NULL;
        switch (channel->getTargetAttribute())
        {
        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
            curve = new Curve(keyCount, 10);
            curve->setQuaternionOffset(3);
            break;
        }
        if (curve == NULL)
        {
            // Unsupported/not implemented curve type
            continue;
        }

        // Copy key values into a temporary array
        unsigned int keyValuesCount = channel->getKeyValues().size();
        float* keyValues = new float[keyValuesCount];
        for (unsigned int j = 0; j < keyValuesCount; ++j)
            keyValues[j] = channel->getKeyValues()[j];

        // Determine animation duration
        float startTime = keyTimes[0];
        float duration = keyTimes[keyCount-1] - startTime;
        if (duration > maxDuration)
            maxDuration = duration;

        // Set curve points
        float* keyValuesPtr = keyValues;
        for (unsigned int j = 0; j < keyCount; ++j)
        {
            // Store time normalized, between 0-1
            float t = (keyTimes[j] - startTime) / duration;

            // Set the curve point
            // TODO: Handle other interpolation types
            curve->setPoint(j, t, keyValuesPtr, gameplay::Curve::LINEAR);

            // Move to the next point on the curve
            keyValuesPtr += curve->getComponentCount();
        }

        delete[] keyValues;
        keyValues = NULL;

        curves.push_back(curve);

        DEBUGPRINT_VARG("> %d%%\r", (int)((float)(i+1) / (float)channelCount * 100.0f));
    }
    DEBUGPRINT("\n");

    // Compute a total combined bounding volume for the MeshSkin that contains all possible
    // vertex positions for all animations targetting the skin. This rough approximation allows
    // us to store a volume that can be used for rough intersection tests (such as for visibility
    // determination) efficiently at runtime.

    // Backup existing node transforms so we can restore them when we are finished
    Matrix* oldTransforms = new Matrix[jointCount];
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        memcpy(oldTransforms[i].m, _joints[i]->getTransformMatrix().m, 16 * sizeof(float));
    }

    float time = 0.0f;
    float srt[10];
    Matrix temp;
    Matrix* jointTransforms = new Matrix[jointCount];
    _mesh->bounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
    _mesh->bounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
    _mesh->bounds.center.set(0, 0, 0);
    _mesh->bounds.radius = 0;
    Vector3 skinnedPos;
    Vector3 tempPos;
    DEBUGPRINT("> Evaluating joints...\n");
    DEBUGPRINT("> 0%%\r");
    BoundingVolume finalBounds;
    while (time <= maxDuration)
    {
        // Evaluate joint transforms at this time interval
        for (unsigned int i = 0, curveCount = curves.size(); i < curveCount; ++i)
        {
            Node* joint = channelTargets[i];
            Curve* curve = curves[i];

            // Evalulate the curve at this time to get the new value
            float tn = time / maxDuration;
            if (tn > 1.0f)
                tn = 1.0f;
            curve->evaluate(tn, srt);

            // Update the joint's local transform
            Matrix::createTranslation(srt[7], srt[8], srt[9], temp.m);
            temp.rotate(*((Quaternion*)&srt[3]));
            temp.scale(srt[0], srt[1], srt[2]);
            joint->setTransformMatrix(temp.m);
        }

        // Store the final matrix pallette of resovled world space joint matrices
        std::vector<Matrix>::const_iterator bindPoseItr = _bindPoses.begin();
        for (unsigned int i = 0; i < jointCount; ++i, bindPoseItr++)
        {
            BoundingVolume bounds = _jointBounds[i];
            if (ISZERO(bounds.radius))
                continue;

            Matrix& m = jointTransforms[i];
            Matrix::multiply(_joints[i]->getWorldMatrix().m, bindPoseItr->m, m.m);
            Matrix::multiply(m.m, _bindShape, m.m);

            // Get a world-space bounding volume for this joint
            bounds.transform(m);
            if (ISZERO(finalBounds.radius))
                finalBounds = bounds;
            else
                finalBounds.merge(bounds);
        }

        // Increment time by 1/30th of second (~ 33 ms)
        if (time < maxDuration && (time + 33.0f) > maxDuration)
            time = maxDuration;
        else
            time += 33.0f;

        DEBUGPRINT_VARG("> %d%%\r", (int)(time / maxDuration * 100.0f));
    }
    DEBUGPRINT("\n");

    // Update the bounding sphere for the mesh
    _mesh->bounds = finalBounds;

    // Restore original joint transforms
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        _joints[i]->setTransformMatrix(oldTransforms[i].m);
    }

    // Cleanup
    for (unsigned int i = 0, curveCount = curves.size(); i < curveCount; ++i)
    {
        delete curves[i];
    }
    delete[] oldTransforms;
    delete[] jointTransforms;

    // Restore removed joints
    if (rootJointParent)
    {
        rootJointParent->addChild(rootJoint);
    }
}

}
<commit_msg>Fixed bug in encoder when there is only 1 key frame. Fixed divide by zero.<commit_after>#include "Base.h"
#include "MeshSkin.h"
#include "Node.h"
#include "StringUtil.h"
#include "Mesh.h"
#include "GPBFile.h"
#include "Animations.h"
#include "Transform.h"
#include "../../gameplay/src/Curve.h"
#include "Matrix.h"

namespace gameplay
{

MeshSkin::MeshSkin(void) :
    _vertexInfluenceCount(0)
{
    Matrix::setIdentity(_bindShape);
}

MeshSkin::~MeshSkin(void)
{
}

unsigned int MeshSkin::getTypeId(void) const
{
    return MESHPART_ID;
}

const char* MeshSkin::getElementName(void) const
{
    return "MeshSkin";
}

void MeshSkin::writeBinary(FILE* file)
{
    Object::writeBinary(file);
    write(_bindShape, 16, file);
    write(_joints.size(), file);
    for (std::vector<Node*>::const_iterator i = _joints.begin(); i != _joints.end(); i++)
    {
        (*i)->writeBinaryXref(file);
    }
    write(_bindPoses.size() * 16, file);
    for (std::vector<Matrix>::const_iterator i = _bindPoses.begin(); i != _bindPoses.end(); i++)
    {
        write(i->m, 16, file);
    }

    /*
    // Write joint bounding spheres
    write((unsigned int)_jointBounds.size(), file);
    for (unsigned int i = 0; i < _jointBounds.size(); ++i)
    {
        BoundingVolume& v = _jointBounds[i];
        write(v.center.x, file);
        write(v.center.y, file);
        write(v.center.z, file);
        write(v.radius, file);
    }
    */
}

void MeshSkin::writeText(FILE* file)
{
    fprintElementStart(file);
    fprintf(file, "<bindShape>");
    fprintfMatrix4f(file, _bindShape);
    fprintf(file, "</bindShape>");
    fprintf(file, "<joints>");
    for (std::vector<std::string>::const_iterator i = _jointNames.begin(); i != _jointNames.end(); i++)
    {
        fprintf(file, "%s ", i->c_str());
    }
    fprintf(file, "</joints>\n");
    fprintf(file, "<bindPoses count=\"%lu\">", _bindPoses.size() * 16);
    for (std::vector<Matrix>::const_iterator i = _bindPoses.begin(); i != _bindPoses.end(); i++)
    {
        for (unsigned int j = 0; j < 16; ++j)
        {
            fprintf(file, "%f ", i->m[j]);
        }
    }
    fprintf(file, "</bindPoses>\n");

    fprintElementEnd(file);
}

void MeshSkin::setBindShape(const float data[])
{
    for (int i = 0; i < 16; ++i)
    {
        _bindShape[i] = data[i];
    }
}

void MeshSkin::setVertexInfluenceCount(unsigned int count)
{
    _vertexInfluenceCount = count;
}

void MeshSkin::setJointNames(const std::vector<std::string>& list)
{
    _jointNames = list;
}

const std::vector<std::string>& MeshSkin::getJointNames()
{
    return _jointNames;
}

void MeshSkin::setJoints(const std::vector<Node*>& list)
{
    _joints = list;
}

void MeshSkin::setBindPoses(std::vector<Matrix>& list)
{
    for (std::vector<Matrix>::iterator i = list.begin(); i != list.end(); ++i)
    {
        _bindPoses.push_back(*i);
    }
}

bool MeshSkin::hasJoint(const char* id)
{
    for (std::vector<std::string>::iterator i = _jointNames.begin(); i != _jointNames.end(); i++)
    {
        if (equals(*i, id))
        {
            return true;
        }
    }
    return false;
}

void MeshSkin::computeBounds()
{
    // Find the offset of the blend indices and blend weights within the mesh vertices
    int blendIndexOffset = -1;
    int blendWeightOffset = -1;
    for (unsigned int i = 0, count = _mesh->getVertexElementCount(); i < count; ++i)
    {
        const VertexElement& e = _mesh->getVertexElement(i);
        switch (e.usage)
        {
        case BLENDINDICES:
            blendIndexOffset = i;
            break;
        case BLENDWEIGHTS:
            blendWeightOffset = i;
            break;
        }
    }
    if (blendIndexOffset == -1 || blendWeightOffset == -1)
    {
        // Need blend indices and blend weights to calculate skinned bounding volume
        return;
    }

    DEBUGPRINT_VARG("\nComputing bounds for skin of mesh: %s\n", _mesh->getId().c_str());

    Node* joint;

    // Get the root joint
    Node* rootJoint = _joints[0];
    Node* parent = rootJoint->getParent();
    while (parent)
    {
        // Is this parent in the list of joints that form the skeleton?
        // If not, then it's simply a parent node to the root joint
        if (find(_joints.begin(), _joints.end(), parent) != _joints.end())
        {
            rootJoint = parent;
        }
        parent = parent->getParent();
    }

    // If the root joint has a parent node, temporarily detach it so that its transform is
    // not included in the bounding volume calculation below
    Node* rootJointParent = rootJoint->getParent();
    if (rootJointParent)
    {
        rootJointParent->removeChild(rootJoint);
    }

    unsigned int jointCount = _joints.size();
    unsigned int vertexCount = _mesh->getVertexCount();

    DEBUGPRINT_VARG("> %d joints found.\n", jointCount);

    std::vector<AnimationChannel*> channels;
    std::vector<Node*> channelTargets;
    std::vector<Curve*> curves;
    std::vector<Vector3> vertices;
    _jointBounds.resize(jointCount);

    // Construct a list of all animation channels that target the joints affecting this mesh skin
    DEBUGPRINT("> Collecting animations...\n");
    DEBUGPRINT("> 0%%\r");
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        joint = _joints[i];

        // Find all animations that target this joint
        Animations* animations = GPBFile::getInstance()->getAnimations();
        for (unsigned int j = 0, animationCount = animations->getAnimationCount(); j < animationCount; ++j)
        {
            Animation* animation = animations->getAnimation(j);
            for (unsigned int k = 0, channelCount = animation->getAnimationChannelCount(); k < channelCount; ++k)
            {
                AnimationChannel* channel = animation->getAnimationChannel(k);
                if (channel->getTargetId() == joint->getId())
                {
                    if (find(channels.begin(), channels.end(), channel) == channels.end())
                    {
                        channels.push_back(channel);
                        channelTargets.push_back(joint);
                    }
                }
            }
        }

        // Calculate the local bounding volume for this joint
        vertices.clear();
        BoundingVolume jointBounds;
        jointBounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
        jointBounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
        for (unsigned int j = 0; j < vertexCount; ++j)
        {
            const Vertex& v = _mesh->getVertex(j);

            if ((v.blendIndices.x == i && !ISZERO(v.blendWeights.x)) ||
                (v.blendIndices.y == i && !ISZERO(v.blendWeights.y)) ||
                (v.blendIndices.z == i && !ISZERO(v.blendWeights.z)) ||
                (v.blendIndices.w == i && !ISZERO(v.blendWeights.w)))
            {
                vertices.push_back(v.position);
                // Update box min/max
                if (v.position.x < jointBounds.min.x)
                    jointBounds.min.x = v.position.x;
                if (v.position.y < jointBounds.min.y)
                    jointBounds.min.y = v.position.y;
                if (v.position.z < jointBounds.min.z)
                    jointBounds.min.z = v.position.z;
                if (v.position.x > jointBounds.max.x)
                    jointBounds.max.x = v.position.x;
                if (v.position.y > jointBounds.max.y)
                    jointBounds.max.y = v.position.y;
                if (v.position.z > jointBounds.max.z)
                    jointBounds.max.z = v.position.z;
            }
        }
        if (vertices.size() > 0)
        {
            // Compute center point
            Vector3::add(jointBounds.min, jointBounds.max, &jointBounds.center);
            jointBounds.center.scale(0.5f);
            // Compute radius
            for (unsigned int j = 0, jointVertexCount = vertices.size(); j < jointVertexCount; ++j)
            {
                float d = jointBounds.center.distanceSquared(vertices[j]);
                if (d > jointBounds.radius)
                    jointBounds.radius = d;
            }
            jointBounds.radius = sqrt(jointBounds.radius);
        }
        _jointBounds[i] = jointBounds;

        DEBUGPRINT_VARG("> %d%%\r", (int)((float)(i+1) / (float)jointCount * 100.0f));
    }
    DEBUGPRINT("\n");

    unsigned int channelCount = channels.size();

    // Create a Curve for each animation channel
    float maxDuration = 0.0f;
    DEBUGPRINT("> Building animation curves...\n");
    DEBUGPRINT("> 0%%\r");
    for (unsigned int i = 0; i < channelCount; ++i)
    {
        AnimationChannel* channel = channels[i];

        const std::vector<float>& keyTimes = channel->getKeyTimes();
        unsigned int keyCount = keyTimes.size();
        if (keyCount == 0)
            continue;

        // Create a curve for this animation channel
        Curve* curve = NULL;
        switch (channel->getTargetAttribute())
        {
        case Transform::ANIMATE_SCALE_ROTATE_TRANSLATE:
            curve = new Curve(keyCount, 10);
            curve->setQuaternionOffset(3);
            break;
        }
        if (curve == NULL)
        {
            // Unsupported/not implemented curve type
            continue;
        }

        // Copy key values into a temporary array
        unsigned int keyValuesCount = channel->getKeyValues().size();
        float* keyValues = new float[keyValuesCount];
        for (unsigned int j = 0; j < keyValuesCount; ++j)
            keyValues[j] = channel->getKeyValues()[j];

        // Determine animation duration
        float startTime = keyTimes[0];
        float duration = keyTimes[keyCount-1] - startTime;
        if (duration > maxDuration)
            maxDuration = duration;

        if (duration > 0.0f)
        {
            // Set curve points
            float* keyValuesPtr = keyValues;
            for (unsigned int j = 0; j < keyCount; ++j)
            {
                // Store time normalized, between 0-1
                float t = (keyTimes[j] - startTime) / duration;

                // Set the curve point
                // TODO: Handle other interpolation types
                curve->setPoint(j, t, keyValuesPtr, gameplay::Curve::LINEAR);

                // Move to the next point on the curve
                keyValuesPtr += curve->getComponentCount();
            }
            curves.push_back(curve);
        }

        delete[] keyValues;
        keyValues = NULL;

        DEBUGPRINT_VARG("> %d%%\r", (int)((float)(i+1) / (float)channelCount * 100.0f));
    }
    DEBUGPRINT("\n");

    // Compute a total combined bounding volume for the MeshSkin that contains all possible
    // vertex positions for all animations targetting the skin. This rough approximation allows
    // us to store a volume that can be used for rough intersection tests (such as for visibility
    // determination) efficiently at runtime.

    // Backup existing node transforms so we can restore them when we are finished
    Matrix* oldTransforms = new Matrix[jointCount];
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        memcpy(oldTransforms[i].m, _joints[i]->getTransformMatrix().m, 16 * sizeof(float));
    }

    float time = 0.0f;
    float srt[10];
    Matrix temp;
    Matrix* jointTransforms = new Matrix[jointCount];
    _mesh->bounds.min.set(FLT_MAX, FLT_MAX, FLT_MAX);
    _mesh->bounds.max.set(-FLT_MAX, -FLT_MAX, -FLT_MAX);
    _mesh->bounds.center.set(0, 0, 0);
    _mesh->bounds.radius = 0;
    Vector3 skinnedPos;
    Vector3 tempPos;
    DEBUGPRINT("> Evaluating joints...\n");
    DEBUGPRINT("> 0%%\r");
    BoundingVolume finalBounds;
    while (time <= maxDuration)
    {
        // Evaluate joint transforms at this time interval
        for (unsigned int i = 0, curveCount = curves.size(); i < curveCount; ++i)
        {
            Node* joint = channelTargets[i];
            Curve* curve = curves[i];

            // Evalulate the curve at this time to get the new value
            float tn = time / maxDuration;
            if (tn > 1.0f)
                tn = 1.0f;
            curve->evaluate(tn, srt);

            // Update the joint's local transform
            Matrix::createTranslation(srt[7], srt[8], srt[9], temp.m);
            temp.rotate(*((Quaternion*)&srt[3]));
            temp.scale(srt[0], srt[1], srt[2]);
            joint->setTransformMatrix(temp.m);
        }

        // Store the final matrix pallette of resovled world space joint matrices
        std::vector<Matrix>::const_iterator bindPoseItr = _bindPoses.begin();
        for (unsigned int i = 0; i < jointCount; ++i, bindPoseItr++)
        {
            BoundingVolume bounds = _jointBounds[i];
            if (ISZERO(bounds.radius))
                continue;

            Matrix& m = jointTransforms[i];
            Matrix::multiply(_joints[i]->getWorldMatrix().m, bindPoseItr->m, m.m);
            Matrix::multiply(m.m, _bindShape, m.m);

            // Get a world-space bounding volume for this joint
            bounds.transform(m);
            if (ISZERO(finalBounds.radius))
                finalBounds = bounds;
            else
                finalBounds.merge(bounds);
        }

        // Increment time by 1/30th of second (~ 33 ms)
        if (time < maxDuration && (time + 33.0f) > maxDuration)
            time = maxDuration;
        else
            time += 33.0f;

        DEBUGPRINT_VARG("> %d%%\r", (int)(time / maxDuration * 100.0f));
    }
    DEBUGPRINT("\n");

    // Update the bounding sphere for the mesh
    _mesh->bounds = finalBounds;

    // Restore original joint transforms
    for (unsigned int i = 0; i < jointCount; ++i)
    {
        _joints[i]->setTransformMatrix(oldTransforms[i].m);
    }

    // Cleanup
    for (unsigned int i = 0, curveCount = curves.size(); i < curveCount; ++i)
    {
        delete curves[i];
    }
    delete[] oldTransforms;
    delete[] jointTransforms;

    // Restore removed joints
    if (rootJointParent)
    {
        rootJointParent->addChild(rootJoint);
    }
}

}
<|endoftext|>