mirror of
https://git.mirrors.martin98.com/https://github.com/syoyo/tinygltf.git
synced 2025-08-10 05:49:00 +08:00
Merge branch 'master' of github.com:syoyo/tinygltfloader
This commit is contained in:
commit
87dc2753da
6
examples/cyhair_to_gltf/Makefile
Normal file
6
examples/cyhair_to_gltf/Makefile
Normal 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
|
||||
|
18
examples/cyhair_to_gltf/README.md
Normal file
18
examples/cyhair_to_gltf/README.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Simple CyHair to glTF converter
|
||||
|
||||

|
||||
|
||||
Convert CyHair to curve points using glTF + extension(curve data format is described in `../alembic_to_gltf/README.md`).
|
||||
|
||||
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`).
|
||||
|
352
examples/cyhair_to_gltf/cyhair2gltf.cc
Normal file
352
examples/cyhair_to_gltf/cyhair2gltf.cc
Normal 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;
|
||||
}
|
305
examples/cyhair_to_gltf/cyhair_loader.cc
Normal file
305
examples/cyhair_to_gltf/cyhair_loader.cc
Normal 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
|
109
examples/cyhair_to_gltf/cyhair_loader.h
Normal file
109
examples/cyhair_to_gltf/cyhair_loader.h
Normal 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_
|
BIN
images/cyhair.png
Normal file
BIN
images/cyhair.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 160 KiB |
Loading…
x
Reference in New Issue
Block a user