2 goat3d - 3D scene, and animation file format library.
3 Copyright (C) 2013-2023 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
25 static struct goat3d_material *read_material(struct goat3d *g, struct json_obj *jmtl);
27 int g3dimpl_loadgltf(struct goat3d *g, struct goat3d_io *io)
32 struct json_value *jval;
33 struct json_item *jitem;
34 struct goat3d_material *mtl;
36 if(!(filebuf = malloc(4096))) {
37 goat3d_logmsg(LOG_ERROR, "goat3d_load: failed to allocate file buffer\n");
40 filesz = io->read(filebuf, 4096, io->cls);
45 for(i=0; i<filesz; i++) {
46 if(!isspace(filebuf[i])) {
47 if(filebuf[i] != '{') {
49 return -1; /* not json */
56 /* alright, it looks like json, load into memory and parse it to continue */
57 filesz = io->seek(0, SEEK_END, io->cls);
58 io->seek(0, SEEK_SET, io->cls);
59 if(!(filebuf = malloc(filesz + 1))) {
60 goat3d_logmsg(LOG_ERROR, "goat3d_load: failed to load file into memory\n");
63 if(io->read(filebuf, filesz, io->cls) != filesz) {
64 goat3d_logmsg(LOG_ERROR, "goat3d_load: EOF while reading file\n");
71 if(json_parse(&root, filebuf) == -1) {
77 /* a valid gltf file needs to have an "asset" node with a version number */
78 if(!(jval = json_lookup(&root, "asset.version"))) {
79 json_destroy_obj(&root);
83 /* read all materials */
84 if((jitem = json_find_item(&root, "materials"))) {
85 if(jitem->val.type != JSON_ARR) {
86 goat3d_logmsg(LOG_ERROR, "goat3d_load: gltf materials value is not an array!\n");
90 for(i=0; i<jitem->val.arr.size; i++) {
91 jval = jitem->val.arr.val + i;
93 if(jval->type != JSON_OBJ) {
94 goat3d_logmsg(LOG_ERROR, "goat3d_load: gltf material is not a json object!\n");
98 if((mtl = read_material(g, &jval->obj))) {
99 goat3d_add_mtl(g, mtl);
107 json_destroy_obj(&root);
111 static int jarr_to_vec(struct json_arr *jarr, float *vec)
115 if(jarr->size < 3 || jarr->size > 4) {
120 if(i >= jarr->size) {
124 if(jarr->val[i].type != JSON_NUM) {
127 vec[i] = jarr->val[i].num;
132 static int jval_to_vec(struct json_value *jval, float *vec)
134 if(jval->type != JSON_ARR) return -1;
135 return jarr_to_vec(&jval->arr, vec);
138 static struct goat3d_material *read_material(struct goat3d *g, struct json_obj *jmtl)
140 struct goat3d_material *mtl;
142 struct json_value *jval;
143 float color[4], specular[4], roughness, metal, ior;
146 if(!(mtl = malloc(sizeof *mtl)) || g3dimpl_mtl_init(mtl) == -1) {
148 goat3d_logmsg(LOG_ERROR, "read_material: failed to allocate material\n");
152 if((str = json_lookup_str(jmtl, "name", 0))) {
153 goat3d_set_mtl_name(mtl, str);
156 if((jval = json_lookup(jmtl, "pbrMetallicRoughness.baseColorFactor")) &&
157 (nelem = jval_to_vec(jval, color)) != -1) {
158 goat3d_set_mtl_attrib(mtl, "diffuse", color);
162 if((roughness = json_lookup_num(jmtl, "pbrMetallicRoughness.roughnessFactor", -1.0)) >= 0) {
163 goat3d_set_mtl_attrib1f(mtl, "roughness", roughness);
164 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_SHININESS, (1.0f - roughness) * 100.0f + 1.0f);
166 if((metal = json_lookup_num(jmtl, "pbrMetallicRoughness.metallicFactor", -1.0)) >= 0) {
167 goat3d_set_mtl_attrib1f(mtl, "metal", metal);
169 if((jval = json_lookup(jmtl, "extensions.KHR_materials_specular.specularColorFactor")) &&
170 (nelem = jval_to_vec(jval, specular)) != -1) {
171 goat3d_set_mtl_attrib(mtl, GOAT3D_MAT_ATTR_SPECULAR, specular);
173 if((ior = json_lookup_num(jmtl, "extensions.KHR_materials_ior.ior", -1.0)) >= 0) {
174 goat3d_set_mtl_attrib1f(mtl, GOAT3D_MAT_ATTR_IOR, ior);
176 /* TODO more attributes */