Add cyhair2gltf converter.

This commit is contained in:
Syoyo Fujita 2016-10-25 01:30:40 +09:00
parent 72f3f77840
commit 3b17371648
5 changed files with 785 additions and 0 deletions

View File

@ -0,0 +1,6 @@
# Suppress some C++ warnings(in clang).
EXTRA_CXXFLAGS := -Weverything -Werror
all:
clang++ -g -o cyhair2gltf $(EXTRA_CXXFLAGS) cyhair2gltf.cc cyhair_loader.cc

View File

@ -0,0 +1,13 @@
# Simple CyHair to glTF converter
For CyHair format, please refer: http://www.cemyuksel.com/cyCodeBase/code.html
For hair model with CyHair format, please refer for example: http://www.cemyuksel.com/research/hairmodels/
## Convert
$ ./cyhair2gltf input.hair output.gltf
## View
You can view converted .gltf with glviwew exmaple(`example/glview`).

View File

@ -0,0 +1,352 @@
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <fstream>
#ifdef __clang__
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wc++11-long-long"
#pragma clang diagnostic ignored "-Wold-style-cast"
#pragma clang diagnostic ignored "-Wpadded"
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wc++11-extensions"
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wreserved-id-macro"
#pragma clang diagnostic ignored "-Wfloat-equal"
#pragma clang diagnostic ignored "-Wdeprecated"
#pragma clang diagnostic ignored "-Wweak-vtables"
#pragma clang diagnostic ignored "-Wextra-semi"
#pragma clang diagnostic ignored "-Wswitch-enum"
#pragma clang diagnostic ignored "-Wglobal-constructors"
#pragma clang diagnostic ignored "-Wunused-parameter"
#pragma clang diagnostic ignored "-Wexit-time-destructors"
#pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
#pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
#pragma clang diagnostic ignored "-Wdouble-promotion"
#pragma clang diagnostic ignored "-Wcovered-switch-default"
#endif
#define PICOJSON_USE_INT64
#include "../../picojson.h"
#ifdef __clang__
#pragma clang diagnostic pop
#endif
#include "../../tiny_gltf_loader.h" // To import some TINYGLTF_*** macros.
#include "cyhair_loader.h"
// Curves are represented as an array of curve.
// i'th curve has nverts[i] points.
// TODO(syoyo) knots, order to support NURBS curve.
typedef struct
{
std::vector<float> points;
std::vector<int> nverts; // # of vertices per strand(curve).
} Curves;
// ----------------------------------------------------------------
// writer module
// @todo { move writer code to tiny_gltf_writer.h }
// http://www.adp-gmbh.ch/cpp/common/base64.html
static const char *base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static std::string base64_encode(unsigned char const* bytes_to_encode,
size_t in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = static_cast<unsigned char>(
((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4));
char_array_4[2] = static_cast<unsigned char>(
((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6));
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i) {
for (j = i; j < 3; j++) char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = static_cast<unsigned char>(
((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4));
char_array_4[2] = static_cast<unsigned char>(
((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6));
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]];
while ((i++ < 3)) ret += '=';
}
return ret;
}
static bool SaveCurvesToGLTF(const std::string& output_filename,
const Curves& curves) {
picojson::object root;
{
picojson::object asset;
asset["generator"] = picojson::value("abc2gltf");
asset["premultipliedAlpha"] = picojson::value(true);
asset["version"] = picojson::value(static_cast<double>(1));
picojson::object profile;
profile["api"] = picojson::value("WebGL");
profile["version"] = picojson::value("1.0.2");
asset["profile"] = picojson::value(profile);
root["assets"] = picojson::value(asset);
}
{
picojson::object buffers;
{
{
std::string b64data = base64_encode(reinterpret_cast<unsigned char const*>(curves.points.data()), curves.points.size() * sizeof(float));
picojson::object buf;
buf["type"] = picojson::value("arraybuffer");
buf["uri"] = picojson::value(
std::string("data:application/octet-stream;base64,") + b64data);
buf["byteLength"] =
picojson::value(static_cast<double>(curves.points.size() * sizeof(float)));
buffers["points"] = picojson::value(buf);
}
// Out extension
{
std::string b64data = base64_encode(reinterpret_cast<unsigned char const*>(curves.nverts.data()), curves.nverts.size() * sizeof(int));
picojson::object buf;
buf["type"] = picojson::value("arraybuffer");
buf["uri"] = picojson::value(
std::string("data:application/octet-stream;base64,") + b64data);
buf["byteLength"] =
picojson::value(static_cast<double>(curves.nverts.size() * sizeof(int)));
buffers["nverts"] = picojson::value(buf);
}
}
root["buffers"] = picojson::value(buffers);
}
{
picojson::object buffer_views;
{
{
picojson::object buffer_view_points;
buffer_view_points["buffer"] = picojson::value(std::string("points"));
buffer_view_points["byteLength"] = picojson::value(static_cast<double>(curves.points.size() * sizeof(float)));
buffer_view_points["byteOffset"] = picojson::value(static_cast<double>(0));
buffer_view_points["target"] = picojson::value(static_cast<int64_t>(TINYGLTF_TARGET_ARRAY_BUFFER));
buffer_views["bufferView_points"] = picojson::value(buffer_view_points);
}
{
picojson::object buffer_view_nverts;
buffer_view_nverts["buffer"] = picojson::value(std::string("nverts"));
buffer_view_nverts["byteLength"] = picojson::value(static_cast<double>(curves.nverts.size() * sizeof(int)));
buffer_view_nverts["byteOffset"] = picojson::value(static_cast<double>(0));
buffer_view_nverts["target"] = picojson::value(static_cast<int64_t>(TINYGLTF_TARGET_ARRAY_BUFFER));
buffer_views["bufferView_nverts"] = picojson::value(buffer_view_nverts);
}
}
root["bufferViews"] = picojson::value(buffer_views);
}
{
picojson::object attributes;
attributes["POSITION"] = picojson::value(std::string("accessor_points"));
attributes["NVERTS"] = picojson::value(std::string("accessor_nverts"));
// Extra information for curves primtive.
picojson::object extra;
extra["ext_mode"] = picojson::value("curves");
picojson::object primitive;
primitive["attributes"] = picojson::value(attributes);
//primitive["indices"] = picojson::value("accessor_indices");
primitive["material"] = picojson::value("material_1");
primitive["mode"] = picojson::value(static_cast<int64_t>(TINYGLTF_MODE_POINTS)); // Use GL_POINTS for backward compatibility
primitive["extras"] = picojson::value(extra);
picojson::array primitive_array;
primitive_array.push_back(picojson::value(primitive));
picojson::object m;
m["primitives"] = picojson::value(primitive_array);
picojson::object meshes;
meshes["mesh_1"] = picojson::value(m);
root["meshes"] = picojson::value(meshes);
}
{
picojson::object accessors;
{
picojson::object accessor_points;
accessor_points["bufferView"] = picojson::value(std::string("bufferView_points"));
accessor_points["byteOffset"] = picojson::value(static_cast<int64_t>(0));
accessor_points["byteStride"] = picojson::value(static_cast<double>(3 * sizeof(float)));
accessor_points["componentType"] = picojson::value(static_cast<int64_t>(TINYGLTF_COMPONENT_TYPE_FLOAT));
accessor_points["count"] = picojson::value(static_cast<int64_t>(curves.points.size()));
accessor_points["type"] = picojson::value(std::string("VEC3"));
accessors["accessor_points"] = picojson::value(accessor_points);
}
{
picojson::object accessor_nverts;
accessor_nverts["bufferView"] = picojson::value(std::string("bufferView_nverts"));
accessor_nverts["byteOffset"] = picojson::value(static_cast<int64_t>(0));
accessor_nverts["byteStride"] = picojson::value(static_cast<double>(sizeof(int)));
accessor_nverts["componentType"] = picojson::value(static_cast<int64_t>(TINYGLTF_COMPONENT_TYPE_INT));
accessor_nverts["count"] = picojson::value(static_cast<int64_t>(curves.nverts.size()));
accessor_nverts["type"] = picojson::value(std::string("SCALAR"));
accessors["accessor_nverts"] = picojson::value(accessor_nverts);
}
picojson::object accessor_indices;
root["accessors"] = picojson::value(accessors);
}
{
// Use Default Material(Do not supply `material.technique`)
picojson::object default_material;
picojson::object materials;
materials["material_1"] = picojson::value(default_material);
root["materials"] = picojson::value(materials);
}
{
picojson::object nodes;
picojson::object node;
picojson::array meshes;
meshes.push_back(picojson::value(std::string("mesh_1")));
node["meshes"] = picojson::value(meshes);
nodes["node_1"] = picojson::value(node);
root["nodes"] = picojson::value(nodes);
}
{
picojson::object defaultScene;
picojson::array nodes;
nodes.push_back(picojson::value(std::string("node_1")));
defaultScene["nodes"] = picojson::value(nodes);
root["scene"] = picojson::value("defaultScene");
picojson::object scenes;
scenes["defaultScene"] = picojson::value(defaultScene);
root["scenes"] = picojson::value(scenes);
}
// @todo {}
picojson::object shaders;
picojson::object programs;
picojson::object techniques;
picojson::object materials;
picojson::object skins;
root["shaders"] = picojson::value(shaders);
root["programs"] = picojson::value(programs);
root["techniques"] = picojson::value(techniques);
root["materials"] = picojson::value(materials);
root["skins"] = picojson::value(skins);
std::ofstream ifs(output_filename.c_str());
if (ifs.bad()) {
std::cerr << "Failed to open " << output_filename << std::endl;
return false;
}
picojson::value v = picojson::value(root);
std::string s = v.serialize(/* pretty */true);
ifs.write(s.data(), static_cast<ssize_t>(s.size()));
ifs.close();
return true;
}
int main(int argc, char** argv) {
std::string cyhair_filename;
std::string gltf_filename;
if (argc < 3) {
std::cerr << "Usage: cyhair2abc input.hair output.gltf" << std::endl;
return EXIT_FAILURE;
}
cyhair_filename = std::string(argv[1]);
gltf_filename = std::string(argv[2]);
example::CyHair cyhair;
{
bool ret = cyhair.Load(cyhair_filename.c_str());
if (!ret) {
std::cerr << "Failed to load " << cyhair_filename << std::endl;
return EXIT_FAILURE;
}
}
// Convert to curves.
Curves curves;
{
// TODO(syoyo): thickness, colors, etc.
curves.points = cyhair.points_;
// NVETS = numSegments + 1
if (cyhair.segments_.empty()) {
for (size_t i = 0; i < cyhair.num_strands_; i++) {
curves.nverts.push_back(cyhair.default_segments_ + 1);
}
} else {
for (size_t i = 0; i < cyhair.segments_.size(); i++) {
curves.nverts.push_back(cyhair.segments_[i] + 1);
}
}
}
bool ret = SaveCurvesToGLTF(gltf_filename, curves);
if (ret) {
std::cout << "Wrote " << gltf_filename << std::endl;
} else {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -0,0 +1,305 @@
/*
The MIT License (MIT)
Copyright (c) 2016 Light Transport Entertainment, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Simple Cyhair loader.
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
#include "cyhair_loader.h"
namespace example {
class real3 {
public:
real3() : x(0.0f), y(0.0f), z(0.0f) {}
real3(float v) : x(v), y(v), z(v) {}
real3(float xx, float yy, float zz) : x(xx), y(yy), z(zz) {}
//~real3() {}
real3 operator+(const real3 &f2) const {
return real3(x + f2.x, y + f2.y, z + f2.z);
}
real3 operator*(const real3 &f2) const {
return real3(x * f2.x, y * f2.y, z * f2.z);
}
real3 operator/(const real3 &f2) const {
return real3(x / f2.x, y / f2.y, z / f2.z);
}
real3 operator/(const float f) const { return real3(x / f, y / f, z / f); }
float x, y, z;
};
inline real3 operator*(float f, const real3 &v) {
return real3(v.x * f, v.y * f, v.z * f);
}
static const float toC2B[4][4] = {
{0.0f, 6.0f / 6.0f, 0.0f, 0.0f},
{-1.0f / 6.0f, 6.0f / 6.0f, 1.0f / 6.0f, 0.0f},
{0.0f, 1.0f / 6.0f, 6.0f / 6.0f, -1.0f / 6.0f},
{0.0f, 0.0, 6.0f / 6.0f, 0.0f}};
static const float toC2B0[4][4] = {
{0.0f, 6.0f / 6.0f, 0.0f, 0.0f},
{0.0f, 3.0f / 6.0f, 4.0f / 6.0f, -1.0f / 6.0f},
{0.0f, 1.0f / 6.0f, 6.0f / 6.0f, -1.0f / 6.0f},
{0.0f, 0.0f, 6.0f / 6.0f, 0.0f}};
static const float toC2B1[4][4] = {
{0.0f, 6.0f / 6.0f, 0.0f, 0.0f},
{-1.0f / 6.0f, 6.0f / 6.0f, 1.0f / 6.0f, 0.0f},
{-1.0f / 6.0f, 4.0f / 6.0f, 3.0f / 6.0f, 0.0f},
{0.0f, 0.0f, 6.0f / 6.0f, 0.0f}};
static void mul_matrix(real3 out[4], const float mat[4][4], const real3 pt[4]) {
for (int i = 0; i < 4; i++) {
out[i] = mat[i][0] * pt[0] + mat[i][1] * pt[1] + mat[i][2] * pt[2] +
mat[i][3] * pt[3];
}
}
static void CamullRomToCubicBezier(real3 Q[4], const real3 *cps, int cps_size,
int seg_idx) {
size_t sz = static_cast<size_t>(cps_size);
if (sz == 2) {
Q[0] = cps[seg_idx];
Q[1] = cps[seg_idx] * 2.0f / 3.0f + cps[seg_idx + 1] * 1.0f / 3.0f;
Q[2] = cps[seg_idx] * 1.0f / 3.0f + cps[seg_idx + 1] * 2.0f / 3.0f;
Q[3] = cps[seg_idx + 1];
} else {
real3 P[4];
if (seg_idx == 0) {
P[0] = real3(0.0f);
P[1] = cps[seg_idx + 0];
P[2] = cps[seg_idx + 1];
P[3] = cps[seg_idx + 2];
mul_matrix(Q, toC2B0, P);
} else if (seg_idx == static_cast<int>(sz - 2)) {
P[0] = cps[seg_idx - 1];
P[1] = cps[seg_idx + 0];
P[2] = cps[seg_idx + 1];
P[3] = real3(0.0f);
mul_matrix(Q, toC2B1, P);
} else {
P[0] = cps[seg_idx - 1];
P[1] = cps[seg_idx + 0];
P[2] = cps[seg_idx + 1];
P[3] = cps[seg_idx + 2];
mul_matrix(Q, toC2B, P);
}
}
}
bool CyHair::Load(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
return false;
}
assert(sizeof(CyHairHeader) == 128);
CyHairHeader header;
if (1 != fread(&header, 128, 1, fp)) {
fclose(fp);
return false;
}
if (memcmp(header.magic, "HAIR", 4) != 0) {
fclose(fp);
return false;
}
flags_ = header.flags;
default_thickness_ = header.default_thickness;
default_transparency_ = header.default_transparency;
default_segments_ = static_cast<int>(header.default_segments);
default_color_[0] = header.default_color[0];
default_color_[1] = header.default_color[1];
default_color_[2] = header.default_color[2];
const bool has_segments = flags_ & 0x1;
const bool has_points = flags_ & 0x2;
const bool has_thickness = flags_ & 0x4;
const bool has_transparency = flags_ & 0x8;
const bool has_color = flags_ & 0x10;
num_strands_ = header.num_strands;
total_points_ = header.total_points;
if (!has_points) {
std::cout << "No point data in CyHair." << std::endl;
return false;
}
if ((default_segments_ < 1) && (!has_segments)) {
std::cout << "No valid segment information in CyHair." << std::endl;
return false;
}
// First read all strand data from a file.
if (has_segments) {
segments_.resize(num_strands_);
if (1 !=
fread(&segments_[0], sizeof(unsigned short) * num_strands_, 1, fp)) {
std::cout << "Failed to read CyHair segments data." << std::endl;
fclose(fp);
return false;
}
}
if (has_points) {
points_.resize(3 * total_points_);
size_t n = fread(&points_[0], total_points_ * sizeof(float) * 3, 1, fp);
if (1 != n) {
std::cout << "Failed to read CyHair points data." << std::endl;
fclose(fp);
return false;
}
}
if (has_thickness) {
thicknesses_.resize(total_points_);
if (1 != fread(&thicknesses_[0], total_points_ * sizeof(float), 1, fp)) {
std::cout << "Failed to read CyHair thickness data." << std::endl;
fclose(fp);
return false;
}
}
if (has_transparency) {
transparencies_.resize(total_points_);
if (1 != fread(&transparencies_[0], total_points_ * sizeof(float), 1, fp)) {
std::cout << "Failed to read CyHair transparencies data." << std::endl;
fclose(fp);
return false;
}
}
if (has_color) {
colors_.resize(3 * total_points_);
if (1 != fread(&colors_[0], total_points_ * sizeof(float) * 3, 1, fp)) {
std::cout << "Failed to read CyHair colors data." << std::endl;
fclose(fp);
return false;
}
}
// Build strand offset table.
strand_offsets_.resize(num_strands_);
strand_offsets_[0] = 0;
for (size_t i = 1; i < num_strands_; i++) {
int num_segments = segments_.empty() ? default_segments_ : segments_[i - 1];
strand_offsets_[i] =
strand_offsets_[i - 1] + static_cast<unsigned int>(num_segments + 1);
}
return true;
}
bool CyHair::ToCubicBezierCurves(std::vector<float> *vertices,
std::vector<float> *radiuss,
const float vertex_scale[3],
const float vertex_translate[3],
const int max_strands, const float user_thickness) {
if (points_.empty() || strand_offsets_.empty()) {
return false;
}
vertices->clear();
radiuss->clear();
int num_strands = static_cast<int>(num_strands_);
if ((max_strands > 0) && (max_strands < num_strands)) {
num_strands = max_strands;
}
std::cout << "[Hair] Convert first " << num_strands << " strands from "
<< max_strands << " strands in the original hair data."
<< std::endl;
// Assume input points are CatmullRom spline.
for (size_t i = 0; i < static_cast<size_t>(num_strands); i++) {
if ((i % 1000) == 0) {
std::cout << i << " / " << num_strands_ << std::endl;
}
int num_segments = segments_.empty() ? default_segments_ : segments_[i];
if (num_segments < 2) {
continue;
}
std::vector<real3> segment_points;
for (size_t k = 0; k < static_cast<size_t>(num_segments); k++) {
// Zup -> Yup
real3 p(points_[3 * (strand_offsets_[i] + k) + 0],
points_[3 * (strand_offsets_[i] + k) + 2],
points_[3 * (strand_offsets_[i] + k) + 1]);
segment_points.push_back(p);
}
// Skip both endpoints
for (int s = 1; s < num_segments - 1; s++) {
int seg_idx = s - 1;
real3 q[4];
CamullRomToCubicBezier(q, segment_points.data(), num_segments, seg_idx);
vertices->push_back(vertex_scale[0] * q[0].x + vertex_translate[0]);
vertices->push_back(vertex_scale[1] * q[0].y + vertex_translate[1]);
vertices->push_back(vertex_scale[2] * q[0].z + vertex_translate[2]);
vertices->push_back(vertex_scale[0] * q[1].x + vertex_translate[0]);
vertices->push_back(vertex_scale[1] * q[1].y + vertex_translate[1]);
vertices->push_back(vertex_scale[2] * q[1].z + vertex_translate[2]);
vertices->push_back(vertex_scale[0] * q[2].x + vertex_translate[0]);
vertices->push_back(vertex_scale[1] * q[2].y + vertex_translate[1]);
vertices->push_back(vertex_scale[2] * q[2].z + vertex_translate[2]);
vertices->push_back(vertex_scale[0] * q[3].x + vertex_translate[0]);
vertices->push_back(vertex_scale[1] * q[3].y + vertex_translate[1]);
vertices->push_back(vertex_scale[2] * q[3].z + vertex_translate[2]);
if (user_thickness > 0) {
// Use user supplied thickness.
radiuss->push_back(user_thickness);
radiuss->push_back(user_thickness);
radiuss->push_back(user_thickness);
radiuss->push_back(user_thickness);
} else {
// TODO(syoyo) Support per point/segment thickness
radiuss->push_back(default_thickness_);
radiuss->push_back(default_thickness_);
radiuss->push_back(default_thickness_);
radiuss->push_back(default_thickness_);
}
}
}
return true;
}
} // namespace example

View File

@ -0,0 +1,109 @@
/*
The MIT License (MIT)
Copyright (c) 2016 Light Transport Entertainment, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// Simple Cyhair loader.
#ifndef EXAMPLE_CYHAIR_LOADER_H_
#define EXAMPLE_CYHAIR_LOADER_H_
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <iostream>
namespace example {
struct CyHairHeader {
char magic[4];
unsigned int num_strands;
unsigned int total_points;
unsigned int flags;
unsigned int default_segments;
float default_thickness;
float default_transparency;
float default_color[3];
char infomation[88];
};
class CyHair {
public:
CyHair()
: flags_(0),
num_strands_(0),
total_points_(0),
default_segments_(-1),
default_thickness_(0.01f),
default_transparency_(1.0f) {
default_color_[0] = 0.5f;
default_color_[1] = 0.5f;
default_color_[2] = 0.5f;
}
~CyHair() {}
/// Load CyHair data from a file.
bool Load(const char *filename);
/// Convert to cubic bezier curves.
/// 4(cubic) * 3(xyz) * num_curves = vertices.size()
/// 4(cubic) * num_curves = radiuss.size()
/// `max_strands` limits the number of strands to convert. -1 = convert all
/// strands.
/// `thickness` overwrites strand thickness if it have positive value.
/// Apply `vertex_translate` after `vertex_scale`.
/// TODO(syoyo) return strand/segment information
bool ToCubicBezierCurves(std::vector<float> *vertices,
std::vector<float> *radiuss,
const float vertex_scale[3],
const float vertex_translate[3],
const int max_strands = -1,
const float thickness = -1.0f);
CyHairHeader header_;
// Raw CyHair values
std::vector<unsigned short> segments_;
std::vector<float> points_; // xyz
std::vector<float> thicknesses_;
std::vector<float> transparencies_;
std::vector<float> colors_; // rgb
unsigned int flags_;
unsigned int num_strands_;
unsigned int total_points_;
int default_segments_;
float default_thickness_;
float default_transparency_;
float default_color_[3];
int pad0;
// Processed CyHair values
std::vector<unsigned int> strand_offsets_;
};
} // namespace example
#endif // EXAMPLE_CYHAIR_LOADER_H_