├── src
├── gltf_hierarchy.hda
│ ├── houdini.hdalibrary
│ ├── Object_1gltf__hierarchy
│ │ ├── Help
│ │ ├── InternalFileOptions
│ │ ├── Sections.list
│ │ ├── TypePropertiesOptions
│ │ ├── CreateScript
│ │ ├── ExtraFileOptions
│ │ ├── Tools.shelf
│ │ ├── DialogScript
│ │ └── PythonModule
│ ├── Sections.list
│ └── INDEX__SECTION
├── Makefile
├── CustomGLTF.global
├── GLTF
│ ├── Makefile
│ ├── GLTF_API.h
│ ├── GLTF_Types.C
│ ├── GLTF_Cache.h
│ ├── GLTF_Util.h
│ ├── GLTF_GeoLoader.h
│ ├── GLTF_Cache.C
│ ├── GLTF_Util.C
│ ├── GLTF_Loader.h
│ ├── GLTF_Types.h
│ └── GLTF_GeoLoader.C
├── HOM
│ ├── Makefile
│ └── HOM_GLTF.C
├── SOP
│ ├── Makefile
│ ├── SOP_GLTF.h
│ └── SOP_GLTF.C
└── ROP
│ ├── Makefile
│ ├── ROP_GLTF_Image.h
│ ├── ROP_GLTF_Refiner.h
│ ├── ROP_GLTF_ExportRoot.h
│ ├── ROP_GLTF.h
│ └── ROP_GLTF_Image.C
├── HoudiniVersion
├── doc
└── nodes
│ ├── obj
│ └── gltf_hierarchy.txt
│ ├── sop
│ └── gltf.txt
│ └── out
│ └── gltf.txt
└── README.txt
/src/gltf_hierarchy.hda/houdini.hdalibrary:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/HoudiniVersion:
--------------------------------------------------------------------------------
1 | Houdini Version: 18.0.619
2 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/Help:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Sections.list:
--------------------------------------------------------------------------------
1 | ""
2 | INDEX__SECTION INDEX_SECTION
3 | houdini.hdalibrary houdini.hdalibrary
4 | Object_1gltf__hierarchy Object/gltf_hierarchy
5 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/InternalFileOptions:
--------------------------------------------------------------------------------
1 | {
2 | "nodeconntype":{
3 | "type":"bool",
4 | "value":false
5 | },
6 | "nodeparmtype":{
7 | "type":"bool",
8 | "value":false
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/Sections.list:
--------------------------------------------------------------------------------
1 | ""
2 | DialogScript DialogScript
3 | CreateScript CreateScript
4 | TypePropertiesOptions TypePropertiesOptions
5 | Help Help
6 | Tools.shelf Tools.shelf
7 | InternalFileOptions InternalFileOptions
8 | PythonModule PythonModule
9 | ExtraFileOptions ExtraFileOptions
10 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/TypePropertiesOptions:
--------------------------------------------------------------------------------
1 | SaveSpareParms := 0;
2 | CheckExternal := 1;
3 | SaveIcon := 0;
4 | GzipContents := 1;
5 | ContentsCompressionType := 1;
6 | UnlockOnCreate := 0;
7 | SaveCachedCode := 0;
8 | LockContents := 0;
9 | MakeDefault := 1;
10 | UseDSParms := 1;
11 | ForbidOutsideParms := 1;
12 | PrefixDroppedParmLabel := 0;
13 | PrefixDroppedParmName := 0;
14 | ParmsFromVfl := 0;
15 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/INDEX__SECTION:
--------------------------------------------------------------------------------
1 | Operator: gltf_hierarchy
2 | Label: glTF Hierarchy
3 | Path: oplib:/Object/gltf_hierarchy?Object/gltf_hierarchy
4 | Icon: OBJ_gltf_hierarchy
5 | Table: Object
6 | License:
7 | Extra:
8 | User:
9 | Inputs: 0 to 0
10 | Subnet: true
11 | Python: false
12 | Empty: false
13 | Modified: Wed Jan 23 13:58:11 2019
14 |
15 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/CreateScript:
--------------------------------------------------------------------------------
1 | # Automatically generated script
2 | \set noalias = 1
3 | #
4 | # Creation script for gltf_hierarchy operator
5 | #
6 |
7 | if ( "$arg1" == "" ) then
8 | echo This script is intended as a creation script
9 | exit
10 | endif
11 |
12 | # Node $arg1 (Object/gltf_hierarchy)
13 | opexprlanguage -s hscript $arg1
14 | opuserdata -n '___Version___' -v '' $arg1
15 |
16 | opcf $arg1
17 |
18 | opcf ..
19 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/ExtraFileOptions:
--------------------------------------------------------------------------------
1 | {
2 | "PythonModule/Cursor":{
3 | "type":"intarray",
4 | "value":[595,64]
5 | },
6 | "PythonModule/IsExpr":{
7 | "type":"bool",
8 | "value":false
9 | },
10 | "PythonModule/IsPython":{
11 | "type":"bool",
12 | "value":true
13 | },
14 | "PythonModule/IsScript":{
15 | "type":"bool",
16 | "value":true
17 | },
18 | "PythonModule/Source":{
19 | "type":"string",
20 | "value":""
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Makefile:
--------------------------------------------------------------------------------
1 | # The HFS Environment variable needs to be set before calling make
2 | # Windows users should also define their MSVCDir environnment varaibale
3 |
4 | .PHONY: gltf sop rop hom all clean
5 |
6 |
7 | gltf:
8 | @$(MAKE) -C GLTF
9 |
10 | sop: gltf
11 | @$(MAKE) -C SOP
12 |
13 | rop: gltf
14 | @$(MAKE) -C ROP
15 |
16 | hom: gltf
17 | @$(MAKE) -C HOM
18 |
19 | all: gltf sop rop hom
20 |
21 | clean:
22 | @$(MAKE) -C GLTF clean
23 | @$(MAKE) -C SOP clean
24 | @$(MAKE) -C ROP clean
25 | @$(MAKE) -C HOM clean
--------------------------------------------------------------------------------
/src/CustomGLTF.global:
--------------------------------------------------------------------------------
1 | # Global include file for custom glTF library
2 | # Modify these values here to customize your glTF library
3 |
4 | # GLTF core library
5 | GLTFLIB = CustomGLTF
6 |
7 | # GLTC custom namespace
8 | GLTFNAMESPACE = GLTF_Custom
9 |
10 | # Custom Operator prefix
11 | TOKEN_PREFIX = "custom"
12 | LABEL_PREFIX = "Custom "
13 |
14 | # SOP lib name
15 | SOPLIB = SOP_CustomGLTF
16 |
17 | # ROP lib name
18 | ROPLIB = ROP_CustomGLTF
19 |
20 | # HOM lib name
21 | HOM_LIB = HOM_CustomGLTF
22 |
23 |
24 | # lib extension
25 | ifdef WINDOWS
26 | EXT = dll
27 | else ifdef MBSD
28 | EXT = dylib
29 | else
30 | EXT = so
31 | endif
--------------------------------------------------------------------------------
/src/GLTF/Makefile:
--------------------------------------------------------------------------------
1 | # The HFS Environment variable needs to be set before calling make
2 | # Windows users should also define their MSVCDir environnment variable
3 |
4 | include ../CustomGLTF.global
5 |
6 | DSONAME = lib$(GLTFLIB).$(EXT)
7 |
8 | SOURCES = \
9 | GLTF_Cache.C \
10 | GLTF_Loader.C \
11 | GLTF_GeoLoader.C \
12 | GLTF_Types.C \
13 | GLTF_Util.C
14 |
15 | INCDIRS = \
16 | -I$(HFS)/toolkit/include
17 |
18 | include $(HFS)/toolkit/makefiles/Makefile.gnu
19 |
20 | HDEFINES += \
21 | -DGLTF_EXPORTS \
22 | -DGLTF_NAMESPACE=$(GLTFNAMESPACE)
23 |
24 | ifndef WINDOWS
25 | # Additional Houdini libs
26 | LIBDIRS += -L$(HFS)/dsolib
27 | LIBS += -lHoudiniGEO -lHoudiniUT
28 | endif
29 |
30 | # Change from -bundle for core lib
31 | ifdef MBSD
32 | SHAREDFLAG = -dynamiclib
33 | endif
34 |
--------------------------------------------------------------------------------
/src/HOM/Makefile:
--------------------------------------------------------------------------------
1 | # The HFS Environment variable needs to be set before calling make
2 | # Windows users should also define their MSVCDir environnment variable
3 |
4 | include ../CustomGLTF.global
5 |
6 | DSONAME = $(HOM_LIB).$(EXT)
7 |
8 | # Custom GLTF library
9 | CUSTOM_GLTF = ".."
10 |
11 | SOURCES = HOM_GLTF.C
12 |
13 | INCDIRS = \
14 | -I$(CUSTOM_GLTF) \
15 | -I$(HFS)/toolkit/include \
16 | -I$(HFS)/python27/include
17 |
18 | ifdef WINDOWS
19 | LIBDIRS += -LIBPATH:$(CUSTOM_GLTF)/GLTF -LIBPATH:$(HFS)/python27/libs
20 | LIBS += lib$(GLTFLIB).lib
21 | else
22 | LIBDIRS += -L$(CUSTOM_GLTF)/GLTF -L$(HFS)/python27/libs
23 | LIBS += -l$(GLTFLIB)
24 | endif
25 |
26 | include $(HFS)/toolkit/makefiles/Makefile.gnu
27 |
28 | HDEFINES += \
29 | -DGLTF_EXPORTS \
30 | -DGLTF_NAMESPACE=$(GLTFNAMESPACE)
31 |
--------------------------------------------------------------------------------
/src/SOP/Makefile:
--------------------------------------------------------------------------------
1 | # The HFS Environment variable needs to be set before calling make
2 | # Windows users should also define their MSVCDir environnment variable
3 |
4 | include ../CustomGLTF.global
5 |
6 | DSONAME = $(SOPLIB).$(EXT)
7 |
8 | # Custom GLTF library
9 | CUSTOM_GLTF = ".."
10 |
11 | SOURCES = SOP_GLTF.C
12 |
13 | INCDIRS = \
14 | -I$(CUSTOM_GLTF) \
15 | -I$(HFS)/toolkit/include
16 |
17 | ifdef WINDOWS
18 | LIBDIRS += -LIBPATH:$(CUSTOM_GLTF)/GLTF
19 | LIBS += lib$(GLTFLIB).lib
20 | else
21 | LIBDIRS += -L$(CUSTOM_GLTF)/GLTF
22 | LIBS += -l$(GLTFLIB)
23 | endif
24 |
25 | include $(HFS)/toolkit/makefiles/Makefile.gnu
26 |
27 | HDEFINES += \
28 | -DGLTF_EXPORTS \
29 | -DGLTF_NAMESPACE=$(GLTFNAMESPACE) \
30 | -DCUSTOM_GLTF_TOKEN_PREFIX='$(TOKEN_PREFIX)' \
31 | -DCUSTOM_GLTF_LABEL_PREFIX='$(LABEL_PREFIX)'
32 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/Tools.shelf:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
8 |
9 |
10 | OBJ
11 |
12 |
13 | $HDA_TABLE_AND_NAME
14 |
15 | Import
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/ROP/Makefile:
--------------------------------------------------------------------------------
1 | # The HFS Environment variable needs to be set before calling make
2 | # Windows users should also define their MSVCDir environnment variable
3 |
4 | include ../CustomGLTF.global
5 |
6 | DSONAME = $(ROPLIB).$(EXT)
7 |
8 | # Custom GLTF library
9 | CUSTOM_GLTF = ".."
10 |
11 | SOURCES = \
12 | ROP_GLTF.C \
13 | ROP_GLTF_ExportRoot.C \
14 | ROP_GLTF_Image.C \
15 | ROP_GLTF_Refiner.C
16 |
17 | INCDIRS = \
18 | -I$(CUSTOM_GLTF) \
19 | -I$(HFS)/toolkit/include
20 |
21 | ifdef WINDOWS
22 | LIBDIRS += -LIBPATH:$(CUSTOM_GLTF)/GLTF
23 | LIBS += lib$(GLTFLIB).lib
24 | else
25 | LIBDIRS += -L$(CUSTOM_GLTF)/GLTF
26 | LIBS += -l$(GLTFLIB)
27 | endif
28 |
29 | include $(HFS)/toolkit/makefiles/Makefile.gnu
30 |
31 | HDEFINES += \
32 | -DGLTF_EXPORTS \
33 | -DGLTF_NAMESPACE=$(GLTFNAMESPACE) \
34 | -DCUSTOM_GLTF_TOKEN_PREFIX='$(TOKEN_PREFIX)' \
35 | -DCUSTOM_GLTF_LABEL_PREFIX='$(LABEL_PREFIX)'
36 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_API.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __GLTF_API_h__
29 | #define __GLTF_API_h__
30 |
31 | #include
32 |
33 | #ifdef GLTF_EXPORTS
34 | #define GLTF_API SYS_VISIBILITY_EXPORT
35 | #define GLTF_API_TINST SYS_VISIBILITY_EXPORT_TINST
36 | #else
37 | #define GLTF_API SYS_VISIBILITY_IMPORT
38 | #define GLTF_API_TINST SYS_VISIBILITY_IMPORT_TINST
39 | #endif
40 |
41 | #ifndef GLTF_NAMESPACE
42 | #define GLTF_NAMESPACE GLTF_Houdini
43 | #endif
44 |
45 | #endif
46 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Types.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #include "GLTF_Types.h"
29 |
30 | #include
31 |
32 | using namespace GLTF_NAMESPACE;
33 |
34 | GLTF_TRANSFORM_TYPE
35 | GLTF_Node::getTransformType() const
36 | {
37 | if (matrix.isIdentity())
38 | {
39 | return GLTF_TRANSFORM_TRS;
40 | }
41 | return GLTF_TRANSFORM_MAT4;
42 | }
43 |
44 | void
45 | GLTF_Node::getTransformAsMatrix(UT_Matrix4F &mat) const
46 | {
47 | UT_Matrix4F transform = matrix;
48 | if (transform.isIdentity())
49 | {
50 | UT_Matrix4F rotation_transform;
51 | UT_Quaternion(rotation).getTransformMatrix(rotation_transform);
52 | UT_Matrix4F trs_matrix(1);
53 |
54 | trs_matrix.scale(scale);
55 | trs_matrix = rotation_transform * trs_matrix;
56 | trs_matrix.translate(translation);
57 |
58 | transform = trs_matrix;
59 | }
60 | mat = transform;
61 | }
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Cache.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __SOP_GLTFCACHE_H__
29 | #define __SOP_GLTFCACHE_H__
30 |
31 | #include "GLTF_API.h"
32 |
33 | #include
34 | #include
35 | #include
36 |
37 | namespace GLTF_NAMESPACE
38 | {
39 |
40 | class GLTF_Loader;
41 |
42 | ///
43 | /// A singleton responsible for storing a cached GLTF_Loader.
44 | ///
45 | class GLTF_API GLTF_Cache
46 | {
47 | public:
48 | static GLTF_Cache &GetInstance();
49 |
50 | ///
51 | /// Creates a new loader with the given filepath, calls Load()
52 | /// and returns a pointer. If we were unable to load, then
53 | /// the loader is evicted and null is returned
54 | ///
55 | const UT_SharedPtr LoadLoader(const UT_StringHolder &path);
56 |
57 | // Removes the loader from the cache, does not destroy any existing
58 | // instances as loaders are UT_SharedPtr's
59 | bool EvictLoader(const UT_StringHolder &path);
60 |
61 | private:
62 | // Gets an existing loader from the cache, returns false
63 | // if the loader does not exist
64 | const UT_SharedPtr GetLoader(const UT_StringHolder &path);
65 |
66 |
67 | void AutomaticEvict();
68 |
69 | UT_Map> myLoaderMap;
70 | };
71 |
72 | } // end GLTF_NAMESPACE
73 |
74 | #endif
75 |
--------------------------------------------------------------------------------
/doc/nodes/obj/gltf_hierarchy.txt:
--------------------------------------------------------------------------------
1 | #type: node
2 | #context: obj
3 | #internal: gltf
4 | #icon: SOP/Geometry
5 |
6 | = glTF =
7 |
8 | The glTF hierarchy importer can import a glTF scene as a network of nodes or as flattened geometry.
9 | See the following link for further information about the [glTF format|https://www.khronos.org/gltf/].
10 |
11 | @parameters
12 |
13 | File Name:
14 | #id: filename
15 |
16 | The glTF file to. The loading method depends on the extension of the file loaded.
17 |
18 | Files with a .glb extension be treated as binary GLB files.
19 |
20 | Files with a .gltf extension will be loaded as normal JSON data.
21 |
22 | Asset Extraction Folder:
23 | #id: assetfolder
24 |
25 | Textures packed into a glTF binary buffer or represented as base64 data must be unpacked before usage.
26 |
27 | This field specifies the location textures are to be unpacked on disk.
28 |
29 | Scene:
30 | #id: scene
31 |
32 | Select the scene to import from the glTF object.
33 |
34 | In the absence of a scene name, the label will be based off the scene index.
35 |
36 | Build Scene:
37 | #id: buildscene
38 |
39 | Build the scene from the input parameters. If the scene was loaded previously, this will reload the scene hierarchy and geometry.
40 |
41 | Lock Geometry:
42 | #id: lockgeo
43 |
44 | Enable this option to lock any geometry nodes created during scene building.
45 |
46 | Flatten Hierarchy:
47 | #id:flattenhierarchy
48 |
49 | Flatten all nodes in the scene down to a single node containing all the
50 | geometry.
51 |
52 | Promote Point Attributes To Vertex:
53 | #id: promotepointattrs
54 |
55 | Promote all point attributes (excluding P) to vertex attributes.
56 |
57 | Points Merge Distance:
58 | #id: pointconsolidatedist
59 |
60 | When __Promote Point Attributes To Vertex__ is enabled, points within this distance to each other will be merged.
61 |
62 |
63 | == Filter Options ==
64 |
65 | Import Geometry:
66 | #id: importgeometry
67 |
68 | Enable this option to import geometry objects. When disabled, glTF meshes will be loaded as empty Geometry nodes.
69 |
70 | Import Custom Attributes:
71 | #id: importcustomattributes
72 |
73 | Enable this option to import any custom attributes from glTF primitives.
74 |
75 | Custom attributes in glTF are prefixed with an underscore _ which will be stripped during import.
76 |
77 | Import Materials:
78 | #id: importmaterials
79 |
80 | Enable this option to import glTF materials as Principled Shader materials. This also creates material assignments.
81 |
82 | If this option is disabled, materials and material assignments will not be created.
83 |
84 | Import Non-Geometry:
85 | #id: importnongeo
86 |
87 | Import nodes which do not create or transform any geometry. Disable this to remove unused nodes when the additional hierarchical data is unneeded.
88 |
89 | Import Unused Materials:
90 | #id: importunusedmaterials
91 |
92 | Import all materials available, even if they are not assigned or used by the imported scene.
93 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Util.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __SOP_GLTFUTIL_H__
29 | #define __SOP_GLTFUTIL_H__
30 |
31 | #include "GLTF_API.h"
32 | #include "GLTF_Types.h"
33 |
34 | namespace GLTF_NAMESPACE
35 | {
36 |
37 | class GLTF_API GLTF_Util
38 | {
39 | public:
40 | template
41 | static T
42 | readInterleavedElement(unsigned char *data, uint32 stride, uint32 index)
43 | {
44 | return *reinterpret_cast(data + stride * index);
45 | }
46 |
47 | static const char *typeGetName(GLTF_Type type);
48 | static GLTF_Int componentTypeGetBytes(GLTF_ComponentType type);
49 | static GLTF_Int typeGetElements(GLTF_Type type);
50 | static GLTF_Int
51 | getDefaultStride(GLTF_Type type, GLTF_ComponentType component_type);
52 |
53 | ///
54 | /// Returns 0 if previous_stride == 0 and GetDefaultStride otherwise.
55 | ///
56 | static GLTF_Int getStride(uint32 previous_stride, GLTF_Type type,
57 | GLTF_ComponentType component_type);
58 |
59 | static GLTF_Type getTypeForTupleSize(uint32 tuplesize);
60 |
61 | ///
62 | /// Returns a list of the scene names in the given filename,
63 | /// where the index in the returned array corrosponds to the
64 | /// scene index, and the value corrosponds to the name if one
65 | /// exists, and "" othewise.
66 | ///
67 | static UT_Array getSceneList(const UT_String &filename);
68 |
69 | static bool
70 | DecomposeMatrixToTRS(const UT_Matrix4F &mat, UT_Vector3F &translation,
71 | UT_Quaternion &rotation, UT_Vector3F scale);
72 | };
73 |
74 | } // end GLTF_NAMESPACE
75 |
76 | #endif
77 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_GeoLoader.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __SOP_GLTFGEOLOADER_H__
29 | #define __SOP_GLTFGEOLOADER_H__
30 |
31 | #include "GLTF_API.h"
32 |
33 | #include
34 |
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | // Forward declarations
41 | class GU_Detail;
42 | class UT_String;
43 |
44 | namespace GLTF_NAMESPACE
45 | {
46 |
47 | class GLTF_Loader;
48 |
49 | struct GLTF_API GLTF_MeshLoadingOptions
50 | {
51 | bool loadCustomAttribs = true;
52 | bool promotePointAttribs = true;
53 | bool consolidatePoints = true;
54 | fpreal pointConsolidationDistance = 0.0001F;
55 | };
56 |
57 | class GLTF_API GLTF_GeoLoader
58 | {
59 | public:
60 | GLTF_GeoLoader(const GLTF_Loader &loader, GLTF_Handle mesh_idx,
61 | GLTF_Handle primitive_idx,
62 | const GLTF_MeshLoadingOptions& options = {});
63 |
64 | bool loadIntoDetail(GU_Detail &detail);
65 |
66 | static bool load(const GLTF_Loader &loader, GLTF_Handle mesh_idx,
67 | GLTF_Handle primitive_idx, GU_Detail &detail,
68 | const GLTF_MeshLoadingOptions options = {});
69 |
70 | private:
71 | bool
72 | LoadVerticesAndPoints(GU_Detail &detail,
73 | const GLTF_MeshLoadingOptions &options,
74 | const GLTF_Accessor &pos, const GLTF_Accessor &ind);
75 |
76 | bool
77 | LoadVerticesAndPointsNonIndexed(GU_Detail &detail, const GLTF_Accessor &pos);
78 |
79 | bool
80 | AddPointAttribute(GU_Detail &detail, const UT_StringHolder &attrib_name,
81 | const GLTF_Accessor &accessor);
82 |
83 | // const uint32 myRootNode;
84 | const GLTF_Handle myMeshIdx;
85 | const GLTF_Handle myPrimIdx;
86 | const GLTF_Loader &myLoader;
87 | const GLTF_MeshLoadingOptions myOptions;
88 | };
89 |
90 | } // end GLTF_NAMESPACE
91 |
92 | #endif
93 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Cache.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 | #include "GLTF_Cache.h"
28 | #include "GLTF_Loader.h"
29 |
30 | using namespace GLTF_NAMESPACE;
31 |
32 | static UT_Lock theThreadLock;
33 | const static exint MAX_CACHE_FILES = 5;
34 |
35 | GLTF_Cache&
36 | GLTF_Cache::GetInstance()
37 | {
38 | static GLTF_Cache theCache;
39 |
40 | return(theCache);
41 | }
42 |
43 | const UT_SharedPtr
44 | GLTF_Cache::GetLoader(const UT_StringHolder &path)
45 | {
46 | UT_AutoLock lock(theThreadLock);
47 |
48 | auto loader = myLoaderMap.find(path);
49 | if (loader == myLoaderMap.end())
50 | {
51 | return UT_SharedPtr(nullptr);
52 | }
53 | return loader->second;
54 | }
55 |
56 | void
57 | GLTF_Cache::AutomaticEvict()
58 | {
59 | UT_AutoLock lock(theThreadLock);
60 |
61 | if (myLoaderMap.size() == 0)
62 | return;
63 |
64 | // todo: replace this with a priority queue for LRU eviction
65 | myLoaderMap.erase(myLoaderMap.begin());
66 | }
67 |
68 | bool GLTF_Cache::EvictLoader(const UT_StringHolder& path)
69 | {
70 | UT_AutoLock lock(theThreadLock);
71 |
72 | return (myLoaderMap.erase(path) > 0);
73 | }
74 |
75 | const UT_SharedPtr
76 | GLTF_Cache::LoadLoader(const UT_StringHolder &path)
77 | {
78 | UT_AutoLock lock(theThreadLock);
79 |
80 | auto loader = GetLoader(path);
81 | if (!loader)
82 | {
83 | if (myLoaderMap.size() > MAX_CACHE_FILES)
84 | {
85 | AutomaticEvict();
86 | }
87 |
88 | auto new_loader = UT_SharedPtr(new GLTF_Loader(UT_String(path)));
89 |
90 | // If loading fails, then return an empty object
91 | if (!new_loader->Load())
92 | {
93 | return UT_SharedPtr(nullptr);
94 | }
95 | else
96 | {
97 | myLoaderMap.emplace(path, loader);
98 | }
99 |
100 | return new_loader;
101 | }
102 |
103 | return loader;
104 | }
105 |
--------------------------------------------------------------------------------
/doc/nodes/sop/gltf.txt:
--------------------------------------------------------------------------------
1 | #type: node
2 | #context: out
3 | #internal: gltf
4 | #icon: SOP/Geometry
5 |
6 | = glTF =
7 |
8 | The glTF SOP can load geometry and flattened hierarchy data from a glTF file.
9 | See the following link for further information about the [glTF format|https://www.khronos.org/gltf/].
10 |
11 | @parameters
12 |
13 | File Name:
14 | #id: filename
15 |
16 | Load this glTF file. The loading method depends on the extension of the file loaded.
17 |
18 | Files with a `.glb` extension are treated as binary GLB files.
19 |
20 | Files with a `.gltf` extension are treated as normal JSON data.
21 |
22 | Load By:
23 | #id: loadby
24 |
25 | Use this type of glTF object to use as the root of the imported data.
26 |
27 | Primitive:
28 | Load the geometry from the selected primitive. Transforms or names are not preserved.
29 |
30 | Node:
31 | Load the geometry from the selected node. Hierarchy, names, and transforms are represented as packed primitives.
32 |
33 | Scene:
34 | Load the geometry from the selected scene. Hierarchy, names, and transforms are represented as packed primitives.
35 |
36 | Mesh ID:
37 | #id: meshid
38 |
39 | Load geometry data from the mesh at this index of the glTF mesh array.
40 |
41 | Primitive Index:
42 | #id: primitiveIndex
43 |
44 | Load geometry data from the primitive at this index of the mesh's primitive array.
45 |
46 | Root Node:
47 | #id: nodeid
48 |
49 | Load the node at this index of the glTF node array. Additionally, load all descendent nodes.
50 |
51 | Scene:
52 | #id: scene
53 |
54 | Load all nodes in the scene at this index of the glTF scene array.
55 |
56 | Geometry Type:
57 | #id: geotype
58 |
59 | Store glTF geometry data in this type of object.
60 |
61 | Flattened Geometry:
62 | Store the geometry as flat Houdini geometry. Transforms and hierarchy are baked in and not stored. Names are represented by primitive attributes and are only stored for meshes, not internal nodes.
63 |
64 | Packed Primitive:
65 | Load the geometry as a hierarchy of packed primitives. Hierarchy, names, and transforms are represented as attributes on the packed primitives.
66 |
67 | Promote Point Attributes To Vertex:
68 | #id: promotepointattrs
69 |
70 | Promote all point attributes (excluding P) to vertex attributes.
71 |
72 | Points Merge Distance:
73 | #id: pointconsolidatedist
74 |
75 | When __Promote Point Attributes To Vertex__ is enabled, points within this distance to each other will be merged.
76 |
77 | Import Custom Attributes:
78 | #id: usecustomattribs
79 |
80 | Enable this option to load all custom attributes from imported primitives.
81 |
82 | Custom attributes in glTF are prefixed with an underscore `_`, which will be stripped during the import process.
83 |
84 | Import Names:
85 | #id: loadnames
86 |
87 | Assigns node, scene, and mesh names as attributes on the imported geometry.
88 |
89 | The scene name is assigned to the `scene_name` detail attribute.
90 |
91 | Node and mesh names are assigned to the `name` attribute on imported packed primitives.
92 |
93 | Import Material Assignments:
94 | #id: materialassignments
95 |
96 | Assigns materials as primitive attributes to the imported geometry.
97 |
98 | Assignments point to the `../../materials/` path.
99 |
100 | If the material name is unique, the assigned material name will simply be the name of the glTF material.
101 | When the name is non_unique, the name will be of the format `Material_i`, where `i` is the index of the material in the glTF global materials array. If the material is nameless, the name will simply be `_i`, where i is again the index.
102 |
--------------------------------------------------------------------------------
/doc/nodes/out/gltf.txt:
--------------------------------------------------------------------------------
1 | #type: node
2 | #context: out
3 | #internal: gltf
4 | #icon: SOP/Geometry
5 |
6 | = glTF =
7 |
8 | The glTF ROP can export your scene as a GLTF file.
9 | See the following link for further information about the [glTF format|https://www.khronos.org/gltf/].
10 |
11 | @parameters
12 |
13 | Render to Disk:
14 | #id: execute
15 |
16 | Begins the render with the last render control settings.
17 |
18 | Controls...:
19 | #id: renderdialog
20 |
21 | Opens the render control dialog to allow adjustments of the render parameters before rendering.
22 |
23 | File Name:
24 | #id: file
25 |
26 | The path of the file to generate. The extension determines the type of file to generate.
27 |
28 | A path with a `.glb` extension generates a single binary GLB file and stores all resources internally.
29 |
30 | A path with a `.gltf` extension may store some resources externally. Exported resources are stored in the same folder as the file and are referenced relatively.
31 |
32 | Texture Format:
33 | #id: format
34 |
35 | Save all images and textures using this file format.
36 |
37 | Max Texture Resolution:
38 | #id: maxresolution
39 |
40 | If any texture is larger than this resolution then downsize it to this resolution. Useful for quickly reducing file size.
41 |
42 | Texture Quality:
43 | #id: imagequality
44 |
45 | The level of compression to apply to outputted textures. Only applies to lossy image formats.
46 |
47 |
48 | Root Object:
49 | #id: root
50 |
51 | The root object of the scene. All objects contained under this node will be included in the glTF scene.
52 |
53 | Objects:
54 | #id: objects
55 |
56 | A pattern/bundle of objects to include in the glTF scene.
57 |
58 | Flip Normal Map Y:
59 | #id: flipnormalmapy
60 |
61 | Invert the y-coordinate on the normal texture map for each exported mesh. glTF's orientation expects an upwards
62 | facing y-axis.
63 |
64 | Save All Non-Displayed (Hidden) Objects:
65 | #id: savehidden
66 |
67 | If this checkbox is turned on, hidden objects matching the __Objects__ pattern/bundle will also written to the glTF scene.
68 |
69 | Export Custom Attributes:
70 | #id: exportcustom
71 |
72 | If this checkbox is turned off, only attributes defined in the glTF standard are exported.
73 |
74 | Otherwise, all public attributes are exported.
75 |
76 | Attributes are exported with their original types when possible. However, glTF allows a maximum of 32 bits for floats and integers. Integers are also required to be unsigned.
77 |
78 | Attempting to export a datatypes larger than this will narrow the type and print a warning.
79 |
80 | Exported custom attribute names are the original names plus an _ (underscore) prefix.
81 |
82 | As glTF only supports point attributes, name collisions will be resolved using the normal attribute resolution order.
83 |
84 | Export Node Names:
85 | #id: exportnames
86 |
87 | This assigns Houdini node names to the name field of glTF objects.
88 |
89 | In addition, the name attribute on packed primitives will be exported.
90 |
91 | Names are assigned to materials, transformation nodes and geometry.
92 |
93 | Export Materials:
94 | #id: exportmaterials
95 |
96 | Enabling this option exports Principled Shader materials.
97 |
98 | Materials are exported as a combination of emissive maps, normal maps, and PBRMetallicRoughness materials.
99 |
100 | All associated textures and images used by the material are exported alongside the material.
101 |
102 | Some Principled Shader options such as ambient occlusion and subsurface scattering are not exported.
103 |
104 | Materials that do not use the Principled Shader will be ignored.
105 |
106 | Cull Empty Nodes:
107 | #id: cullempty
108 |
109 | Enabling this will cause any Object node which has no descendents containing geometry to be ignored.
110 |
111 | Rescale Texture as Power of Two:
112 | #id: poweroftwo
113 |
114 | This will rescale exported images so that the final image has dimensions which are powers of two (eg. 512x1024, 2048x2048).
115 |
116 | This is required by some low-spec glTF implementations as well as WebGL implementations in older browsers.
117 |
--------------------------------------------------------------------------------
/src/HOM/HOM_GLTF.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #include
29 | // This file contains functions that will run arbitrary Python code
30 | #include
31 | #include
32 |
33 | #include
34 | #include
35 |
36 | #include
37 | #include
38 |
39 | static const char *Doc_GLTFClearCache = "Doc_GLTFClearCache(gltfPath)\n";
40 |
41 | using namespace GLTF_NAMESPACE;
42 |
43 | static PY_PyObject *
44 | Py_ClearGLTFCache(PY_PyObject *self, PY_PyObject *file)
45 | {
46 | const char *file_str;
47 | if (!PY_PyArg_ParseTuple(file, "s", &file_str))
48 | {
49 | PY_Py_RETURN_NONE;
50 | }
51 |
52 | GLTF_Cache::GetInstance().EvictLoader(file_str);
53 | PY_Py_RETURN_NONE;
54 | }
55 |
56 | static const char *Doc_GLTFGetSceneList = "Doc_GLTFGetSceneNames(filename)\n"
57 | "\n";
58 |
59 | PY_PyObject *
60 | Py_GLTFGetSceneList(PY_PyObject *self, PY_PyObject *file)
61 | {
62 | const char *file_str;
63 | if (!PY_PyArg_ParseTuple(file, "s", &file_str))
64 | {
65 | PY_Py_RETURN_NONE;
66 | }
67 |
68 | UT_Array scene_list = GLTF_Util::getSceneList(file_str);
69 |
70 | exint num_scenes = scene_list.size();
71 | PY_PyObject *result = PY_PyList_New(num_scenes * 2);
72 |
73 | for (exint i = 0; i < num_scenes; ++i)
74 | {
75 | std::string scene_name = scene_list[i].c_str();
76 | if (scene_name == "")
77 | {
78 | scene_name = "Scene " + std::to_string(i + 1);
79 | }
80 |
81 | std::string index_string = std::to_string(i);
82 | PY_PyObject *pyname = PY_PyString_FromString(scene_name.c_str());
83 | PY_PyObject *py_scene_idx = PY_PyString_FromString(index_string.c_str());
84 | PY_PyList_SetItem(result, 2 * i, py_scene_idx);
85 | PY_PyList_SetItem(result, 2 * i + 1, pyname);
86 | }
87 |
88 | return result;
89 | }
90 |
91 | void
92 | HOMextendLibrary()
93 | {
94 | {
95 | // A PY_InterpreterAutoLock will grab the Python global interpreter
96 | // lock (GIL). It's important that we have the GIL before making
97 | // any calls into the Python API.
98 | PY_InterpreterAutoLock interpreter_auto_lock;
99 |
100 | static PY_PyMethodDef gltf_hom_extension_methods[] = {
101 | {"gltfClearCache", Py_ClearGLTFCache, PY_METH_VARARGS(),
102 | Doc_GLTFClearCache},
103 |
104 | {"gltfGetSceneList", Py_GLTFGetSceneList, PY_METH_VARARGS(),
105 | Doc_GLTFGetSceneList},
106 |
107 | {NULL, NULL, 0, NULL}};
108 |
109 | PY_Py_InitModule("_gltf_hom_extensions", gltf_hom_extension_methods);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/ROP/ROP_GLTF_Image.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #ifndef __ROP_GLTF_IMAGE_h__
27 | #define __ROP_GLTF_IMAGE_h__
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | class IMG_Format;
38 | class COP2_Node;
39 | class UT_String;
40 | class PXL_Raster;
41 | class IMG_Stat;
42 | class ROP_GLTF_BaseErrorManager;
43 |
44 | struct ROP_GLTF_ChannelMapping
45 | {
46 | // This implemented so we can obtain a lexiographic ordering
47 | // of ChannelMaps in order to cache them in a map
48 | bool operator<(const ROP_GLTF_ChannelMapping &r) const;
49 |
50 | UT_StringHolder path;
51 | exint from_channel;
52 | exint to_channel;
53 | };
54 |
55 | struct ROP_GLTF_ImgExportParms
56 | {
57 | bool roundUpPowerOfTwo = false;
58 | // 0 indicates that there is no max raster size
59 | exint maxRasterSize = 0;
60 | exint quality = 90;
61 | exint max_res = 0;
62 | bool flipGreen = false;
63 | };
64 |
65 | ///
66 | /// Utility functions for importing, exporting and manipulating images in
67 | /// the context of GLTF textures.
68 | ///
69 |
70 | class ROP_GLTF_Image
71 | {
72 | public:
73 | ///
74 | /// Takes a list of images and associated channels, packs them into
75 | /// a single image file, preprocesses the image and outputs it to &os
76 | ///
77 | static bool
78 | CreateMappedTexture(const UT_Array &mappings,
79 | std::ostream &os, const IMG_Format *format, uint32 time,
80 | const ROP_GLTF_ImgExportParms &parms,
81 | ROP_GLTF_BaseErrorManager &errormgr);
82 |
83 | ///
84 | /// Converts the file format for the given images, processes it for GLTF
85 | /// and outputs it to the output stream &os.
86 | ///
87 | static bool OutputImage(const UT_String &filename, const IMG_Format *format,
88 | std::ostream &os, fpreal time,
89 | const ROP_GLTF_ImgExportParms &parms,
90 | ROP_GLTF_BaseErrorManager &errormgr);
91 |
92 | private:
93 | //
94 | // Gets the image rasters from the given File or COP. Any methods
95 | // with "include_alpha" will return RGBA if the flag is on, or otherwise
96 | // RGB. This is to avoid handling other packings.
97 | //
98 | static bool
99 | GetImageRasters(const UT_StringHolder &filename, const uint32 time,
100 | UT_Array> &rasters,
101 | IMG_Stat &stat, bool include_alpha);
102 |
103 | static exint NextPowerOfTwo(exint num);
104 |
105 | static bool
106 | OutputCopToStream(COP2_Node *node, const IMG_Format *format,
107 | std::ostream &os, fpreal time,
108 | const ROP_GLTF_ImgExportParms &parms);
109 |
110 | static bool
111 | OutputImageToStream(const UT_String &filename,
112 | const IMG_Format *file_format, std::ostream &os,
113 | fpreal time, const ROP_GLTF_ImgExportParms &parms);
114 |
115 | static void
116 | ApplyTransformations(IMG_Stat &stat,
117 | UT_Array> &rasters,
118 | const ROP_GLTF_ImgExportParms &parms);
119 |
120 | static bool
121 | GetImageRastersFromFile(const UT_StringHolder &filename, const uint32 time,
122 | UT_Array> &rasters,
123 | IMG_Stat &stat, bool include_alpha);
124 |
125 | static bool
126 | GetImageRastersFromCOP(COP2_Node *node, const uint32 time,
127 | UT_Array> &rasters,
128 | IMG_Stat &stat, bool include_alpha);
129 | };
130 |
131 | #endif
132 |
--------------------------------------------------------------------------------
/README.txt:
--------------------------------------------------------------------------------
1 | This contains the source code for the glTF importer and exporter for Houdini.
2 |
3 | This project has been setup to produce custom glTF libraries with its own
4 | namespace and node operator names so that it can be used alongside the default
5 | Houdini glTF libarries.
6 |
7 | Optionally, you can modify the custom library namespace and node operator names
8 | in src/CustomGLTF.global.
9 |
10 | This project has been setup to build with make. See
11 | https://www.sidefx.com/docs/hdk/_h_d_k__intro__compiling.html#HDK_Intro_Compiling_Makefiles
12 |
13 | For coding reference, see the Houdini Development Kit: https://www.sidefx.com/docs/hdk/
14 |
15 | Requires Houdini 18.0.552 or newer. Does not work with earlier versions of Houdini.
16 |
17 | -------------------------------------------------------------------------------
18 | Libraries:
19 | -------------------------------------------------------------------------------
20 |
21 | - src/GLTF
22 | Source code for the core glTF import and export library which contains most
23 | of the parsing and writing code. The generated libCustomGLTF.so/.dll should be
24 | placed in a path pointed to by LD_LIBRARY_PATH (Linux), or DYLD_LIBRARY_PATH
25 | (macOS) or PATH (Windows).
26 |
27 | - src/SOP
28 | Source code for the glTF SOP importer node. The generated SOP_CustomGLTF.so/.dll
29 | should be placed in $HOME/houdiniX.X/dso or a path pointed to by HOUDINI_DSO_PATH.
30 |
31 | - src/ROP
32 | Source code for the glTF ROP exporter node. The generated ROP_CustomGLTF.so/.dll
33 | should be placed in $HOME/houdiniX.X/dso or a path pointed to by HOUDINI_DSO_PATH.
34 |
35 | - src/HOM
36 | Source code for the glTF HOM module. The generated HOM_CustomGLTF.so/.dll
37 | should be placed in $HOME/houdiniX.X/dso or a path pointed to by HOUDINI_DSO_PATH.
38 |
39 | - src/gltf_hierarchy.hda
40 | The Object node to load in a glTF scene hierarchy, as a Houdini Digital Asset.
41 | This should be placed $HOME/houdiniX.X/otls.
42 |
43 | -------------------------------------------------------------------------------
44 | Building the glTF libraries on Windows using Cygwin:
45 | -------------------------------------------------------------------------------
46 |
47 | To build the glTF libraries on Windows the HFS environment variable needs to be
48 | set to the Houdini install directory, and the MSVCDir environment must be set
49 | to the VC subdirectory of your Visual Studio installation.
50 |
51 | To do so, first open a cygwin shell and go to the Houdini X.Y.ZZZ directory and
52 | source the Houdini environment:
53 |
54 | cd C:/Program\ Files/Side\ Effects\ Software/Houdini\ X.Y.ZZZ
55 | source ./houdini_setup
56 |
57 | Then set the MSVC dir (change to your version of MSVC).
58 | export MSVCDir="C:/Program Files (x86)/Microsoft Visual Studio/2017/Professional/VC/Tools/MSVC/14.16.27023"
59 |
60 | The following steps assume you cloned this repo to C:\HoudiniGLTF
61 |
62 | Go to the HoudiniGLTF\src folder. Then do one of the following:
63 |
64 | To build all libs: make WINDOWS=1 all
65 |
66 | Note that libCustomGLTF.dll must be built first. The other libs depend on it.
67 |
68 | Once the libraries are built, add the location of libCustomGLTF.dll to PATH:
69 |
70 | set PATH=%PATH%;C:\HoudiniGLTF\src\GLTF
71 |
72 | Then copy over the built SOP, ROP, and HOM .dlls to %HOME%/houdiniX.X/dso
73 | (i.e. Houdini home directory).
74 |
75 | Run Houdini and you should see the Custom GLTF SOP and ROP nodes.
76 |
77 | Notes:
78 |
79 | To build a specific lib (such as libCustomGLTF.dll): make WINDOWS=1 gltf
80 |
81 | To clean the built files: make WINDOWS=1 clean
82 |
83 | Troubleshooting:
84 |
85 | If you get compiler errors about Windows libraries such as 'corecrt.h', change
86 | the WIN32_KIT_VERSION entry in Houdini 18.0.532\toolkit\makefiles\Makefile.win
87 | to the one installed on your system in C:\Program Files (x86)\Windows Kits\10\Lib.
88 | For example: WIN32_KIT_VERSION = 10.0.17763.0
89 |
90 | If either the SOP or ROP libraries are not loading or the custom nodes are not
91 | showing up, set HOUDINI_DSO_ERROR=3 environment variable, and launch Houdini.
92 | There should be log entries when these libraries are loaded.
93 |
94 | -------------------------------------------------------------------------------
95 | Building the glTF libraries on Linux or macOS:
96 | -------------------------------------------------------------------------------
97 |
98 | First, make sure the HFS environment variable is properly set to your Houdini
99 | install directory.
100 |
101 | Linux: export HFS=/opt/hfsX.Y.ZZZ
102 | macOS: export HFS=/Applications/Houdini/HoudiniX.Y.ZZZ/Frameworks/Houdini.framework/Versions/X.Y/Resources
103 |
104 | The following steps assume you closed this repo to /dev/HoudiniGLTF
105 |
106 | Go to the HoudiniGLTF/src/ folder. And execute the following:
107 |
108 | Linux: make all
109 | macOS: make MBSD=1 all
110 |
111 | Note that libCustomGLTF.so/.dylib must be built first. The other libs depend on it.
112 |
113 | Once the libraries are built, add the location of libCustomGLTF.so/.dylib to LD_LIBRARY_PATH:
114 |
115 | Linux: export LD_LIBRARY_PATH=/dev/HoudiniGLTF/src/GLTF
116 | macOS: export DYLD_LIBRARY_PATH=/dev/HoudiniGLTF/src/GLTF
117 |
118 | Then copy over the built SOP, ROP, and HOM .so/.dylib libs to $HOME/houdiniX.X/dso
119 | (i.e. Houdini home directory).
120 |
121 | On macOS, Houdini home directory is located at ~/Library/Preferences/houdini/X.Y/
122 |
123 | If either the SOP or ROP libraries are not loading or the custom nodes are not
124 | showing up, set HOUDINI_DSO_ERROR=3 environment variable, and launch Houdini.
125 | There should be log entries when these libraries are loaded.
--------------------------------------------------------------------------------
/src/SOP/SOP_GLTF.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __SOP_GLTF_H__
29 | #define __SOP_GLTF_H__
30 |
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 |
37 | #include
38 | #include
39 |
40 | class GU_Detail;
41 | class GU_PrimPacked;
42 |
43 | enum GLTF_LoadStyle
44 | {
45 | Scene,
46 | Node,
47 | Mesh,
48 | Primitive
49 | };
50 |
51 | enum GLTF_GeoType
52 | {
53 | Houdini_Geo,
54 | Packed_Primitives
55 | };
56 |
57 | typedef GLTF_NAMESPACE::GLTF_Int GLTF_Int;
58 | typedef GLTF_NAMESPACE::GLTF_Handle GLTF_Handle;
59 | typedef GLTF_NAMESPACE::GLTF_Node GLTF_Node;
60 |
61 | /// SOP to read GLTF geometry
62 | class SOP_GLTF : public SOP_Node
63 | {
64 | public:
65 | // Standard hdk declarations
66 | static OP_Node *
67 | myConstructor(OP_Network *net, const char *name, OP_Operator *entry);
68 |
69 | static PRM_Template myTemplateList[];
70 | static void installSOP(OP_OperatorTable *table);
71 |
72 | // Returns a an array of pairs, where the index corresponds to the
73 | // index of the mesh, the first item in the pair is the name of
74 | // the mesh, and the second is the number of primitives
75 | const UT_Array> &getMeshNames() const;
76 | const UT_Array &getNodeNames() const;
77 | const UT_Array &getSceneNames() const;
78 |
79 | protected:
80 | SOP_GLTF(OP_Network *net, const char *name, OP_Operator *op);
81 | virtual ~SOP_GLTF();
82 |
83 | virtual bool updateParmsFlags() override;
84 | virtual OP_ERROR cookMySop(OP_Context &context) override;
85 |
86 | virtual void getDescriptiveParmName(UT_String &name) const override
87 | {
88 | name = "filename";
89 | }
90 |
91 | private:
92 | void saveMeshNames(const GLTF_NAMESPACE::GLTF_Loader &loader);
93 |
94 | class Parms
95 | {
96 | public:
97 | Parms();
98 |
99 | UT_String myFileName;
100 | GLTF_LoadStyle myLoadStyle;
101 | GLTF_GeoType myGeoType;
102 | GLTF_Handle myMeshID;
103 | GLTF_Handle myPrimIndex;
104 | uint32 myUseCustomAttribs;
105 | GLTF_Handle myRootNode;
106 | GLTF_Handle myScene;
107 | uint32 myLoadNames;
108 | uint32 myLoadMats;
109 | uint32 myPromotePointAttrsToVertex;
110 | fpreal myPointConsolidationDistance;
111 | };
112 |
113 | void evaluateParms(Parms &parms, OP_Context &context);
114 |
115 | UT_Array myNodes;
116 | // The pair consists of
117 | UT_Array> myMeshes;
118 | UT_Array myScenes;
119 | };
120 |
121 | class SOP_GLTF_Loader
122 | {
123 | public:
124 | struct Options
125 | {
126 | Options() = default;
127 | ~Options() = default;
128 | bool loadNames = false;
129 | bool flatten = false;
130 | bool loadMats = false;
131 | bool loadCustomAttribs = false;
132 | UT_String material_path;
133 | bool promotePointAttribs = true;
134 | bool consolidateByMesh = true;
135 | fpreal pointConsolidationDistance = 0.0001F;
136 | };
137 |
138 | SOP_GLTF_Loader(const GLTF_NAMESPACE::GLTF_Loader &loader, GU_Detail *detail,
139 | Options options);
140 |
141 | void loadMesh(const GLTF_Handle mesh_idx);
142 | void loadNode(const GLTF_Node &node);
143 | void loadScene(GLTF_Handle scene_idx);
144 | bool loadPrimitive(GLTF_Handle node_idx, GLTF_Handle prim_idx);
145 |
146 | private:
147 | void getMaterialPath(GLTF_Int index, UT_String &path);
148 |
149 | // Puts the current node in parent_gd as a packed primitive
150 | // with the name as well as transforms
151 | void loadNodeRecursive(const GLTF_Node &node, GU_Detail *parent_gd,
152 | UT_Matrix4F cum_xform);
153 |
154 | void createAndSetName(GU_Detail *detail, const char *name) const;
155 | GLTF_NAMESPACE::GLTF_MeshLoadingOptions getGeoOptions() const;
156 |
157 | const GLTF_NAMESPACE::GLTF_Loader &myLoader;
158 | GU_Detail *myDetail;
159 | const Options myOptions;
160 | };
161 |
162 | #endif
163 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Util.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #include "GLTF_Cache.h"
29 | #include "GLTF_Loader.h"
30 | #include "GLTF_Util.h"
31 |
32 | #include
33 | #include
34 |
35 | using namespace GLTF_NAMESPACE;
36 |
37 | const char GLTF_API *
38 | GLTF_Util::typeGetName(GLTF_Type type)
39 | {
40 | switch (type)
41 | {
42 | case GLTF_Type::GLTF_TYPE_SCALAR:
43 | return "SCALAR";
44 | case GLTF_Type::GLTF_TYPE_MAT2:
45 | return "MAT2";
46 | case GLTF_Type::GLTF_TYPE_MAT3:
47 | return "MAT3";
48 | case GLTF_Type::GLTF_TYPE_MAT4:
49 | return "MAT4";
50 | case GLTF_Type::GLTF_TYPE_VEC2:
51 | return "VEC2";
52 | case GLTF_Type::GLTF_TYPE_VEC3:
53 | return "VEC3";
54 | case GLTF_Type::GLTF_TYPE_VEC4:
55 | return "VEC4";
56 | case GLTF_Type::GLTF_TYPE_INVALID:
57 | return nullptr;
58 | }
59 |
60 | return "";
61 | }
62 |
63 | GLTF_Int
64 | GLTF_Util::componentTypeGetBytes(GLTF_ComponentType type)
65 | {
66 | switch (type)
67 | {
68 | case GLTF_ComponentType::GLTF_COMPONENT_BYTE:
69 | case GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_BYTE:
70 | return 1;
71 | case GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_SHORT:
72 | case GLTF_ComponentType::GLTF_COMPONENT_SHORT:
73 | return 2;
74 | case GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_INT:
75 | case GLTF_ComponentType::GLTF_COMPONENT_FLOAT:
76 | return 4;
77 | case GLTF_ComponentType::GLTF_COMPONENT_INVALID:
78 | return 0;
79 | default:
80 | return 0;
81 | }
82 | }
83 |
84 | GLTF_Int
85 | GLTF_Util::typeGetElements(GLTF_Type type)
86 | {
87 | switch (type)
88 | {
89 | case GLTF_Type::GLTF_TYPE_SCALAR:
90 | return 1;
91 | case GLTF_Type::GLTF_TYPE_VEC2:
92 | return 2;
93 | case GLTF_Type::GLTF_TYPE_VEC3:
94 | return 3;
95 | case GLTF_Type::GLTF_TYPE_VEC4:
96 | case GLTF_Type::GLTF_TYPE_MAT2:
97 | return 4;
98 | case GLTF_Type::GLTF_TYPE_MAT3:
99 | return 9;
100 | case GLTF_Type::GLTF_TYPE_MAT4:
101 | return 16;
102 | default:
103 | return 0;
104 | }
105 | }
106 |
107 | GLTF_Int
108 | GLTF_Util::getDefaultStride(GLTF_Type type,
109 | GLTF_ComponentType component_type)
110 | {
111 | return componentTypeGetBytes(component_type) *
112 | typeGetElements(type);
113 | }
114 |
115 | GLTF_Int
116 | GLTF_Util::getStride(uint32 previous_stride, GLTF_Type type,
117 | GLTF_ComponentType component_type)
118 | {
119 | if (previous_stride != 0)
120 | {
121 | return previous_stride;
122 | }
123 | return componentTypeGetBytes(component_type) *
124 | typeGetElements(type);
125 | }
126 |
127 | GLTF_Type
128 | GLTF_Util::getTypeForTupleSize(uint32 tuplesize)
129 | {
130 | switch (tuplesize)
131 | {
132 | case 1:
133 | return GLTF_Type::GLTF_TYPE_SCALAR;
134 | case 2:
135 | return GLTF_Type::GLTF_TYPE_VEC2;
136 | case 3:
137 | return GLTF_Type::GLTF_TYPE_VEC3;
138 | case 4:
139 | return GLTF_Type::GLTF_TYPE_VEC4;
140 | default:
141 | return GLTF_Type::GLTF_TYPE_INVALID;
142 | }
143 | }
144 |
145 | UT_Array
146 | GLTF_Util::getSceneList(const UT_String &filename)
147 | {
148 | UT_Array object_paths;
149 |
150 | auto loader = GLTF_Cache::GetInstance().LoadLoader(filename);
151 | if (!loader)
152 | return object_paths;
153 |
154 | for (const GLTF_Scene *scene : loader->getScenes())
155 | {
156 | object_paths.append(UT_String(UT_String::ALWAYS_DEEP, scene->name));
157 | }
158 |
159 | return object_paths;
160 | }
161 |
162 | bool
163 | GLTF_Util::DecomposeMatrixToTRS(const UT_Matrix4F &mat,
164 | UT_Vector3F &translation,
165 | UT_Quaternion &rotation, UT_Vector3F scale)
166 | {
167 | if (mat.determinant() != 0.0)
168 | {
169 | return false;
170 | }
171 |
172 | UT_Vector3D translationD;
173 | UT_Vector3D scaleD;
174 | UT_Vector3D euler_rotation;
175 | UT_Vector3D shears;
176 | UT_XformOrder rotorder;
177 |
178 | mat.explode(UT_XformOrder::TRS, euler_rotation, scaleD, translationD,
179 | shears);
180 |
181 | const fpreal64 EPSILON = 0.000001;
182 | if (shears.length() > EPSILON)
183 | {
184 | return false;
185 | }
186 |
187 | translation = translationD;
188 | rotation.updateFromEuler(euler_rotation, rotorder);
189 | scale = scaleD;
190 |
191 | return true;
192 | }
193 |
--------------------------------------------------------------------------------
/src/ROP/ROP_GLTF_Refiner.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #ifndef __ROP_GLTF_REFINER_h__
27 | #define __ROP_GLTF_REFINER_h__
28 |
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include
36 |
37 | typedef GLTF_NAMESPACE::GLTF_Primitive GLTF_Primitive;
38 | typedef GLTF_NAMESPACE::GLTF_Mesh GLTF_Mesh;
39 | typedef GLTF_NAMESPACE::GLTF_ComponentType GLTF_ComponentType;
40 | typedef GLTF_NAMESPACE::GLTF_BufferViewTarget GLTF_BufferViewTarget;
41 |
42 | class GT_PrimPolygonMesh;
43 | class GT_PrimInstance;
44 | class GU_Detail;
45 | class ROP_GLTF_BaseErrorManager;
46 | class GT_GEOPrimPacked;
47 |
48 | class ROP_GLTF_Refiner : public GT_Refine
49 | {
50 | public:
51 | struct Refine_Options
52 | {
53 | bool output_custom_attribs = false;
54 | };
55 |
56 | ROP_GLTF_Refiner(ROP_GLTF_ExportRoot &root, GLTF_Node *node,
57 | const UT_StringHolder &obj_material,
58 | std::function create_material,
59 | Refine_Options options);
60 |
61 | virtual ~ROP_GLTF_Refiner();
62 | virtual void addPrimitive(const GT_PrimitiveHandle &prim) override;
63 |
64 | // GLTF buffer allocation is currently unprotected
65 | bool allowThreading() const override { return false; }
66 |
67 | ///
68 | /// A convenience function. Refines the detail, and adds meshes
69 | /// (or potentially submeshes if instancing is used) to the GLTF_Node
70 | /// that is passed in.
71 | ///
72 | static void
73 | refine(const GU_Detail *src, ROP_GLTF_ExportRoot &root, GLTF_Node &node,
74 | const UT_StringHolder &obj_material,
75 | std::function create_material,
76 | Refine_Options options);
77 |
78 |
79 | private:
80 | void processPrimPolygon(GT_PrimPolygonMesh *prim, UT_Matrix4D trans,
81 | GLTF_Mesh &mesh);
82 |
83 | bool processInstance(const GT_PrimInstance *instance);
84 |
85 | GLTF_Handle appendMeshIfNotEmpty(GLTF_Mesh &mesh);
86 |
87 | void
88 | addMesh(const GT_PrimPolygonMesh &prim, UT_Matrix4D trans, GLTF_Mesh &mesh);
89 |
90 | // Creates a GLTF_Primitive based on the given attributes and
91 | // indices then returns a reference to it.
92 | GLTF_Primitive &
93 | addPoints(const GT_AttributeListHandle &attributes,
94 | const GT_DataArrayHandle &indices, GLTF_Mesh &mesh);
95 |
96 | // Translates a Houdini component type to the 'closest' GLTF
97 | // component type
98 | GLTF_ComponentType GetComponentTypeFromStorage(GT_Storage storage);
99 |
100 | struct Attrib_CopyResult
101 | {
102 | uint32 size;
103 | uint32 offset;
104 | UT_Array elem_min;
105 | UT_Array elem_max;
106 | uint32 entries;
107 | };
108 |
109 | template
110 | Attrib_CopyResult
111 | CopyAttribData(uint32 bid, const T *arr, GT_Size entries,
112 | GT_Size old_tuple_size, GT_Size new_tuple_size,
113 | std::function func, uint32 stride);
114 |
115 | //
116 | // Allocates data from the GLTF buffer 'bid' and moves attribute data
117 | // to handle, converting type if needed.
118 | // If old_tuple_size > new_tuple_size, then the size of the tuple will
119 | // be truncated (this is mainly used for UVs).
120 | //
121 | template
122 | uint32 AddAttrib(const GT_DataArrayHandle &handle,
123 | GLTF_ComponentType target_type, GT_Size new_tuple_size,
124 | uint32 bid, GLTF_BufferViewTarget buffer_type,
125 | std::function func = {}, uint32 stride = 1);
126 |
127 | bool ExportAttribute(const UT_StringRef &attrib_name,
128 | const GT_DataArrayHandle &attrib_data,
129 | GLTF_Primitive &prim);
130 |
131 | ROP_GLTF_ExportRoot &myRoot;
132 | GLTF_Node *myNode;
133 | const UT_StringHolder &myObjectMaterial;
134 | std::function myCreateMaterial;
135 | const Refine_Options myOptions;
136 | };
137 |
138 | class ROP_GLTF_PointSplit
139 | {
140 | public:
141 | static void
142 | Split(const GT_PrimPolygonMesh &polymesh, fpreal64 tol,
143 | GT_AttributeListHandle &new_points, GT_DataArrayHandle &new_vertices);
144 |
145 | private:
146 | ROP_GLTF_PointSplit(const GT_PrimPolygonMesh &prim, fpreal64 tol);
147 |
148 | // Refines detail and prim attributes down to vertex attributes
149 | // (which will later be refined into point attributes)
150 | GT_AttributeListHandle refineDetailPrims();
151 |
152 | // Returns true if the points can be merged
153 | template
154 | bool compareAttribs(GT_Offset pt_1, GT_Offset pt_2, T *attr_arr,
155 | GT_Size tuple_size);
156 |
157 | template
158 | GT_DataArrayHandle
159 | splitAttribute(GT_Int32Array *new_verts,
160 | UT_Array> &vertexes_using_point,
161 | GT_Int32Array *new_pts_indirect, T *attr_arr,
162 | GT_Size tuple_size);
163 |
164 | void
165 | splitAttrib(GT_AttributeListHandle &new_points, GT_DataArrayHandle &new_vertice,
166 | const GT_AttributeListHandle &vertex_attribs, exint idx);
167 |
168 | const GT_PrimPolygonMesh &myPrim;
169 | const fpreal myTol;
170 | };
171 |
172 | #endif
173 |
--------------------------------------------------------------------------------
/src/ROP/ROP_GLTF_ExportRoot.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #ifndef __ROP_GLTF_EXPORTROOT_h__
27 | #define __ROP_GLTF_EXPORTROOT_h__
28 |
29 | #include "ROP_GLTF_Image.h"
30 | #include
31 | #include
32 | #include
33 | #include
34 |
35 | #include
36 |
37 | #include
38 |
39 | constexpr const char *GENERATOR_STRING = "Houdini GLTF 2.0 Exporter";
40 | constexpr const char *GLTF_VERSION = "2.0";
41 |
42 | class UT_WorkBuffer;
43 | class UT_JSONWriter;
44 | class UT_OFStream;
45 | class OP_Node;
46 | struct ROP_GLTF_ChannelMapping;
47 |
48 | typedef GLTF_NAMESPACE::GLTF_Accessor GLTF_Accessor;
49 | typedef GLTF_NAMESPACE::GLTF_Animation GLTF_Animation;
50 | typedef GLTF_NAMESPACE::GLTF_Asset GLTF_Asset;
51 | typedef GLTF_NAMESPACE::GLTF_Buffer GLTF_Buffer;
52 | typedef GLTF_NAMESPACE::GLTF_BufferView GLTF_BufferView;
53 | typedef GLTF_NAMESPACE::GLTF_Camera GLTF_Camera;
54 | typedef GLTF_NAMESPACE::GLTF_Image GLTF_Image;
55 | typedef GLTF_NAMESPACE::GLTF_Material GLTF_Material;
56 | typedef GLTF_NAMESPACE::GLTF_Mesh GLTF_Mesh;
57 | typedef GLTF_NAMESPACE::GLTF_Node GLTF_Node;
58 | typedef GLTF_NAMESPACE::GLTF_Scene GLTF_Scene;
59 | typedef GLTF_NAMESPACE::GLTF_Sampler GLTF_Sampler;
60 | typedef GLTF_NAMESPACE::GLTF_Skin GLTF_Skin;
61 | typedef GLTF_NAMESPACE::GLTF_Texture GLTF_Texture;
62 | typedef GLTF_NAMESPACE::GLTF_Material GLTF_Material;
63 |
64 | typedef GLTF_NAMESPACE::GLTF_Int GLTF_Int;
65 | typedef GLTF_NAMESPACE::GLTF_Offset GLTF_Offset;
66 | typedef GLTF_NAMESPACE::GLTF_Handle GLTF_Handle;
67 |
68 |
69 | class ROP_GLTF_ExportRoot
70 | {
71 | public:
72 | struct ExportSettings
73 | {
74 | bool exportNames = false;
75 | };
76 |
77 | ROP_GLTF_ExportRoot(ExportSettings s);
78 | ~ROP_GLTF_ExportRoot();
79 |
80 | bool HasCachedChannelImage(
81 | const UT_Array &mapping) const;
82 | GLTF_Handle
83 | GetCachedChannelImage(const UT_Array &mapping);
84 | void
85 | InsertCachedChannelImage(const UT_Array &mapping,
86 | GLTF_Handle idx);
87 |
88 | UT_Map &GetImageCache();
89 | UT_Map &GetMaterialCache();
90 |
91 | /// This keeps track of the amount of times a specific filename
92 | /// outputted to to avoid name collisions
93 | UT_Map &GetNameUsagesMap();
94 |
95 | ///
96 | /// Allocates additional space in the buffer in index bid.
97 | /// TODO: stop allocating from reallocing space.
98 | ///
99 | void *BufferAlloc(GLTF_Handle bid, GLTF_Offset bytes, GLTF_Offset alignment,
100 | GLTF_Offset &offset);
101 |
102 | ///
103 | /// Returns a reference to the internal root GLTF object
104 | ///
105 | GLTF_NAMESPACE::GLTF_Loader &loader();
106 |
107 | ///
108 | /// Exports this structure as a GLTF file.
109 | ///
110 | bool ExportGLTF(const UT_String &path);
111 |
112 | ///
113 | /// Exports the file as GLB on os. If a uri is defined on buffer 0,
114 | /// it will be ignored. Otherwise, there are no modifications to the
115 | /// outputted JSON.
116 | ///
117 | bool ExportAsGLB(const UT_String &path);
118 | void SerializeJSON(UT_JSONWriter &writer);
119 |
120 | //
121 | // Convenience functions: Will return the index of the created
122 | // node in the parameter idx.
123 | //
124 | GLTF_Accessor &CreateAccessor(GLTF_Handle &idx);
125 | GLTF_Buffer &CreateBuffer(GLTF_Handle &idx);
126 | GLTF_BufferView &CreateBufferview(GLTF_Handle &idx);
127 | GLTF_Node &CreateNode(GLTF_Handle &idx);
128 | GLTF_Mesh &CreateMesh(GLTF_Handle &idx);
129 | GLTF_Scene &CreateScene(GLTF_Handle &idx);
130 | GLTF_Image &CreateImage(GLTF_Handle &idx);
131 | GLTF_Texture &CreateTexture(GLTF_Handle &idx);
132 | GLTF_Material &CreateMaterial(GLTF_Handle &idx);
133 |
134 | GLTF_Accessor *getAccessor(GLTF_Handle idx);
135 | GLTF_Animation *getAnimation(GLTF_Handle idx);
136 | GLTF_Asset getAsset();
137 | GLTF_Buffer *getBuffer(GLTF_Handle idx);
138 | GLTF_BufferView *getBufferView(GLTF_Handle idx);
139 | GLTF_Camera *getCamera(GLTF_Handle idx);
140 | GLTF_Image *getImage(GLTF_Handle idx);
141 | GLTF_Material *getMaterial(GLTF_Handle idx);
142 | GLTF_Mesh *getMesh(GLTF_Handle idx);
143 | GLTF_Node *getNode(GLTF_Handle idx);
144 | GLTF_Sampler *getSampler(GLTF_Handle idx);
145 | GLTF_Handle getDefaultScene();
146 | GLTF_Scene *getScene(GLTF_Handle idx);
147 | GLTF_Skin *getSkin(GLTF_Handle idx);
148 | GLTF_Texture *getTexture(GLTF_Handle idx);
149 |
150 | void SetDefaultScene(GLTF_Handle idx);
151 |
152 |
153 | const UT_Array &getAccessors() const;
154 | const UT_Array &getAnimations() const;
155 | const UT_Array &getBuffers() const;
156 | const UT_Array &getBufferViews() const;
157 | const UT_Array &getCameras() const;
158 | const UT_Array &getImages() const;
159 | const UT_Array &getMaterials() const;
160 | const UT_Array &getMeshes() const;
161 | const UT_Array &getNodes() const;
162 | const UT_Array &getSamplers() const;
163 | const UT_Array &getScenes() const;
164 | const UT_Array &getSkins() const;
165 | const UT_Array &getTextures() const;
166 |
167 | private:
168 | void
169 | OutputName(UT_JSONWriter &writer, const char *string, const char *value);
170 |
171 | void SerializeAsset(UT_JSONWriter &writer);
172 | void SerializeAccessors(UT_JSONWriter &writer);
173 | void SerializeBuffers(UT_JSONWriter &writer);
174 | void SerializeBufferViews(UT_JSONWriter &writer);
175 | void SerializeNodes(UT_JSONWriter &writer);
176 | void SerializeMeshes(UT_JSONWriter &writer);
177 | void SerializeMaterials(UT_JSONWriter &writer);
178 | void SerializePrimitives(UT_JSONWriter &writer,
179 | const UT_Array &primitive);
180 | void SerializeTextures(UT_JSONWriter &writer);
181 | void SerializeImages(UT_JSONWriter &writer);
182 | void SerializeScenes(UT_JSONWriter &writer);
183 |
184 | bool OutputBuffer(const char *folder, GLTF_Handle buffer) const;
185 | void OutputGLBBuffer(UT_OFStream &stream) const;
186 |
187 | // Pre-output pass: these should be used only before outputting as they
188 | // may potentially invalidate handles
189 | void ResolveBufferLengths();
190 | void RemoveEmptyBuffers();
191 |
192 | // Any external files must be copied to the output folder as
193 | // GLTF is only required to support "http://" and relative URI
194 | void ConvertAbsolutePaths(const UT_String &base_path);
195 |
196 | // Returns true if opened output stream to given path.
197 | // Automatically creates directories in path.
198 | bool OpenFileStreamAtPath(const UT_String &path, UT_OFStream &os);
199 |
200 | UT_Array> myBufferData;
201 |
202 | UT_Map myNameUsagesMap;
203 | UT_Map myImageMap;
204 | UT_Map myMaterialMap;
205 |
206 | std::map, GLTF_Handle>
207 | myChannelImageMap;
208 |
209 | GLTF_NAMESPACE::GLTF_Loader myLoader;
210 | ExportSettings mySettings;
211 | };
212 |
213 | #endif
214 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Loader.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #ifndef __SOP_GLTFLOADER_H__
29 | #define __SOP_GLTFLOADER_H__
30 |
31 | #include "GLTF_API.h"
32 | #include "GLTF_Types.h"
33 |
34 | #include
35 | #include
36 | #include
37 |
38 | class UT_JSONValue;
39 | class UT_JSONValueMap;
40 | class UT_JSONValueArray;
41 |
42 | namespace GLTF_NAMESPACE
43 | {
44 |
45 | //=================================================
46 |
47 | ///
48 | /// A class for loading a GLTF file into a more usuable structure.
49 | ///
50 | class GLTF_API GLTF_Loader
51 | {
52 | public:
53 | GLTF_Loader();
54 | GLTF_Loader(UT_String filename);
55 | virtual ~GLTF_Loader();
56 |
57 | // Delete copy constructor
58 | GLTF_Loader(GLTF_Loader &loader) = delete;
59 | GLTF_Loader(const GLTF_Loader &loader) = delete;
60 |
61 | ///
62 | /// Loads and parses the JSON data within this GLTF file.
63 | /// Does not load any associated buffer data.
64 | /// @return Whether or not the load suceeded
65 | ///
66 | bool Load();
67 |
68 | ///
69 | /// Loads all data that can be accessed with the given accessor and returns
70 | /// a pointer to the beginning of the data. The caller is not responsible
71 | /// for deleting the returned data.
72 | /// @return Whether or not the accessor data load suceeded
73 | ///
74 | bool LoadAccessorData(const GLTF_Accessor &accessor, unsigned char *&data) const;
75 |
76 | GLTF_Accessor *createAccessor(GLTF_Handle& idx);
77 | GLTF_Animation *createAnimation(GLTF_Handle& idx);
78 | GLTF_Buffer *createBuffer(GLTF_Handle& idx);
79 | GLTF_BufferView *createBufferView(GLTF_Handle& idx);
80 | GLTF_Camera *createCamera(GLTF_Handle& idx);
81 | GLTF_Image *createImage(GLTF_Handle& idx);
82 | GLTF_Material *createMaterial(GLTF_Handle& idx);
83 | GLTF_Mesh *createMesh(GLTF_Handle& idx);
84 | GLTF_Node *createNode(GLTF_Handle& idx);
85 | GLTF_Sampler *createSampler(GLTF_Handle& idx);
86 | GLTF_Scene *createScene(GLTF_Handle& idx);
87 | GLTF_Skin *createSkin(GLTF_Handle& idx);
88 | GLTF_Texture *createTexture(GLTF_Handle& idx);
89 |
90 | GLTF_Accessor const *getAccessor(GLTF_Handle idx) const;
91 | GLTF_Animation const *getAnimation(GLTF_Handle idx) const;
92 | GLTF_Asset getAsset() const;
93 | GLTF_Buffer const *getBuffer(GLTF_Handle idx) const;
94 | GLTF_BufferView const *getBufferView(GLTF_Handle idx) const;
95 | GLTF_Camera const *getCamera(GLTF_Handle idx) const;
96 | GLTF_Image const *getImage(GLTF_Handle idx) const;
97 | GLTF_Material const *getMaterial(GLTF_Handle idx) const;
98 | GLTF_Mesh const *getMesh(GLTF_Handle idx) const;
99 | GLTF_Node const *getNode(GLTF_Handle idx) const;
100 | GLTF_Sampler const *getSampler(GLTF_Handle idx) const;
101 | GLTF_Handle getDefaultScene() const;
102 | GLTF_Scene const *getScene(GLTF_Handle idx) const;
103 | GLTF_Skin const *getSkin(GLTF_Handle idx) const;
104 | GLTF_Texture const *getTexture(GLTF_Handle idx) const;
105 |
106 | GLTF_Accessor *getAccessor(GLTF_Handle idx);
107 | GLTF_Animation *getAnimation(GLTF_Handle idx);
108 | GLTF_Buffer *getBuffer(GLTF_Handle idx);
109 | GLTF_BufferView *getBufferView(GLTF_Handle idx);
110 | GLTF_Camera *getCamera(GLTF_Handle idx);
111 | GLTF_Image *getImage(GLTF_Handle idx);
112 | GLTF_Material *getMaterial(GLTF_Handle idx);
113 | GLTF_Mesh *getMesh(GLTF_Handle idx);
114 | GLTF_Node *getNode(GLTF_Handle idx);
115 | GLTF_Sampler *getSampler(GLTF_Handle idx);
116 | GLTF_Scene *getScene(GLTF_Handle idx);
117 | GLTF_Skin *getSkin(GLTF_Handle idx);
118 | GLTF_Texture *getTexture(GLTF_Handle idx);
119 |
120 | const UT_Array &getAccessors() const;
121 | const UT_Array &getAnimations() const;
122 | const UT_Array &getBuffers() const;
123 | const UT_Array &getBufferViews() const;
124 | const UT_Array &getCameras() const;
125 | const UT_Array &getImages() const;
126 | const UT_Array &getMaterials() const;
127 | const UT_Array &getMeshes() const;
128 | const UT_Array &getNodes() const;
129 | const UT_Array &getSamplers() const;
130 | const UT_Array &getScenes() const;
131 | const UT_Array &getSkins() const;
132 | const UT_Array &getTextures() const;
133 |
134 | void removeBuffer(GLTF_Handle idx);
135 | void removeNode(GLTF_Handle idx);
136 |
137 | void setDefaultScene(const GLTF_Handle &idx);
138 | void setAsset(const GLTF_Asset &asset);
139 |
140 | exint getNumAccessors() const;
141 | exint getNumAnimations() const;
142 | exint getNumBuffers() const;
143 | exint getNumBufferViews() const;
144 | exint getNumCameras() const;
145 | exint getNumImages() const;
146 | exint getNumMaterials() const;
147 | exint getNumMeshes() const;
148 | exint getNumNodes() const;
149 | exint getNumSamplers() const;
150 | exint getNumScenes() const;
151 | exint getNumSkins() const;
152 | exint getNumTextures() const;
153 |
154 | private:
155 | // Handles reading the JSON map, which may be external or
156 | // embedded in a GLB file
157 | bool ReadJSON(const UT_JSONValueMap &root_json);
158 |
159 | bool ReadGLTF();
160 | bool ReadGLB();
161 |
162 | bool ReadAsset(const UT_JSONValueMap &asset_json);
163 |
164 | // Read items from GLTF JSON -> GLTF struct
165 | bool ReadNode(const UT_JSONValueMap &node_json, const exint idx);
166 | bool ReadBuffer(const UT_JSONValueMap &buffer_json, const exint idx);
167 | bool ReadBufferView(const UT_JSONValueMap &bufferview_json, const exint idx);
168 | bool ReadAccessor(const UT_JSONValueMap &accessor_json, const exint idx);
169 | bool ReadPrimitive(const UT_JSONValue *primitive_json, GLTF_Primitive *primitive);
170 | bool ReadMesh(const UT_JSONValueMap &mesh_json, const exint idx);
171 | bool ReadTexture(const UT_JSONValueMap &texture_json, const exint idx);
172 | bool ReadSampler(const UT_JSONValueMap &sampler_json, const exint idx);
173 | bool ReadImage(const UT_JSONValueMap &image_json, const exint idx);
174 | bool ReadScene(const UT_JSONValueMap &scene_json, const exint idx);
175 | bool ReadMaterial(const UT_JSONValueMap &material_json, const exint idx);
176 |
177 | // Utility: Takes an array of maps, and calls funcs() on every item
178 | bool ReadArrayOfMaps(const UT_JSONValue *arr, bool required,
179 | std::function func);
180 |
181 | UT_String myFilename;
182 | UT_String myBasePath;
183 |
184 | bool myIsLoaded;
185 |
186 | // Retrieves the buffer at idx, potentially from cache if cached.
187 | bool LoadBuffer(uint32 idx, unsigned char *&buffer_data) const;
188 |
189 | // Simply an indexed array of pointers to buffer data
190 | UT_Array myAccesors;
191 | UT_Array myAnimations;
192 | GLTF_Asset myAsset;
193 | UT_Array myBuffers;
194 | UT_Array myBufferViews;
195 | UT_Array myCameras;
196 | UT_Array myImages;
197 | UT_Array myMaterials;
198 | UT_Array myMeshes;
199 | UT_Array myNodes;
200 | UT_Array mySamplers;
201 | uint32 myScene = GLTF_INVALID_IDX;
202 | UT_Array myScenes;
203 | UT_Array mySkins;
204 | UT_Array myTextures;
205 |
206 | // Loading is transparent to the caller -- we want const
207 | // semantics that
208 | mutable UT_Lock myAccessorLock;
209 | mutable UT_Array myBufferCache;
210 | };
211 |
212 | //=================================================
213 |
214 | } // end GLTF_NAMESPACE
215 |
216 | #endif
217 |
--------------------------------------------------------------------------------
/src/ROP/ROP_GLTF.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #ifndef __ROP_GLTF_h__
27 | #define __ROP_GLTF_h__
28 |
29 | #include
30 | #include
31 | #include
32 |
33 | using GLTF_NAMESPACE::GLTF_Node;
34 | using GLTF_NAMESPACE::GLTF_TextureInfo;
35 | using GLTF_NAMESPACE::GLTF_Scene;
36 |
37 | typedef GLTF_NAMESPACE::GLTF_Handle GLTF_Handle;
38 |
39 | struct ROP_GLTF_ChannelMapping;
40 | struct ROP_GLTF_ImgExportParms;
41 | class ROP_GLTF_ExportRoot;
42 | class ROP_GLTF_ErrorManager;
43 | class OBJ_Geometry;
44 |
45 | class ROP_GLTF_BaseErrorManager
46 | {
47 | public:
48 | virtual ~ROP_GLTF_BaseErrorManager() = default;
49 | virtual void AddError(int code, const char *msg = 0) const = 0;
50 | virtual void AddWarning(int code, const char *msg = 0) const = 0;
51 | };
52 |
53 | struct ROP_GLTF_TextureParms
54 | {
55 | public:
56 | bool myFlipGreenChannel = false;
57 | };
58 |
59 | class ROP_GLTF : public ROP_Node
60 | {
61 | public:
62 | static OP_Node *
63 | myConstructor(OP_Network *net, const char *name, OP_Operator *op);
64 |
65 | ///
66 | /// Simply wraps ROP_GLTF so that the error manager on Node
67 | /// can be used in external classes.
68 | ///
69 | class ROP_GLTF_ErrorManager : public ROP_GLTF_BaseErrorManager
70 | {
71 | public:
72 | ROP_GLTF_ErrorManager(ROP_GLTF &gltf);
73 | ROP_GLTF_ErrorManager(ROP_GLTF_ErrorManager &gltf) = delete;
74 | ROP_GLTF_ErrorManager(ROP_GLTF_ErrorManager &&gltf) = delete;
75 | virtual ~ROP_GLTF_ErrorManager() = default;
76 | virtual void AddError(int code, const char *msg = 0) const;
77 | virtual void AddWarning(int code, const char *msg = 0) const;
78 |
79 | private:
80 | ROP_GLTF &myNode;
81 | };
82 |
83 | protected:
84 | ROP_GLTF(OP_Network *net, const char *name, OP_Operator *entry);
85 | virtual bool updateParmsFlags();
86 | virtual ~ROP_GLTF();
87 |
88 | // From ROP_Node
89 | virtual int startRender(int nframes, fpreal s, fpreal e);
90 | virtual ROP_RENDER_CODE renderFrame(fpreal time, UT_Interrupt *boss);
91 | virtual ROP_RENDER_CODE endRender();
92 |
93 | void OUTPUT_FILE(UT_String &str, fpreal t) const
94 | {
95 | evalString(str, "file", 0, t);
96 | }
97 | void OBJPATH(UT_String &str, fpreal t) const
98 | {
99 | evalString(str, "objpath", 0, t);
100 | }
101 | void OBJECTS(UT_String &str, fpreal time) const
102 | {
103 | evalString(str, "objects", 0, time);
104 | }
105 | bool EXPORT_MATERIALS(fpreal time) const
106 | {
107 | return evalInt("exportmaterials", 0, time) != 0;
108 | }
109 | bool EXPORT_CUSTOM_ATTRIBS(fpreal time) const
110 | {
111 | return evalInt("customattribs", 0, time) != 0;
112 | }
113 | bool EXPORT_NAMES(fpreal time) const
114 | {
115 | return evalInt("exportnames", 0, time) != 0;
116 | }
117 | void IMAGEFORMAT(UT_String &str, fpreal time) const
118 | {
119 | evalString(str, "imageformat", 0, time);
120 | }
121 | void MAXRES(UT_String &str, fpreal time) const
122 | {
123 | evalString(str, "maxresolution", 0, time);
124 | }
125 | void EXPORTTYPE(UT_String &str, fpreal time) const
126 | {
127 | evalString(str, "exporttype", 0, time);
128 | }
129 | bool CULL_EMPTY_NODES(fpreal time) const
130 | {
131 | return evalInt("cullempty", 0, time) != 0;
132 | }
133 | bool POWER_OF_TWO(fpreal time) const
134 | {
135 | return evalInt("poweroftwo", 0, time) != 0;
136 | }
137 | int IMAGE_QUALITY(fpreal time) const
138 | {
139 | return evalInt("imagequality", 0, time);
140 | }
141 | bool SAVE_HIDDEN(fpreal time) const
142 | {
143 | return evalInt("savehidden", 0, time) != 0;
144 | }
145 | bool USE_SOP_PATH(fpreal time) const
146 | {
147 | return evalInt("usesoppath", 0, time) != 0;
148 | }
149 | void SOP_PATH(UT_String &str, fpreal time) const
150 | {
151 | evalString(str, "soppath", 0, time);
152 | }
153 | bool FLIP_Y_NORMALS(fpreal time) const
154 | {
155 | return evalInt("flipnormalmapy", 0, time) != 0;
156 | }
157 |
158 | private:
159 | class GLTF_HierarchyBuilder
160 | {
161 | public:
162 | GLTF_HierarchyBuilder(
163 | OP_Node *root_node, GLTF_Node *root_gltf,
164 | ROP_GLTF_ExportRoot &export_root,
165 | std::function proc_func);
166 |
167 | uint32 Traverse(OBJ_Node *node, fpreal time);
168 |
169 | private:
170 | const OP_Node *myRootNode;
171 | ROP_GLTF_ExportRoot &myRootExporter;
172 | GLTF_Node *myRootGLTF;
173 | UT_Map myNodeMap;
174 | const std::function myFunc;
175 | };
176 |
177 | const IMG_Format *GetImageFormat(fpreal time) const;
178 | const char *GetImageMimeType(fpreal time) const;
179 |
180 | // Takes a glTF node and a Houdini node, and assigns the transformation
181 | // of the Houdini node to the glTF node.
182 | void AssignGLTFTransform(GLTF_Node &gltf_node, OBJ_Node *node,
183 | fpreal time) const;
184 | void
185 | AssignGLTFName(GLTF_Node &gltf_node, OBJ_Node *node, fpreal time) const;
186 |
187 | // Creates a GLTF_Mesh mesh from *node and assigns it to the glTF node.
188 | // If the sop is not null, then it will pull geometry from *sop. Otherwise
189 | // it will pull geometry from the current node being rendered.
190 | void SetupGLTFMesh(GLTF_Node &gltf_node, OBJ_Node *node, fpreal time,
191 | SOP_Node *sop = nullptr);
192 |
193 | // Translation of Houdini principled shader parameters -> GLTF material
194 | // Parameters
195 | uint32 TranslatePrincipledShader(const OP_Context &context,
196 | const OP_Node *ps_node);
197 |
198 | // As we are pulling from multiple directories and outputting
199 | // the images in a single directory, there is a possiblity of
200 | // name collisions. This numbers files with the same name
201 | // in an arbitrary order.
202 | void GetNonCollidingName(const UT_String &s, UT_String &o);
203 |
204 | SOP_Node *GetSOPNode(fpreal time) const;
205 | bool hasSOPInput(fpreal time) const;
206 |
207 | // For handling textures with input from only a single channel
208 | bool
209 | TranslateTexture(const UT_String &path, const OP_Context &context,
210 | GLTF_TextureInfo &tex_info,
211 | const ROP_GLTF_TextureParms &tex_parms = {});
212 |
213 | // For handling textures with input from multiple channels
214 | bool
215 | TranslateTexture(const UT_Array &mappings,
216 | const OP_Context &context, GLTF_TextureInfo &tex_info,
217 | const ROP_GLTF_TextureParms &tex_parms = {});
218 |
219 | uint32
220 | OutputTexture(const UT_String &output_path, const ROP_GLTF_TextureParms &parms,
221 | std::function output_function,
223 | GLTF_TextureInfo &tex_info, const OP_Context &context);
224 |
225 | GLTF_Scene &InitializeBasicGLTFScene(GLTF_Handle &root_scene_idx);
226 |
227 | bool IsExportingGLB() const;
228 | const UT_String &GetBasePath() const;
229 |
230 | bool WriteTreeToDisk(fpreal time);
231 | void InitializeGLTFTree(fpreal time);
232 |
233 | bool BuildGLTFTree(fpreal time);
234 | bool BuildFromSOP(fpreal time, SOP_Node *sop, OBJ_Node *geo);
235 | bool BuildGLTFTreeFromHierarchy(OP_Node *root_node, OP_Bundle *bundle,
236 | fpreal time);
237 |
238 | ////////////////////////////////////////////
239 |
240 | UT_UniquePtr myRoot;
241 | fpreal myEndTime;
242 | fpreal myStartTime;
243 | bool myExportingGLB;
244 | UT_String myFilename;
245 | UT_String myBasepath;
246 | ROP_GLTF_ErrorManager *myErrorHandler;
247 | };
248 |
249 | #endif
250 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/DialogScript:
--------------------------------------------------------------------------------
1 | # Dialog script for gltf_hierarchy automatically generated
2 |
3 | {
4 | name gltf_hierarchy
5 | script gltf_hierarchy
6 | label "glTF Hierarchy"
7 |
8 | help {
9 | ""
10 | }
11 |
12 | inputlabel 1 "Sub-Network Input #1"
13 | inputlabel 2 "Sub-Network Input #2"
14 | inputlabel 3 "Sub-Network Input #3"
15 | inputlabel 4 "Sub-Network Input #4"
16 |
17 | group {
18 | name "stdswitcher3_1"
19 | label "Subnet"
20 | invisibletab
21 |
22 | parm {
23 | name "label1"
24 | baseparm
25 | label "Input #1 Label"
26 | invisible
27 | export dialog
28 | }
29 | parm {
30 | name "label2"
31 | baseparm
32 | label "Input #2 Label"
33 | invisible
34 | export dialog
35 | }
36 | parm {
37 | name "label3"
38 | baseparm
39 | label "Input #3 Label"
40 | invisible
41 | export dialog
42 | }
43 | parm {
44 | name "label4"
45 | baseparm
46 | label "Input #4 Label"
47 | invisible
48 | export dialog
49 | }
50 | parm {
51 | name "tdisplay"
52 | baseparm
53 | label "Display"
54 | invisible
55 | joinnext
56 | export all
57 | }
58 | parm {
59 | name "display"
60 | baseparm
61 | label "Display"
62 | invisible
63 | export all
64 | }
65 | parm {
66 | name "outputobj"
67 | baseparm
68 | label "Output Transform"
69 | invisible
70 | export all
71 | }
72 | parm {
73 | name "visibleobjects"
74 | baseparm
75 | label "Visible Children"
76 | invisible
77 | export none
78 | }
79 | parm {
80 | name "picking"
81 | baseparm
82 | label "Viewport Selecting Enabled"
83 | invisible
84 | export none
85 | }
86 | parm {
87 | name "pickscript"
88 | baseparm
89 | label "Select Script"
90 | invisible
91 | export none
92 | }
93 | parm {
94 | name "caching"
95 | baseparm
96 | label "Cache Object Transform"
97 | invisible
98 | export none
99 | }
100 | parm {
101 | name "use_dcolor"
102 | baseparm
103 | label "Set Wireframe Color"
104 | invisible
105 | export none
106 | }
107 | parm {
108 | name "dcolor"
109 | baseparm
110 | label "Wireframe Color"
111 | invisible
112 | export none
113 | }
114 | }
115 |
116 | parm {
117 | name "filename"
118 | label "File Name"
119 | type file
120 | default { "" }
121 | parmtag { "filechooser_mode" "read" }
122 | parmtag { "filechooser_pattern" "*.gltf, *.glb" }
123 | parmtag { "script_callback" "hou.pwd().hdaModule().EvaluateMenus(kwargs['node'])" }
124 | parmtag { "script_callback_language" "python" }
125 | }
126 | parm {
127 | name "assetfolder"
128 | label "Asset Extraction Folder"
129 | type directory
130 | default { "$HIP/glTF_Assets" }
131 | }
132 | parm {
133 | name "scene"
134 | label "Scene"
135 | type string
136 | default { "0" }
137 | menu {
138 | [ "kwargs['node'].hdaModule().GenerateSceneMenu(kwargs['node'])" ]
139 | language python
140 | }
141 | parmtag { "script_callback_language" "python" }
142 | }
143 | parm {
144 | name "buildscene"
145 | label "Build Scene"
146 | type button
147 | default { "0" }
148 | parmtag { "export_disable" "1" }
149 | parmtag { "script_callback" "kwargs['node'].hdaModule().ReloadGeometry(kwargs['node'])" }
150 | parmtag { "script_callback_language" "python" }
151 | }
152 | parm {
153 | name "lockgeo"
154 | label "Lock Geometry"
155 | type toggle
156 | default { "on" }
157 | disablewhen "{ importgeometry == 0 }"
158 | }
159 | parm {
160 | name "flattenhierarchy"
161 | label "Flatten Hierarchy"
162 | type toggle
163 | default { "off" }
164 | parmtag { "import_enable" "1" }
165 | }
166 | parm {
167 | name "promotepointattrs"
168 | label "Promote Point Attributes To Vertex"
169 | type toggle
170 | default { "1" }
171 | }
172 | parm {
173 | name "pointconsolidatedist"
174 | label "Points Merge Distance"
175 | type float
176 | default { "0.0001" }
177 | disablewhen "{ promotepointattrs == 0 }"
178 | range { 0! 10 }
179 | }
180 | group {
181 | name "stdswitcher3_2"
182 | label "Filter Options"
183 |
184 | parm {
185 | name "importgeometry"
186 | label "Import Geometry"
187 | type toggle
188 | default { "on" }
189 | disablewhen "{ flattenhierarchy != 0 }"
190 | }
191 | parm {
192 | name "importcustomattributes"
193 | label "Import Custom Attributes"
194 | type toggle
195 | default { "on" }
196 | disablewhen "{ importgeometry == 0 }"
197 | }
198 | parm {
199 | name "importmaterials"
200 | label "Import Materials"
201 | type toggle
202 | default { "on" }
203 | }
204 | parm {
205 | name "importnongeo"
206 | label "Import Non-Geometry"
207 | type toggle
208 | default { "off" }
209 | disablewhen "{ importgeometry == 0 } { flattenhierarchy != 0 }"
210 | }
211 | parm {
212 | name "importunusedmaterials"
213 | label "Import Unused Materials"
214 | type toggle
215 | default { "off" }
216 | disablewhen "{ importmaterials == 0 } { flattenhierarchy != 0 }"
217 | }
218 | }
219 |
220 | group {
221 | name "stdswitcher3_2_1"
222 | label "Transform"
223 |
224 | parm {
225 | name "xOrd"
226 | baseparm
227 | label "Transform Order"
228 | joinnext
229 | export none
230 | }
231 | parm {
232 | name "rOrd"
233 | baseparm
234 | label "Scene"
235 | nolabel
236 | export none
237 | }
238 | parm {
239 | name "t"
240 | baseparm
241 | label "Translate"
242 | export none
243 | }
244 | parm {
245 | name "r"
246 | baseparm
247 | label "Rotate"
248 | export none
249 | }
250 | parm {
251 | name "s"
252 | baseparm
253 | label "Scale"
254 | export none
255 | }
256 | parm {
257 | name "p"
258 | baseparm
259 | label "Pivot Translate"
260 | export none
261 | }
262 | parm {
263 | name "pr"
264 | baseparm
265 | label "Pivot Rotate"
266 | export none
267 | }
268 | parm {
269 | name "scale"
270 | baseparm
271 | label "Uniform Scale"
272 | export none
273 | }
274 | parm {
275 | name "pre_xform"
276 | baseparm
277 | label "Modify Pre-Transform"
278 | export none
279 | }
280 | parm {
281 | name "keeppos"
282 | baseparm
283 | label "Keep Position When Parenting"
284 | export none
285 | }
286 | parm {
287 | name "childcomp"
288 | baseparm
289 | label "Child Compensation"
290 | export none
291 | }
292 | parm {
293 | name "constraints_on"
294 | baseparm
295 | label "Enable Constraints"
296 | export none
297 | }
298 | parm {
299 | name "constraints_path"
300 | baseparm
301 | label "Constraints"
302 | invisible
303 | export none
304 | }
305 | parm {
306 | name "lookatpath"
307 | baseparm
308 | label "Look At"
309 | invisible
310 | export none
311 | }
312 | parm {
313 | name "lookupobjpath"
314 | baseparm
315 | label "Look Up Object"
316 | invisible
317 | export none
318 | }
319 | parm {
320 | name "lookup"
321 | baseparm
322 | label "Look At Up Vector"
323 | invisible
324 | export none
325 | }
326 | parm {
327 | name "pathobjpath"
328 | baseparm
329 | label "Path Object"
330 | invisible
331 | export none
332 | }
333 | parm {
334 | name "roll"
335 | baseparm
336 | label "Roll"
337 | invisible
338 | export none
339 | }
340 | parm {
341 | name "pos"
342 | baseparm
343 | label "Position"
344 | invisible
345 | export none
346 | }
347 | parm {
348 | name "uparmtype"
349 | baseparm
350 | label "Parameterization"
351 | invisible
352 | export none
353 | }
354 | parm {
355 | name "pathorient"
356 | baseparm
357 | label "Orient Along Path"
358 | invisible
359 | export none
360 | }
361 | parm {
362 | name "up"
363 | baseparm
364 | label "Orient Up Vector"
365 | invisible
366 | export none
367 | }
368 | parm {
369 | name "bank"
370 | baseparm
371 | label "Auto-Bank factor"
372 | invisible
373 | export none
374 | }
375 | }
376 |
377 | }
378 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_Types.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) COPYRIGHTYEAR
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 | #ifndef __SOP_GLTFTYPES_H__
28 | #define __SOP_GLTFTYPES_H__
29 |
30 | #include "GLTF_API.h"
31 |
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 | #include
40 |
41 | namespace GLTF_NAMESPACE
42 | {
43 |
44 | #define GLTF_INVALID_IDX uint32(~0)
45 |
46 | typedef uint32 GLTF_Int;
47 | typedef uint32 GLTF_Offset;
48 | typedef uint32 GLTF_Handle;
49 |
50 | const uint32 GLB_BUFFER_IDX = 0;
51 | const uint32 GLTF_GLB_MAGIC = 0x46546C67;
52 | const uint32 GLTF_GLB_JSON = 0x4E4F534A;
53 | const uint32 GLTF_GLB_BIN = 0x004E4942;
54 |
55 | constexpr const char *GLTF_PROJECTION_NAME_ORTHOGRAPHIC = "ORTHOGRAPHIC";
56 | constexpr const char *GLTF_PROJECTION_NAME_PERSPECTIVE = "PERSPECTIVE";
57 | constexpr const char *GLTF_TYPE_NAME_SCALAR = "SCALAR";
58 | constexpr const char *GLTF_TYPE_NAME_VEC2 = "VEC2";
59 | constexpr const char *GLTF_TYPE_NAME_VEC3 = "VEC3";
60 | constexpr const char *GLTF_TYPE_NAME_VEC4 = "VEC4";
61 | constexpr const char *GLTF_TYPE_NAME_MAT2 = "MAT2";
62 | constexpr const char *GLTF_TYPE_NAME_MAT3 = "MAT3";
63 | constexpr const char *GLTF_TYPE_NAME_MAT4 = "MAT4";
64 |
65 | enum GLTF_RenderMode
66 | {
67 | GLTF_RENDERMODE_POINTS = 0,
68 | GLTF_RENDERMODE_LINES = 1,
69 | GLTF_RENDERMODE_LINE_LOOP = 2,
70 | GLTF_RENDERMODE_LINE_STRIP = 3,
71 | GLTF_RENDERMODE_TRIANGLES = 4,
72 | GLTF_RENDERMODE_TRIANGLE_STRIP = 5,
73 | GLTF_RENDERMODE_TRIANGLE_FAN = 6,
74 | GLTF_RENDERMODE_INVALID
75 | };
76 |
77 | enum GLTF_ComponentType
78 | {
79 | GLTF_COMPONENT_INVALID = 0,
80 | GLTF_COMPONENT_BYTE = 5120,
81 | GLTF_COMPONENT_UNSIGNED_BYTE = 5121,
82 | GLTF_COMPONENT_SHORT = 5122,
83 | GLTF_COMPONENT_UNSIGNED_SHORT = 5123,
84 | GLTF_COMPONENT_UNSIGNED_INT = 5125,
85 | GLTF_COMPONENT_FLOAT = 5126
86 | };
87 |
88 | enum GLTF_BufferViewTarget
89 | {
90 | GLTF_BUFFER_INVALID = 0,
91 | GLTF_BUFFER_ARRAY = 34962,
92 | GLTF_BUFFER_ELEMENT = 34963
93 | };
94 |
95 | enum GLTF_TexFilter
96 | {
97 | GLTF_TEXFILTER_INVALID = 0,
98 | GLTF_TEXFILTER_NEAREST = 9728,
99 | GLTF_TEXFILTER_LINEAR = 9729,
100 | GLTF_TEXFILTER_NEAREST_MIPMAP_NEAREST = 9984,
101 | GLTF_TEXFILTER_LINEAR_MIPMAP_NEAREST = 9985,
102 | GLTF_TEXFILTER_NEAREST_MIPMAP_LINEAR = 9986,
103 | GLTF_TEXFILTER_LINEAR_MIPMAP_LINEAR = 9987,
104 | };
105 |
106 | enum GLTF_TexWrap
107 | {
108 | GLTF_TEXWRAP_INVALID = 0,
109 | GLTF_TEXWRAP_CLAMP_TO_EDGE = 33071,
110 | GLTF_TEXWRAP_MIRRORED_REPEAT = 33648,
111 | GLTF_TEXWRAP_REPEAT = 10497,
112 | };
113 |
114 | //=================================================
115 |
116 | // Represented as a string in the GLTF file
117 | enum GLTF_Type
118 | {
119 | GLTF_TYPE_INVALID = 0,
120 | GLTF_TYPE_SCALAR,
121 | GLTF_TYPE_VEC2,
122 | GLTF_TYPE_VEC3,
123 | GLTF_TYPE_VEC4,
124 | GLTF_TYPE_MAT2,
125 | GLTF_TYPE_MAT3,
126 | GLTF_TYPE_MAT4
127 | };
128 |
129 | enum GLTF_TRANSFORM_TYPE
130 | {
131 | GLTF_TRANSFORM_NONE = 0,
132 | GLTF_TRANSFORM_MAT4,
133 | GLTF_TRANSFORM_TRS
134 | };
135 |
136 | enum GLTF_TextureTypes
137 | {
138 | TEXTURE_NONE = 0,
139 | TEXTURE_NORMAL,
140 | TEXTURE_OCCLUSION,
141 | TEXTURE_EMISSIVE
142 | };
143 |
144 | //=========================================================================
145 |
146 | // GLTF types - unimplemented fields are currently displayed as comments in the
147 | // structure declarations
148 |
149 | struct GLTF_API GLTF_TextureInfo
150 | {
151 | GLTF_Handle index = GLTF_INVALID_IDX;
152 | GLTF_Int texCoord = 0;
153 | // extensions
154 | // extras
155 | };
156 |
157 | struct GLTF_API GLTF_NormalTextureInfo : public GLTF_TextureInfo
158 | {
159 | fpreal32 scale = 1.0;
160 | // extensions
161 | // extras
162 | };
163 |
164 | struct GLTF_API GLTF_Accessor
165 | {
166 | GLTF_Handle bufferView = GLTF_INVALID_IDX;
167 | GLTF_Int byteOffset = 0;
168 | GLTF_ComponentType componentType = GLTF_COMPONENT_INVALID; // Required
169 | bool normalized = false;
170 | GLTF_Int count = 0;
171 | GLTF_Type type = GLTF_TYPE_INVALID;
172 |
173 | // A double has a 53 bit mantissa, we can therefore cast to either float
174 | // or int32 without loss of precision
175 | UT_Array max;
176 | UT_Array min;
177 |
178 | // sparse
179 | UT_String name = "";
180 | // extensions
181 | // extras
182 | };
183 |
184 | struct GLTF_API GLTF_Animation
185 | {
186 | // channels
187 | UT_Array samplers;
188 | UT_String name = "";
189 | // extensions
190 | // extras
191 | };
192 |
193 | struct GLTF_API GLTF_Asset
194 | {
195 | UT_String copyright = "";
196 | UT_String generator = "";
197 | UT_String version;
198 | UT_String minversion = "";
199 | // extensions
200 | // extras
201 | };
202 |
203 | struct GLTF_API GLTF_Buffer
204 | {
205 | UT_String myURI = "";
206 | GLTF_Int myByteLength;
207 | UT_String name = "";
208 | // extensions
209 | // extras
210 | };
211 |
212 | struct GLTF_API GLTF_BufferView
213 | {
214 | GLTF_Handle buffer = 0; // Required
215 | GLTF_Int byteOffset = 0;
216 | GLTF_Int byteLength = 0; // Required
217 | GLTF_Int byteStride = 0;
218 | GLTF_BufferViewTarget target = GLTF_BUFFER_INVALID;
219 | UT_String name = "";
220 | // extensions
221 | // extras
222 | };
223 |
224 | struct GLTF_API GLTF_Orthographic
225 | {
226 | fpreal32 xmag;
227 | fpreal32 ymag;
228 | fpreal32 zmag;
229 | fpreal32 znear;
230 | // extensions
231 | //
232 | };
233 |
234 | struct GLTF_API GLTF_Perspsective
235 | {
236 | UT_Optional aspectRatio;
237 | fpreal32 yfov;
238 | UT_Optional zmag;
239 | fpreal32 znear;
240 | // extensions
241 | // extras
242 | };
243 |
244 | struct GLTF_API GLTF_Camera
245 | {
246 | UT_Optional orthographic;
247 | UT_Optional perspective;
248 | UT_String type = "";
249 | UT_String name = "";
250 | // extensions
251 | // extras
252 | };
253 |
254 | struct GLTF_API GLTF_Channel
255 | {
256 | GLTF_Handle sampler;
257 | GLTF_Handle target;
258 | // extensions
259 | // extras
260 | };
261 |
262 | struct GLTF_API GLTF_Images
263 | {
264 | UT_String uri;
265 | UT_String mimeType;
266 | UT_String name;
267 | // extensions
268 | // extras
269 | };
270 |
271 | struct GLTF_API GLTF_Indices
272 | {
273 | GLTF_Handle bufferView;
274 | GLTF_Int byteOffset;
275 | GLTF_ComponentType componentType;
276 | // extensions
277 | // extras
278 | };
279 |
280 | struct GLTF_API GLTF_PBRMetallicRoughness
281 | {
282 | UT_Vector4 baseColorFactor = {0.0f, 0.0f, 0.0f, 1.0f};
283 | UT_Optional baseColorTexture;
284 | fpreal32 metallicFactor = 1.0f;
285 | fpreal32 roughnessFactor = 1.0f;
286 | UT_Optional metallicRoughnessTexture;
287 | };
288 |
289 | struct GLTF_API GLTF_Material
290 | {
291 | UT_String name = "";
292 | // extensions
293 | // extras
294 | UT_Optional metallicRoughness;
295 | UT_Optional normalTexture;
296 | UT_Optional occlusionTexture;
297 | UT_Optional emissiveTexture;
298 | UT_Vector3F emissiveFactor = {0.0f, 0.0f, 0.0f};
299 | UT_String alphaMode;
300 | fpreal32 alphaCutoff;
301 | bool doubleSided;
302 | };
303 |
304 | struct GLTF_API GLTF_Primitive
305 | {
306 | UT_StringMap attributes;
307 | GLTF_Handle indices = GLTF_INVALID_IDX;
308 | GLTF_Handle material = GLTF_INVALID_IDX;
309 | GLTF_RenderMode mode = GLTF_RENDERMODE_TRIANGLES;
310 | // targets
311 | // extensions
312 | // extras
313 | };
314 |
315 | struct GLTF_API GLTF_Mesh
316 | {
317 | UT_Array primitives;
318 | // weights
319 | UT_String name;
320 | // extensions
321 | // extras
322 | };
323 |
324 | struct GLTF_API GLTF_Sampler
325 | {
326 | GLTF_TexFilter magfilter;
327 | GLTF_TexFilter minFilter;
328 | GLTF_TexWrap wrapS;
329 | GLTF_TexWrap wrapT;
330 | UT_String name;
331 | // extensions
332 | // extras
333 | };
334 |
335 | struct GLTF_API GLTF_Image
336 | {
337 | UT_String uri = "";
338 | UT_String mimeType = "";
339 | GLTF_Handle bufferView = GLTF_INVALID_IDX;
340 | UT_String name = "";
341 | // extensions
342 | // extras
343 | };
344 |
345 | struct GLTF_API GLTF_Node
346 | {
347 | GLTF_Handle camera = GLTF_INVALID_IDX;
348 | UT_Array children;
349 | GLTF_Handle skin = GLTF_INVALID_IDX;
350 | UT_Matrix4 matrix = UT_Matrix4::getIdentityMatrix();
351 | GLTF_Handle mesh = GLTF_INVALID_IDX;
352 | UT_Vector4 rotation = {0, 0, 0, 1};
353 | UT_Vector3 scale = {1, 1, 1};
354 | UT_Vector3 translation = {0, 0, 0};
355 | // weights
356 | UT_String name = "";
357 | // Extensions
358 | // Extras
359 |
360 | // GLTF standard specifies that only a Matrix OR TRS transform
361 | // is stored.
362 | GLTF_TRANSFORM_TYPE getTransformType() const;
363 | void getTransformAsMatrix(UT_Matrix4F &mat) const;
364 | };
365 |
366 | struct GLTF_API GLTF_Scene
367 | {
368 | UT_Array nodes;
369 | UT_String name = "";
370 | // extensions
371 | // extras
372 | };
373 |
374 | struct GLTF_API GLTF_Skin
375 | {
376 | // inverse bind matrices
377 | // skeleton
378 | // joins
379 | // extensions
380 | // extras
381 | };
382 |
383 | struct GLTF_API GLTF_Sparse
384 | {
385 | // count
386 | // indices
387 | // values
388 | // extensions
389 | // extras
390 | };
391 |
392 | struct GLTF_API GLTF_Target
393 | {
394 | GLTF_Node *node;
395 | UT_String path;
396 | // extensions
397 | // extras
398 | };
399 |
400 | struct GLTF_API GLTF_Texture
401 | {
402 | uint32 sampler = GLTF_INVALID_IDX;
403 | uint32 source = GLTF_INVALID_IDX;
404 | UT_String name = "";
405 | // extensions
406 | // extras
407 | };
408 |
409 | } // end GLTF_NAMESPACE
410 |
411 | #endif
412 |
--------------------------------------------------------------------------------
/src/ROP/ROP_GLTF_Image.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2018
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | #include "ROP_GLTF_Image.h"
27 |
28 | #include "ROP_GLTF.h"
29 | #include
30 | #include
31 | #include
32 | #include
33 | #include
34 | #include
35 | #include
36 | #include
37 | #include
38 | #include
39 |
40 | #include
41 |
42 | using namespace GLTF_NAMESPACE;
43 |
44 | // We use a set internal format when processing the pixels as all merged
45 | // rasters must have the same format. As jpeg & png both only support 8 bits
46 | // per color per pixel, we default to using RGB8.
47 | class theWorkFormat
48 | {
49 | public:
50 | static const PXL_DataFormat px_data_format = PXL_INT8;
51 | static const IMG_DataType img_data_format = IMG_INT8;
52 | static const IMG_ComponentOrder img_component_order = IMG_COMPONENT_RGBA;
53 | };
54 |
55 | bool
56 | ROP_GLTF_Image::OutputImageToStream(const UT_String &filename,
57 | const IMG_Format *format, std::ostream &os,
58 | fpreal time,
59 | const ROP_GLTF_ImgExportParms &parms)
60 | {
61 | UT_Array> rasters;
62 | IMG_Stat stat;
63 |
64 | if (!GetImageRasters(filename, time, rasters, stat, true))
65 | return false;
66 |
67 | if (rasters.size() == 0)
68 | return false;
69 |
70 | ApplyTransformations(stat, rasters, parms);
71 |
72 | IMG_FileParms file_parms;
73 | file_parms.setColorModel(IMG_RGBA);
74 | file_parms.setComponentOrder(theWorkFormat::img_component_order);
75 | file_parms.setDataType(theWorkFormat::img_data_format);
76 | file_parms.setOption("quality", std::to_string(parms.quality).c_str());
77 |
78 | IMG_File *file = IMG_File::create(os, stat, &file_parms, format);
79 |
80 | if (!file)
81 | return false;
82 |
83 | UT_Array ptr_rasters;
84 | for (auto raster : rasters)
85 | ptr_rasters.append(raster.get());
86 |
87 | if (!file->writeImages(ptr_rasters))
88 | {
89 | file->close();
90 | return false;
91 | }
92 |
93 | file->close();
94 | return true;
95 | }
96 |
97 | bool
98 | ROP_GLTF_Image::CreateMappedTexture(
99 | const UT_Array &mappings, std::ostream &os,
100 | const IMG_Format *format, uint32 time, const ROP_GLTF_ImgExportParms &parms,
101 | ROP_GLTF_BaseErrorManager &errormgr)
102 | {
103 | if (mappings.size() == 0)
104 | return false;
105 |
106 | UT_Array>> rasters;
107 |
108 | for (const ROP_GLTF_ChannelMapping &mapping : mappings)
109 | {
110 | UT_Array> curmap_rasters;
111 |
112 | IMG_Stat stat;
113 | if (GetImageRasters(mapping.path, time, curmap_rasters, stat, false))
114 | {
115 | rasters.append(std::move(curmap_rasters));
116 | }
117 | else if(mapping.path != "")
118 | {
119 | UT_String error("Invalid texture specified.");
120 | error.append(mapping.path);
121 | errormgr.AddWarning(UT_ERROR_MESSAGE, error);
122 | }
123 | }
124 |
125 | if (rasters.size() == 0)
126 | return false;
127 |
128 | // Find the size of the largest raster
129 | int xres = 0;
130 | int yres = 0;
131 | for (auto it = rasters.begin(); it != rasters.end(); it++)
132 | {
133 | for (UT_SharedPtr raster : *it)
134 | {
135 | xres = std::max(static_cast(raster->getXres()), xres);
136 | yres = std::max(static_cast(raster->getYres()), yres);
137 | }
138 | }
139 |
140 | auto new_raster = UT_SharedPtr(
141 | new PXL_Raster(PACK_RGB,
142 | theWorkFormat::px_data_format, xres, yres));
143 |
144 | new_raster->clear();
145 |
146 | PXL_FillParms fill_parms;
147 | fill_parms.setSourceType(theWorkFormat::px_data_format);
148 | fill_parms.setDestType(theWorkFormat::px_data_format);
149 | fill_parms.setDestArea(0, 0, xres - 1, yres - 1);
150 | fill_parms.setSourceArea(0, 0, xres - 1, yres - 1);
151 | fill_parms.mySInc = 3;
152 | fill_parms.myDInc = 3;
153 |
154 | exint idx = 0;
155 | exint num_channels = 0;
156 |
157 | for (auto it = rasters.begin(); it != rasters.end(); it++)
158 | {
159 | const UT_Array> &planes = *it;
160 | if (planes.size() == 0 ||
161 | planes[0]->getPacking() != PACK_RGB)
162 | {
163 | continue;
164 | }
165 |
166 | auto from_channel = mappings[idx].from_channel;
167 | auto to_channel = mappings[idx].to_channel;
168 |
169 | fill_parms.mySource = planes[0]->getPixel(0, 0, from_channel);
170 | fill_parms.myDest = new_raster->getPixel(0, 0, to_channel);
171 |
172 | PXL_Fill::fill(fill_parms);
173 |
174 | num_channels++;
175 | idx++;
176 | }
177 |
178 | if (num_channels == 0)
179 | return false;
180 |
181 | IMG_Stat stat(xres, yres, theWorkFormat::img_data_format, IMG_RGB);
182 |
183 | // Apply transformations
184 | UT_Array> planes { new_raster };
185 |
186 | ApplyTransformations(stat, planes, parms);
187 |
188 | new_raster = planes[0];
189 |
190 | // Save the file to our stream
191 | IMG_File *file;
192 |
193 | IMG_FileParms file_parms;
194 | file_parms.setColorModel(IMG_RGB);
195 | file_parms.setComponentOrder(theWorkFormat::img_component_order);
196 | file_parms.orientImage(IMG_ORIENT_LEFT_FIRST, IMG_ORIENT_BOTTOM_FIRST);
197 | file_parms.setInterleaved(IMG_INTERLEAVED);
198 | file_parms.setDataType(theWorkFormat::img_data_format);
199 | file_parms.setOption("quality", std::to_string(parms.quality).c_str());
200 |
201 | file = IMG_File::create(os, stat, &file_parms, format);
202 |
203 | UT_Array new_raster_arr {new_raster.get()};
204 | if (!file->writeImages(new_raster_arr))
205 | {
206 | file->close();
207 | return false;
208 | }
209 | file->close();
210 |
211 | return true;
212 | }
213 |
214 | bool
215 | ROP_GLTF_Image::OutputImage(const UT_String &filename, const IMG_Format *format,
216 | std::ostream &os, fpreal time,
217 | const ROP_GLTF_ImgExportParms &parms,
218 | ROP_GLTF_BaseErrorManager &errormgr)
219 | {
220 | // Special case for not creating errors, since some HDAs may
221 | // enable the texture parameter but not actually specify a texture
222 | if (filename == "")
223 | return false;
224 |
225 | if (filename.startsWith("op:"))
226 | {
227 | OP_Node *node = OPgetDirector()->findNode(filename);
228 | if (!node)
229 | return false;
230 |
231 | COP2_Node *copnode = node->castToCOP2Node();
232 |
233 | if (!copnode)
234 | return false;
235 |
236 | return OutputCopToStream(copnode, format, os, time, parms);
237 | }
238 |
239 | return OutputImageToStream(filename, format, os, time, parms);
240 | }
241 |
242 | bool
243 | ROP_GLTF_Image::GetImageRasters(const UT_StringHolder &filename,
244 | const uint32 time,
245 | UT_Array> &rasters,
246 | IMG_Stat &stat, bool include_alpha)
247 | {
248 | if (filename.isEmpty())
249 | return false;
250 |
251 | if (filename.startsWith("op:"))
252 | {
253 | OP_Node *node = OPgetDirector()->findNode(filename);
254 | if (!node)
255 | return false;
256 |
257 | COP2_Node *copnode = node->castToCOP2Node();
258 |
259 | if (!copnode)
260 | return false;
261 |
262 | if (!GetImageRastersFromCOP(copnode, time, rasters, stat, include_alpha))
263 | return false;
264 |
265 | return true;
266 | }
267 |
268 | if (!GetImageRastersFromFile(filename, time, rasters, stat, include_alpha))
269 | return false;
270 |
271 | return true;
272 | }
273 |
274 | bool
275 | ROP_GLTF_Image::GetImageRastersFromFile(
276 | const UT_StringHolder &filename, const uint32 time,
277 | UT_Array> &rasters, IMG_Stat &stat,
278 | bool include_alpha)
279 | {
280 | IMG_ColorModel img_model = IMG_RGB;
281 | if (include_alpha)
282 | {
283 | img_model = IMG_RGBA;
284 | }
285 |
286 |
287 | IMG_FileParms img_parms;
288 | img_parms.setColorModel(img_model);
289 | img_parms.setComponentOrder(theWorkFormat::img_component_order);
290 | img_parms.setInterleaved(IMG_INTERLEAVED);
291 | img_parms.setDataType(theWorkFormat::img_data_format);
292 | img_parms.selectPlaneNames("C");
293 |
294 | IMG_File *file = IMG_File::open(filename, &img_parms);
295 |
296 | if (file == nullptr)
297 | return false;
298 |
299 | UT_Array ptr_rasters;
300 | if (!file->readImages(ptr_rasters))
301 | {
302 | file->close();
303 | return false;
304 | }
305 |
306 | file->close();
307 |
308 | for (auto raster : ptr_rasters)
309 | rasters.append(UT_SharedPtr(raster));
310 |
311 | stat = file->getStat();
312 |
313 | return true;
314 | }
315 |
316 | bool
317 | ROP_GLTF_Image::GetImageRastersFromCOP(
318 | COP2_Node *node, const uint32 time,
319 | UT_Array> &rasters,
320 | IMG_Stat &stat,
321 | bool include_alpha)
322 | {
323 | IMG_ColorModel img_model = IMG_RGB;
324 | PXL_Packing pxl_model = PACK_RGB;
325 | if (include_alpha)
326 | {
327 | img_model = IMG_RGBA;
328 | pxl_model = PACK_RGBA;
329 | }
330 |
331 |
332 | COP2_ImageSource *is = node->getImageSource();
333 | OP_Context context(time);
334 |
335 | if (!is)
336 | return false;
337 |
338 | short key;
339 | if (!is->open(key))
340 | return false;
341 |
342 | // Automatically close the image fstream when exiting the function
343 | std::unique_ptr>
344 | image_source(std::move(is), [&](COP2_ImageSource *s) { s->close(key); });
345 |
346 | // Handle color plane
347 | const TIL_Sequence *image_seq = image_source->getSequence(0.f);
348 | if (!image_seq)
349 | return false;
350 |
351 | TIL_Sequence seq = *image_seq;
352 | seq.setSingleImage(1);
353 | seq.setStart(1);
354 | seq.setLength(1);
355 |
356 | int xres, yres;
357 | seq.getRes(xres, yres);
358 | context.setRes(xres, yres);
359 |
360 | // Handle colour plane
361 | const TIL_Plane *const_color_plane =
362 | seq.getPlane(COP2_Node::getColorPlaneName());
363 |
364 | UT_SharedPtr color_plane;
365 | if (const_color_plane)
366 | {
367 | color_plane = UT_SharedPtr(new TIL_Plane(*const_color_plane));
368 | color_plane->setScoped(1);
369 | }
370 |
371 | UT_SharedPtr color_raster;
372 |
373 | stat = IMG_Stat(xres, yres, theWorkFormat::img_data_format, img_model);
374 |
375 | if (color_plane)
376 | {
377 | color_raster = UT_SharedPtr(
378 | new TIL_Raster(pxl_model,
379 | theWorkFormat::px_data_format, xres, yres),
380 | [=](TIL_Raster *r){}
381 | );
382 |
383 | // Pack both A and C planes into a single raster
384 | if (!is->getImage(color_raster.get(), time, xres, yres, *color_plane, 0,
385 | 0, 0, xres-1 , yres -1, 1.0, true))
386 | {
387 | return false;
388 | }
389 |
390 | rasters.append(color_raster);
391 | }
392 |
393 | return true;
394 | }
395 |
396 | void
397 | ROP_GLTF_Image::ApplyTransformations(
398 | IMG_Stat &stat, UT_Array> &rasters,
399 | const ROP_GLTF_ImgExportParms &parms)
400 | {
401 | if (parms.flipGreen)
402 | {
403 | for (auto raster : rasters)
404 | {
405 | auto packing = raster->getPacking();
406 | if (packing != PACK_RGB && packing != PACK_RGBA &&
407 | packing != PACK_RGB_NI && packing != PACK_RGBA_NI)
408 | {
409 | // We're not handling a RGB(A) image, time to give up
410 | continue;
411 | }
412 |
413 | exint inc = 1;
414 | if (packing == PACK_RGB)
415 | inc = 3;
416 | else if (packing == PACK_RGBA)
417 | inc = 4;
418 | // Otherwise the raster is interleaved so the increment is 1
419 |
420 | PXL_FillParms fill_parms;
421 | fill_parms.setDestType(raster->getFormat());
422 | fill_parms.setDestArea(0, 0, stat.getXres() - 1, stat.getYres() - 1);
423 | fill_parms.myDInc = inc;
424 | fill_parms.myDest = raster->getPixel(0, 0, 1);
425 | fill_parms.myFillColor = 1;
426 | PXL_Fill::invert(fill_parms);
427 | }
428 | }
429 |
430 | auto xres = rasters[0]->getXres();
431 | auto yres = rasters[0]->getYres();
432 | if (parms.roundUpPowerOfTwo || xres > parms.max_res || yres > parms.max_res)
433 | {
434 | xres = std::min(NextPowerOfTwo(xres), parms.max_res);
435 | yres = std::min(NextPowerOfTwo(yres), parms.max_res);
436 | for (exint idx = 0; idx < rasters.size(); idx++)
437 | {
438 | auto dest = UT_SharedPtr(new PXL_Raster());
439 | TIL_Raster::scaleRasterToSize(dest.get(), rasters[idx].get(), xres, yres);
440 | rasters[idx] = dest;
441 | }
442 |
443 | stat.setResolution(xres, yres);
444 | }
445 | }
446 |
447 | exint
448 | ROP_GLTF_Image::NextPowerOfTwo(exint num)
449 | {
450 | return static_cast(
451 | SYSpow(2, SYSceil(log2(static_cast(num)))));
452 | }
453 |
454 | bool
455 | ROP_GLTF_Image::OutputCopToStream(COP2_Node *node, const IMG_Format *format,
456 | std::ostream &os, fpreal time,
457 | const ROP_GLTF_ImgExportParms &parms)
458 | {
459 | UT_Array> rasters;
460 | IMG_Stat stat;
461 |
462 | if (!GetImageRastersFromCOP(node, time, rasters, stat, true))
463 | return false;
464 |
465 | if (rasters.size() == 0)
466 | return false;
467 |
468 | ApplyTransformations(stat, rasters, parms);
469 |
470 | // glTF specifies that texel alpha values should not be premultiplied
471 | IMG_FileParms img_parms;
472 | img_parms.setColorModel(IMG_RGBA);
473 | img_parms.setComponentOrder(theWorkFormat::img_component_order);
474 | img_parms.setInterleaved(IMG_INTERLEAVED);
475 | img_parms.setDataType(theWorkFormat::img_data_format);
476 |
477 | if (!format->formatStoresColorSpace())
478 | img_parms.adjustGammaForFormat(stat, format, IMG_DT_ANY);
479 |
480 | IMG_File *file = IMG_File::create(os, stat, &img_parms, format, 0, true);
481 |
482 | // Recreate the array of dumb pointers to interface with old code
483 | UT_Array ptr_rasters;
484 |
485 | for (auto sp_raster : rasters)
486 | ptr_rasters.append(sp_raster.get());
487 |
488 | if (!file->writeImages(ptr_rasters))
489 | {
490 | file->close();
491 | return false;
492 | }
493 |
494 | file->close();
495 | return true;
496 | }
497 |
498 | bool
499 | ROP_GLTF_ChannelMapping::operator<(const ROP_GLTF_ChannelMapping &r) const
500 | {
501 | if (path < r.path)
502 | return true;
503 | if (path > r.path)
504 | return false;
505 |
506 | if (from_channel < r.from_channel)
507 | return true;
508 | if (from_channel > r.from_channel)
509 | return false;
510 |
511 | if (to_channel < r.to_channel)
512 | return true;
513 | if (to_channel > r.to_channel)
514 | return false;
515 |
516 | return false;
517 | }
518 |
--------------------------------------------------------------------------------
/src/GLTF/GLTF_GeoLoader.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 | #include "GLTF_GeoLoader.h"
28 |
29 | #include "GLTF_Loader.h"
30 | #include "GLTF_Types.h"
31 | #include "GLTF_Util.h"
32 |
33 | #include
34 | #include
35 | #include
36 | #include
37 |
38 | #include
39 |
40 | using namespace GLTF_NAMESPACE;
41 |
42 | //======================================================================
43 |
44 | UT_StringHolder
45 | GLTF_MapAttribName(const UT_String &name)
46 | {
47 | // Convert UV's
48 | if (name == "TEXCOORD_0")
49 | return "uv";
50 | if (name.startsWith("TEXCOORD_"))
51 | {
52 | UT_String name_copy = name;
53 | name_copy.eraseHead(9);
54 | if (name_copy.isInteger())
55 | {
56 | int uv_idx = name_copy.toInt();
57 | UT_String gltf_uvname("uv");
58 | gltf_uvname.append(std::to_string(uv_idx + 1).c_str());
59 | return UT_StringHolder(gltf_uvname.c_str());
60 | }
61 | // Falls through here
62 | }
63 | if (name == "NORMAL")
64 | return "N";
65 | if (name == "TANGENT")
66 | return "tangentu";
67 | if (name == "COLOR_0")
68 | return "Cd";
69 |
70 | return name;
71 | }
72 |
73 | bool
74 | GLTF_IsAttributeCustom(UT_String name)
75 | {
76 | if (name.startsWith("_"))
77 | return true;
78 | return false;
79 | }
80 |
81 | //
82 | // Fills the attrib with the given GLTF buffer, applying operation
83 | // to convert types or perform other operations.
84 | //
85 | template
86 | static bool
87 | FillAttrib(GA_Detail &detail, GA_AttributeOwner owner, const UT_StringRef &name,
88 | unsigned char *attrib_data, uint32 attrib_stride,
89 | std::function operation)
90 | {
91 | GA_RWHandleT accessor_handle(&detail, owner, name);
92 | if (!accessor_handle.isValid())
93 | return false;
94 |
95 | uint32 i = 0;
96 | for (GA_Iterator it(detail.getPointRange()); !it.atEnd(); ++it, ++i)
97 | {
98 | T elem =
99 | GLTF_Util::readInterleavedElement(attrib_data, attrib_stride, i);
100 |
101 | GA_Offset offset = *it;
102 | accessor_handle.set(offset, operation(elem));
103 | }
104 |
105 | return true;
106 | }
107 |
108 | template
109 | static bool
110 | FillAttrib(GA_Detail &detail, GA_AttributeOwner owner, const UT_StringRef &name,
111 | unsigned char *attrib_data, uint32 attrib_stride)
112 | {
113 | GA_RWHandleT accessor_handle(&detail, owner, name);
114 | if (!accessor_handle.isValid())
115 | return false;
116 |
117 | uint32 i = 0;
118 | for (GA_Iterator it(detail.getPointRange()); !it.atEnd(); ++it, ++i)
119 | {
120 | T elem =
121 | GLTF_Util::readInterleavedElement(attrib_data, attrib_stride, i);
122 |
123 | GA_Offset offset = *it;
124 | accessor_handle.set(offset, elem);
125 | }
126 |
127 | return true;
128 | }
129 |
130 | //======================================================================
131 |
132 | bool
133 | GLTF_GeoLoader::load(const GLTF_Loader &loader, GLTF_Handle mesh_idx,
134 | GLTF_Handle primitive_idx, GU_Detail &detail,
135 | const GLTF_MeshLoadingOptions options)
136 | {
137 | GLTF_GeoLoader geoload(loader, mesh_idx, primitive_idx, options);
138 | return geoload.loadIntoDetail(detail);
139 | }
140 |
141 |
142 | bool
143 | GLTF_GeoLoader::loadIntoDetail(GU_Detail &detail)
144 | {
145 | const GLTF_Mesh *mesh = myLoader.getMesh(myMeshIdx);
146 |
147 | if (!mesh)
148 | return false;
149 |
150 | if (mesh->primitives.size() <= myPrimIdx)
151 | return false;
152 |
153 | const GLTF_Primitive &primitive = mesh->primitives[myPrimIdx];
154 |
155 | if (primitive.mode != GLTF_RENDERMODE_TRIANGLES)
156 | return false;
157 |
158 | // Load points & vertices attributes
159 | auto position_attribute = primitive.attributes.find("POSITION");
160 | if (position_attribute == primitive.attributes.end())
161 | return false;
162 |
163 | const GLTF_Accessor *position = myLoader.getAccessor(position_attribute->second);
164 | if (position == nullptr)
165 | return false;
166 |
167 | const GLTF_Accessor *indices = myLoader.getAccessor(primitive.indices);
168 | if (indices != nullptr)
169 | {
170 | if (!LoadVerticesAndPoints(detail, myOptions, *position, *indices))
171 | return false;
172 | }
173 | else if (!LoadVerticesAndPointsNonIndexed(detail, *position))
174 | return false;
175 |
176 | detail.bumpDataIdsForAddOrRemove(true, true, true);
177 |
178 | // Now for any other attribute, load it as a point attribute
179 | for (const auto &attrib : primitive.attributes)
180 | {
181 | UT_String attrib_name(attrib.first.c_str());
182 |
183 | // Position is treated specially (see LoadVerticesAndPoints)
184 | if (attrib.first == "POSITION")
185 | continue;
186 |
187 | bool custom_attrib = GLTF_IsAttributeCustom(attrib_name);
188 | if (myOptions.loadCustomAttribs || !custom_attrib)
189 | {
190 | // Erase the _
191 | if (custom_attrib)
192 | attrib_name.eraseHead(1);
193 |
194 | const GLTF_Accessor &attrib_acc =
195 | *myLoader.getAccessor(attrib.second);
196 |
197 | if (!AddPointAttribute(detail, attrib_name, attrib_acc))
198 | return false;
199 | }
200 | }
201 |
202 | // Handle the case when the exporter decides to use a single bufferview
203 | // for multiple submeshes (I've only seen this on the Unity exporter).
204 | // This could be handled more efficiently by only loading the points
205 | // referenced by the accessor.
206 | detail.destroyUnusedPoints();
207 |
208 | if (myOptions.promotePointAttribs)
209 | {
210 | // Promote all point attributes to vert attributes
211 | //
212 | // Note that we can't do this easily while iterating over
213 | // GA_AttributeDict, since GU_Promote will remove items from its
214 | // UT_ArrayStringMap while we are iterating over it through a proxy
215 | // iterator ... Using a temporary array is the simplest.
216 | UT_Array to_promote;
217 |
218 | for (auto attribute : detail.getAttributeDict(GA_ATTRIB_POINT))
219 | {
220 | // Skip Position attribute
221 | if (attribute->getName() == "P")
222 | continue;
223 |
224 | if (attribute->getScope() != GA_AttributeScope::GA_SCOPE_PUBLIC)
225 | continue;
226 |
227 | to_promote.append(attribute);
228 | }
229 |
230 | for (auto attribute : to_promote)
231 | GU_Promote::promote(detail, attribute, GA_ATTRIB_VERTEX);
232 |
233 | // Consolidate points
234 | if (myOptions.consolidatePoints)
235 | {
236 | detail.onlyConsolidatePoints(
237 | myOptions.pointConsolidationDistance, nullptr, 0, true,
238 | GU_Detail::ONLYCONS_GRP_PROP_LEAST, true);
239 | }
240 |
241 | }
242 |
243 | return true;
244 | }
245 |
246 | bool
247 | GLTF_GeoLoader::AddPointAttribute(GU_Detail &detail,
248 | const UT_StringHolder& attrib_name,
249 | const GLTF_Accessor &accessor)
250 | {
251 | unsigned char *attrib_data;
252 | GA_RWHandleT accessor_handle;
253 |
254 | if (!myLoader.LoadAccessorData(accessor, attrib_data))
255 | return false;
256 |
257 | const GLTF_BufferView &bufferview = *myLoader.getBufferView(accessor.bufferView);
258 |
259 | uint32 attrib_stride = GLTF_Util::getStride(
260 | bufferview.byteStride, accessor.type, accessor.componentType);
261 |
262 | const UT_StringHolder houdini_attrib_name =
263 | GLTF_MapAttribName(attrib_name.c_str());
264 |
265 | const uint32 num_elements = GLTF_Util::typeGetElements(accessor.type);
266 |
267 | // TODO: We are typecasting uint32 to int32
268 | if (accessor.componentType == GLTF_COMPONENT_FLOAT)
269 | {
270 | if (num_elements == 1)
271 | {
272 | detail.addFloatTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
273 | houdini_attrib_name, 1);
274 |
275 | FillAttrib(detail, GA_ATTRIB_POINT, houdini_attrib_name,
276 | attrib_data, attrib_stride);
277 | }
278 | else if (num_elements == 2)
279 | {
280 | if (houdini_attrib_name == "uv" || houdini_attrib_name == "uv2")
281 | {
282 | const auto flip_uvs = [](UT_Vector2F tex_coord) {
283 | return UT_Vector3F(tex_coord.x(), 1.f - tex_coord.y(), 0);
284 | };
285 |
286 | detail.addFloatTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
287 | houdini_attrib_name, 3);
288 |
289 | FillAttrib(
290 | detail, GA_ATTRIB_POINT, houdini_attrib_name, attrib_data,
291 | attrib_stride, flip_uvs);
292 | }
293 | else
294 | {
295 | detail.addFloatTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
296 | houdini_attrib_name, 2);
297 |
298 | FillAttrib(detail, GA_ATTRIB_POINT,
299 | houdini_attrib_name, attrib_data,
300 | attrib_stride);
301 | }
302 | }
303 | else if (num_elements == 3)
304 | {
305 | if (houdini_attrib_name == "N")
306 | {
307 | detail.addNormalAttribute(GA_ATTRIB_POINT, GA_STORE_REAL32);
308 | }
309 | else
310 | {
311 | detail.addFloatTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
312 | houdini_attrib_name, 3);
313 | }
314 |
315 | FillAttrib(detail, GA_ATTRIB_POINT,
316 | houdini_attrib_name, attrib_data,
317 | attrib_stride);
318 | }
319 | else if (num_elements == 4)
320 | {
321 | detail.addFloatTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
322 | houdini_attrib_name, 4);
323 | FillAttrib(detail, GA_ATTRIB_POINT,
324 | houdini_attrib_name, attrib_data,
325 | attrib_stride);
326 | }
327 | else
328 | {
329 | UT_ASSERT(false);
330 | }
331 | }
332 | else if (accessor.componentType == GLTF_COMPONENT_UNSIGNED_BYTE ||
333 | accessor.componentType == GLTF_COMPONENT_UNSIGNED_SHORT ||
334 | accessor.componentType == GLTF_COMPONENT_UNSIGNED_INT)
335 | {
336 | detail.addIntTuple(GA_ATTRIB_POINT, GA_SCOPE_PUBLIC,
337 | houdini_attrib_name, num_elements);
338 |
339 | if (num_elements == 1)
340 | {
341 | FillAttrib(detail, GA_ATTRIB_POINT, houdini_attrib_name,
342 | attrib_data, attrib_stride);
343 | }
344 | else if (num_elements == 2)
345 | {
346 | FillAttrib(detail, GA_ATTRIB_POINT,
347 | houdini_attrib_name, attrib_data,
348 | attrib_stride);
349 | }
350 | else if (num_elements == 3)
351 | {
352 | FillAttrib(detail, GA_ATTRIB_POINT,
353 | houdini_attrib_name, attrib_data,
354 | attrib_stride);
355 | }
356 | else if (num_elements == 4)
357 | {
358 | FillAttrib(detail, GA_ATTRIB_POINT,
359 | houdini_attrib_name, attrib_data,
360 | attrib_stride);
361 | }
362 | else
363 | {
364 | UT_ASSERT(false);
365 | }
366 | }
367 |
368 | return true;
369 | }
370 |
371 | bool
372 | GLTF_GeoLoader::LoadVerticesAndPoints(GU_Detail &detail,
373 | const GLTF_MeshLoadingOptions &options,
374 | const GLTF_Accessor &pos,
375 | const GLTF_Accessor &ind)
376 | {
377 |
378 | // We convert everything to indexed triangle meshes, so the number
379 | // of indices must divisible by 3
380 | if (ind.count % 3 != 0)
381 | return false;
382 |
383 | GLTF_BufferView pos_bv = *myLoader.getBufferView(pos.bufferView);
384 |
385 | // Load the vertex data from the binary into a buffer
386 | unsigned char *position_data;
387 | if (!myLoader.LoadAccessorData(pos, position_data))
388 | return false;
389 |
390 | uint32 pos_stride = GLTF_Util::getStride(pos_bv.byteStride, pos.type,
391 | pos.componentType);
392 |
393 | // Read in the vertices from the (potentially) interleaved array
394 | GA_Offset start_pt_off = detail.appendPointBlock(pos.count);
395 | for (uint32 i = 0; i < pos.count; i++)
396 | {
397 | UT_Vector3F vec = GLTF_Util::readInterleavedElement(
398 | position_data, pos_stride, i);
399 | detail.setPos3(start_pt_off + i, vec);
400 | }
401 |
402 | // Wire up indices
403 | unsigned char *indice_data;
404 | const GLTF_BufferView &indBV = *myLoader.getBufferView(ind.bufferView);
405 | if (!myLoader.LoadAccessorData(ind, indice_data))
406 | return false;
407 |
408 | uint32 ind_stride = GLTF_Util::getStride(indBV.byteStride, ind.type,
409 | ind.componentType);
410 | const uint32 num_tris = ind.count / 3;
411 |
412 | GA_Offset start_vtxoff;
413 | detail.appendPrimitivesAndVertices(GA_PRIMPOLY, ind.count / 3, 3,
414 | start_vtxoff, true);
415 |
416 | uint32 tri_vert_indexes[3];
417 | for (uint32 tri_idx = 0; tri_idx < num_tris; tri_idx++)
418 | {
419 | for (uint32 tri_vert_num = 0; tri_vert_num < 3; tri_vert_num++)
420 | {
421 | const uint32 indice = tri_idx * 3 + tri_vert_num;
422 |
423 | uint32 elem = 0;
424 | if (ind.componentType ==
425 | GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_BYTE)
426 | {
427 | elem = GLTF_Util::readInterleavedElement(
428 | indice_data, ind_stride, indice);
429 | }
430 | else if (ind.componentType ==
431 | GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_SHORT)
432 | {
433 | elem = GLTF_Util::readInterleavedElement(
434 | indice_data, ind_stride, indice);
435 | }
436 | else if (ind.componentType ==
437 | GLTF_ComponentType::GLTF_COMPONENT_UNSIGNED_INT)
438 | {
439 | elem = GLTF_Util::readInterleavedElement(
440 | indice_data, ind_stride, indice);
441 | }
442 | else
443 | {
444 | return false;
445 | }
446 |
447 | tri_vert_indexes[tri_vert_num] = start_vtxoff + elem;
448 | }
449 |
450 | const uint32 cur_tri_off = start_pt_off + tri_idx * 3;
451 |
452 | detail.getTopology().wireVertexPoint(
453 | static_cast(cur_tri_off + 0),
454 | static_cast(tri_vert_indexes[0]));
455 | // Swap second and third vertex indexes to reverse tri winding order
456 | detail.getTopology().wireVertexPoint(
457 | static_cast(cur_tri_off + 2),
458 | static_cast(tri_vert_indexes[1]));
459 | detail.getTopology().wireVertexPoint(
460 | static_cast(cur_tri_off + 1),
461 | static_cast(tri_vert_indexes[2]));
462 | }
463 |
464 | return true;
465 | }
466 |
467 | bool
468 | GLTF_GeoLoader::LoadVerticesAndPointsNonIndexed(GU_Detail &detail, const GLTF_Accessor &pos)
469 | {
470 | // We convert everything to triangle meshes, so the number
471 | // of vertices must divisible by 3
472 | if (pos.count % 3 != 0)
473 | return false;
474 |
475 | const uint32 num_tris = pos.count / 3;
476 |
477 | GLTF_BufferView pos_bv = *myLoader.getBufferView(pos.bufferView);
478 |
479 | // Load the vertex data from the binary into a buffer
480 | unsigned char *position_data;
481 | if (!myLoader.LoadAccessorData(pos, position_data))
482 | return false;
483 |
484 | uint32 pos_stride = GLTF_Util::getStride(pos_bv.byteStride, pos.type,
485 | pos.componentType);
486 |
487 | // Read in the vertices from the (potentially) interleaved array
488 | GA_Offset start_pt_off = detail.appendPointBlock(pos.count);
489 | for (uint32 i = 0; i < pos.count; i++)
490 | {
491 | UT_Vector3F vec = GLTF_Util::readInterleavedElement(
492 | position_data, pos_stride, i);
493 | detail.setPos3(start_pt_off + i, vec);
494 | }
495 |
496 | GA_Offset start_vtxoff;
497 | detail.appendPrimitivesAndVertices(GA_PRIMPOLY, num_tris, 3,
498 | start_vtxoff, true);
499 |
500 | for (uint32 tri_idx = 0; tri_idx < num_tris; tri_idx++)
501 | {
502 | const uint32 cur_tri_off = start_pt_off + (tri_idx * 3);
503 | const uint32 vtx_tri_off = start_vtxoff + (tri_idx * 3);
504 |
505 | detail.getTopology().wireVertexPoint(
506 | static_cast(cur_tri_off + 0),
507 | static_cast(vtx_tri_off + 0));
508 | // Swap second and third vertex indexes to reverse tri winding order
509 | detail.getTopology().wireVertexPoint(
510 | static_cast(cur_tri_off + 2),
511 | static_cast(vtx_tri_off + 1));
512 | detail.getTopology().wireVertexPoint(
513 | static_cast(cur_tri_off + 1),
514 | static_cast(vtx_tri_off + 2));
515 | }
516 |
517 | return true;
518 | }
519 |
520 | GLTF_GeoLoader::GLTF_GeoLoader(const GLTF_Loader &loader, GLTF_Handle mesh_idx,
521 | GLTF_Handle primitive_idx,
522 | const GLTF_MeshLoadingOptions& options)
523 | : myMeshIdx(mesh_idx),
524 | myPrimIdx(primitive_idx),
525 | myLoader(loader),
526 | myOptions(options)
527 | {
528 | }
529 |
--------------------------------------------------------------------------------
/src/gltf_hierarchy.hda/Object_1gltf__hierarchy/PythonModule:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 | import hou
4 | import string
5 | import struct
6 | import tempfile
7 | import base64
8 | import _gltf_hom_extensions as _gltf
9 |
10 | try:
11 | from hou import ui
12 | except:
13 | ui = None
14 |
15 |
16 | ##############################################
17 | # General utils
18 |
19 | # Throw an error and exit if we give it an invalid dir
20 |
21 | # (This is taken from Alembic Archive exporter)
22 | _sanitizeTable = None
23 |
24 | def _sanitizeName(name):
25 | def valid(i):
26 | ch = chr(i)
27 | if ch.isalnum():
28 | return ch
29 | return '_'
30 | global _sanitizeTable
31 | if not _sanitizeTable:
32 | letters = ''.join([string.letters, string.digits, '_'])
33 | # Full alphabet
34 | alpha = ''.join(map(chr, range(256)))
35 | xlate = ''.join(map(valid, range(256)))
36 | _sanitizeTable = string.maketrans(alpha, xlate)
37 | name = string.translate(name, _sanitizeTable)
38 | if name[0].isdigit():
39 | name = '_' + name
40 | return name
41 |
42 | ###############################################
43 | # Parm utils
44 |
45 | def _setNodeName(node, name):
46 | if len(name) == 0:
47 | return
48 | sanitized_name = _sanitizeName(name)
49 | node.setName(sanitized_name, True)
50 |
51 | def _setParmValue(node, parmname, value):
52 | parm = node.parm(parmname)
53 | if parm:
54 | parm.set(value)
55 |
56 | ##################################################
57 |
58 | def _new_geonode(parent, name, filename, mesh_id, locked, custom_attribs, promote_point_attrs, point_consolidation_dist):
59 | geo = parent.createNode("geo")
60 | gltf_sop = geo.createNode("gltf")
61 | _setNodeName(gltf_sop, name)
62 |
63 | _setParmValue(gltf_sop, "filename", filename)
64 | _setParmValue(gltf_sop, "loadby", "mesh")
65 | _setParmValue(gltf_sop, "meshid", mesh_id)
66 | _setParmValue(gltf_sop, "materialassigns", True)
67 | _setParmValue(gltf_sop, "usecustomattribs", custom_attribs)
68 | _setParmValue(gltf_sop, "promotepointattrs", promote_point_attrs)
69 | _setParmValue(gltf_sop, "pointconsolidatedist", point_consolidation_dist)
70 | gltf_sop.setHardLocked(locked)
71 |
72 | return geo, gltf_sop
73 |
74 | def _new_matnet(parent, name):
75 | matnet = parent.createNode("matnet")
76 | _setNodeName(matnet, name)
77 | return matnet
78 |
79 | def _new_pbr_node(parent):
80 | pbr_shadernode = parent.createNode('principledshader')
81 |
82 | return pbr_shadernode
83 |
84 | def _get_suffix_from_mimetype(mimetype):
85 | if mimetype == "image/jpeg":
86 | return ".jpg"
87 | elif mimetype == "image/png":
88 | return ".png"
89 | else:
90 | raise Exception("Invalid image mimeType!")
91 |
92 | # Creates a new null node based on the parameters given in "node"
93 | # The node has no transform if "node" contains no TRS or affine
94 | # matrix translation.
95 | def set_transform(node, gltf_node):
96 | if "matrix" in gltf_node:
97 | matrix = hou.Matrix4(gltf_node["matrix"])
98 | node.setParmTransform(matrix)
99 | node.setParmTransform(matrix)
100 |
101 | if "translation" in gltf_node:
102 | translation = hou.Vector3(gltf_node["translation"])
103 | node.parmTuple("t").set(translation)
104 |
105 | if "rotation" in gltf_node:
106 | rotation = hou.Quaternion(gltf_node["rotation"])
107 |
108 | node.parmTuple("r").set(rotation.extractEulerRotates())
109 |
110 | if "scale" in gltf_node:
111 | scale = hou.Vector3(gltf_node["scale"])
112 | node.parmTuple("s").set(scale)
113 |
114 | return node
115 |
116 | ##################################################
117 | class GLTF_Scene:
118 | def __init__(self, root_node, filename, json_obj, is_glb, settings):
119 | self.json_obj = json_obj
120 | self.filename = filename
121 | file_head, file_tail = os.path.split(filename)
122 | file_name, file_ext = os.path.splitext(file_tail)
123 | self.file_tail = file_name
124 | self.root_node = root_node
125 | self.base_folder = os.path.dirname(filename)
126 | self.material_cache = dict()
127 | self.image_cache = dict()
128 | self.is_glb = is_glb
129 | self.tmp_img_ctr = 0
130 | self.buffer_data = [None]
131 | self.settings = settings
132 |
133 | # The number of times a name has appearaed
134 | self.name_set = dict()
135 | if "materials" in json_obj:
136 | for material in json_obj["materials"]:
137 | if not "name" in material:
138 | continue
139 | name = material["name"]
140 | appearances = 0
141 | if name in self.name_set:
142 | appearances = self.name_set[name]
143 | self.name_set[name] = appearances + 1
144 |
145 | if "buffers" in json_obj:
146 | self.buffer_data = [None] * len(json_obj["buffers"])
147 | if "nodes" in json_obj:
148 | self.node_hasgeo = [None] * len(json_obj["nodes"])
149 |
150 | def determine_nodes_with_geo(self):
151 | if not "nodes" in self.json_obj:
152 | return
153 |
154 | def recurs(node, idx):
155 | if self.node_hasgeo[idx]:
156 | return True
157 |
158 | if "mesh" in node:
159 | self.node_hasgeo[idx] = True
160 | return True
161 |
162 | if "children" in node:
163 | for child in node["children"]:
164 | if(recurs(self.json_obj["nodes"][child], idx)):
165 | self.node_hasgeo[idx] = True
166 | return True
167 |
168 | return False
169 |
170 |
171 | for (idx, node) in enumerate(self.json_obj["nodes"]):
172 | recurs(node, idx)
173 |
174 | def load_flattened(self, scene):
175 | self.init_networks()
176 |
177 | c_atrbs = self.settings["import_cust_attribs"]
178 | lock = self.settings["lock_geo"]
179 | filename = self.settings["filename"]
180 |
181 | geo = self.root_node.createNode("geo")
182 | gltf_sop = geo.createNode("gltf")
183 |
184 | _setParmValue(gltf_sop, "filename", filename)
185 | _setParmValue(gltf_sop, "loadby", "scene")
186 | _setParmValue(gltf_sop, "scene", 0)
187 | _setParmValue(gltf_sop, "usecustomattribs", c_atrbs)
188 | _setParmValue(gltf_sop, "loadMeshNames", True)
189 | _setParmValue(gltf_sop, "materialassigns", self.settings["import_mats"])
190 |
191 | gltf_sop.setHardLocked(lock)
192 |
193 | if self.settings["import_mats"]:
194 | if self.settings["import_mats"] and "materials" in self.json_obj:
195 | for idx in xrange(len(self.json_obj["materials"])):
196 | self.import_material(idx)
197 |
198 | return geo, gltf_sop
199 |
200 | # This is seperated from the constructor, because this class can
201 | # potentially also be used for purposes other than building geometry
202 | def load(self):
203 | self.init_networks()
204 |
205 | if self.settings["import_unusedmats"] and "materials" in self.json_obj:
206 | for idx in xrange(len(self.json_obj["materials"])):
207 | self.import_material(idx)
208 |
209 | self.determine_nodes_with_geo()
210 |
211 | def get_abspath(self, filename):
212 | return os.path.join(os.path.abspath(self.base_folder),
213 | filename).replace("\\", "/")
214 |
215 | def init_networks(self):
216 | self.matnet = _new_matnet(self.root_node, "materials")
217 |
218 | def get_bufferview_data(self, bufferview_idx):
219 | bufferview = self.json_obj["bufferViews"][bufferview_idx]
220 | buffer_data = self.buffer_data[bufferview["buffer"]]
221 |
222 | byte_offset = 0
223 | if "byteOffset" in bufferview:
224 | byte_offset = bufferview["byteOffset"]
225 | byte_length = bufferview["byteLength"]
226 |
227 | return buffer_data[byte_offset : byte_offset + byte_length]
228 |
229 | def get_buffer_data(self, buffer_idx):
230 | if self.buffer_data[buffer_idx] == None:
231 | self.load_buffer(buffer_idx)
232 | return self.buffer_data[buffer_idx]
233 |
234 | def load_buffer(self, buffer_idx):
235 | buffer = self.json_obj["buffers"][buffer_idx]
236 |
237 | if "uri" in buffer:
238 | buffer_uri = buffer["uri"]
239 | with open(buffer_uri, "r") as f:
240 | buffer_data = f.read(length)
241 | self.buffer_data[buffer_idx] = buffer_data
242 | return
243 | else:
244 | raise Exception("TODO: Implement b64 buffer support!")
245 |
246 | def parse_scene(self, scene_id):
247 |
248 | scene = self.json_obj["scenes"][scene_id]
249 | for node in scene["nodes"]:
250 | self.walk_node(node, None)
251 |
252 | def walk_node(self, node_id, parent):
253 | if not self.node_hasgeo[node_id] and not self.settings["import_non_geo"]:
254 | return
255 |
256 | node = self.json_obj["nodes"][node_id]
257 |
258 | # Import each submesh
259 | if "mesh" in node and self.settings["import_geometry"]:
260 | new_node = self.create_mesh(node["mesh"])
261 | else:
262 | new_node = self.root_node.createNode("null")
263 |
264 | set_transform(new_node, node)
265 |
266 | if "name" in node:
267 | _setNodeName(new_node, node["name"])
268 |
269 | if parent != None:
270 | new_node.setNextInput(parent)
271 |
272 | # Import each child
273 | if "children" in node:
274 | for child in node["children"]:
275 | self.walk_node(child, new_node)
276 |
277 | def new_image_filename(self, image, image_idx, suffix):
278 | name = self.settings["asset_folder"]
279 | name = name + self.file_tail + "_" + str(image_idx)
280 | if "name" in image:
281 | name = name + "_" + image["name"]
282 | name = name + suffix
283 | return name
284 |
285 | def get_image_uri(self, image_idx):
286 | if image_idx in self.image_cache:
287 | return self.image_cache[image_idx]
288 |
289 | image = self.json_obj["images"][image_idx]
290 | if "uri" in image:
291 | # Unpack the image from
292 | uri = image["uri"]
293 | if uri[:5] == 'data:':
294 | data_start = uri.find(';base64,')
295 | if data_start != -1:
296 | data = uri[data_start+8:]
297 | mimetype = uri[5:data_start]
298 | suffix = _get_suffix_from_mimetype(mimetype)
299 | new_filename = self.new_image_filename(image, image_idx, suffix)
300 | with open(new_filename, 'wb') as newFile:
301 | newFile.write(base64.b64decode(data))
302 | final_path = newFile.name.replace("\\", "/")
303 | self.image_cache[image_idx] = final_path
304 | return final_path
305 |
306 | # The image is already external - just return the path
307 | return self.get_abspath(image["uri"])
308 |
309 | # Unpack the image from the binary buffer
310 | if "bufferView" in image:
311 |
312 | suffix = _get_suffix_from_mimetype(image["mimeType"])
313 |
314 | data = self.get_bufferview_data(image["bufferView"])
315 |
316 | new_filename = self.new_image_filename(image, image_idx, suffix)
317 | with open(new_filename, 'wb') as newFile:
318 | newFile.write(data)
319 | final_path = newFile.name.replace("\\", "/")
320 | self.image_cache[image_idx] = final_path
321 | return final_path
322 |
323 | def _translate_normal_texture(self, pbr_node, normal_texture):
324 | _setParmValue(pbr_node, "baseBumpAndNormal_enable", True)
325 |
326 | image_idx = normal_texture["index"]
327 | img_path = self.get_image_uri(image_idx)
328 |
329 | _setParmValue(pbr_node, "baseNormal_texture", img_path)
330 |
331 | if "scale" in normal_texture:
332 | _setParmValue(pbr_node, "baseNormal_scale", normal_texture["scale"])
333 |
334 | def _translate_emissive_texture(self, pbr_node, emissive_texture):
335 | image = self.json_obj["images"][emissive_texture["index"]]
336 | _setParmValue(pbr_node, "emitcolor_useTexture", True)
337 |
338 | image_idx = emissive_texture["index"]
339 | img_path = self.get_image_uri(image_idx)
340 | _setParmValue(pbr_node, "emitcolor_texture", img_path)
341 |
342 | def _translate_metallic_roughness(self, pbr_node, mr_params):
343 | if "baseColorFactor" in mr_params:
344 | pbr_node.parmTuple("basecolor").set(mr_params["baseColorFactor"][:3])
345 | # Alpha is stored seperately from color in Principled Shader
346 | _setParmValue(pbr_node, "opac", mr_params["baseColorFactor"][3])
347 | else:
348 | pbr_node.parmTuple("basecolor").set([1, 1, 1])
349 | _setParmValue(pbr_node, "opac", 1)
350 |
351 | if "baseColorTexture" in mr_params:
352 | _setParmValue(pbr_node, "basecolor_useTexture", True)
353 | texture = self.json_obj["textures"][mr_params["baseColorTexture"]["index"]]
354 | if "source" in texture:
355 | image_idx = texture["source"]
356 | imgpath = self.get_image_uri(image_idx)
357 | _setParmValue(
358 | pbr_node,
359 | "basecolor_texture",
360 | imgpath
361 | )
362 |
363 | if "metallicFactor" in mr_params:
364 | _setParmValue(pbr_node, "metallic", mr_params["metallicFactor"])
365 | else:
366 | _setParmValue(pbr_node, "metallic", 1)
367 |
368 | if "roughnessFactor" in mr_params:
369 | _setParmValue(pbr_node, "rough", mr_params["roughnessFactor"])
370 | else:
371 | _setParmValue(pbr_node, "rough", 1)
372 |
373 | if "metallicRoughnessTexture" in mr_params:
374 | _setParmValue(pbr_node, "metallic_useTexture", True)
375 | _setParmValue(pbr_node, "rough_useTexture", True)
376 | texture = self.json_obj["textures"][mr_params["metallicRoughnessTexture"]["index"]]
377 | if "source" in texture:
378 | image_idx = texture["source"]
379 | img_path = self.get_image_uri(image_idx)
380 | _setParmValue(pbr_node, "baseNormal_texture", img_path)
381 |
382 | # Hook up metal/roughness material to texture
383 | _setParmValue(pbr_node, "metallic_texture", img_path)
384 | _setParmValue(pbr_node, "metallic_monoChannel", 3)
385 |
386 | _setParmValue(pbr_node, "rough_texture", img_path)
387 | _setParmValue(pbr_node, "rough_monoChannel", 2)
388 |
389 | # We use a naming scheme material_name_idx for cases of duplicate
390 | # materials
391 | def get_material_name(self, mat_idx, mat_name):
392 | is_duplicate = mat_name in self.name_set and self.name_set[mat_name] > 1
393 | if is_duplicate:
394 | return mat_name + "_" + str(mat_idx)
395 |
396 | return mat_name
397 |
398 | def import_material(self, material_id):
399 | # Check if we've created the material already
400 | if material_id in self.material_cache:
401 | return self.material_cache[material_id]
402 |
403 | material = self.json_obj["materials"][material_id]
404 |
405 | pbr_node = _new_pbr_node(self.matnet)
406 | if "name" in material:
407 | name = self.get_material_name(material_id, material["name"])
408 | else:
409 | name = self.get_material_name(material_id, pbr_node.name())
410 | _setNodeName(pbr_node, name)
411 |
412 | if "pbrMetallicRoughness" in material:
413 | self._translate_metallic_roughness(pbr_node, material["pbrMetallicRoughness"])
414 |
415 | if "normalTexture" in material:
416 | self._translate_normal_texture(pbr_node, material["normalTexture"])
417 |
418 | if "emissiveTexture" in material:
419 | self._translate_emissive_texture(pbr_node, material["emissiveTexture"])
420 |
421 | if "emissiveFactor" in material:
422 | pbr_node.parmTuple("emitcolor").set(material["emissiveFactor"])
423 |
424 | # TODO: Handle other material properties
425 | self.material_cache[material_id] = pbr_node
426 |
427 | return pbr_node
428 |
429 | def import_primitive(self, mesh, mesh_name, mesh_id):
430 | geonode, gltf_geo_node = _new_geonode(
431 | self.root_node,
432 | mesh_name,
433 | self.filename,
434 | mesh_id,
435 | self.settings["lock_geo"],
436 | self.settings["import_cust_attribs"],
437 | self.settings["promote_point_attrs"],
438 | self.settings["point_consolidation_dist"]
439 | )
440 |
441 | for idx, prim in enumerate(mesh["primitives"]):
442 | if "material" in prim and self.settings["import_mats"]:
443 | material_node = self.import_material(prim["material"])
444 |
445 | return geonode
446 |
447 | def create_mesh(self, mesh_id):
448 | mesh = self.json_obj["meshes"][mesh_id]
449 |
450 | if "name" in mesh:
451 | name = mesh["name"]
452 | else:
453 | name = ""
454 |
455 | mesh_node = self.import_primitive(mesh, name, mesh_id)
456 |
457 | return mesh_node
458 |
459 | ##################################################
460 |
461 | def _destroy_children(rootnode):
462 | for child in rootnode.children():
463 | child.destroy()
464 |
465 | def LoadGLB(rootnode, filename, settings):
466 | with open(filename, "rb") as glb_file:
467 |
468 | content = glb_file.read()
469 |
470 | offset = 0
471 | header = struct.unpack_from("4sII", content)
472 | ver = header[1]
473 |
474 | if ver != 2:
475 | raise Exception("Attempted to load unsupported GLB version")
476 |
477 | offset = 12
478 |
479 | # Read in JSON chunk
480 | json_chunk = struct.unpack_from("I4s", content, offset)
481 |
482 | chunk_length = json_chunk[0]
483 | chunk_type = json_chunk[1]
484 |
485 | json_data = content[offset + 8 :offset + 8 + chunk_length]
486 | json_obj = json.loads(json_data)
487 |
488 | offset = offset + 8 + chunk_length
489 |
490 | # Read in DATA chunk
491 | data_chunk = struct.unpack_from("I4s", content, offset)
492 | data_chunk_length = data_chunk[0]
493 | data_chunk_type = data_chunk[1]
494 | bin_data = content[offset + 8 : offset + 8 + data_chunk_length]
495 |
496 | scene = GLTF_Scene(rootnode, filename, json_obj, True, settings)
497 | scene.buffer_data[0] = bin_data
498 |
499 | if settings["flattenhierarchy"]:
500 | scene.load_flattened(settings["scene"])
501 | else:
502 | scene.load()
503 | scene.parse_scene(settings["scene"])
504 |
505 | rootnode.layoutChildren()
506 | scene.matnet.layoutChildren()
507 |
508 | def LoadGLTF(rootnode, filename, settings):
509 | json_obj = json.load(open(filename))
510 | scene = GLTF_Scene(rootnode, filename, json_obj, False, settings)
511 |
512 | if settings["flattenhierarchy"]:
513 | scene.load_flattened(settings["scene"])
514 | else:
515 | scene.load()
516 | scene.parse_scene(settings["scene"])
517 |
518 | # Format nodes nicely
519 | rootnode.layoutChildren()
520 | scene.matnet.layoutChildren()
521 |
522 | def EnsureAssetFolder(dirname, original_dirname):
523 | if not os.path.exists(dirname):
524 | try:
525 | os.makedirs(dirname)
526 | except IOError:
527 | return False
528 |
529 | # Need to check permissions on dir itself
530 | if not os.access(original_dirname, os.W_OK | os.X_OK):
531 | return False
532 |
533 | return True
534 |
535 | def LoadHierarchy(filename, rootnode):
536 | settings = {}
537 | settings["import_geometry"] = rootnode.parm("importgeometry").evalAsInt() != 0
538 | settings["import_cust_attribs"] = rootnode.parm("importcustomattributes").evalAsInt() != 0
539 | settings["import_mats"] = rootnode.parm("importmaterials").evalAsInt() != 0
540 | settings["import_unusedmats"] = (rootnode.parm("importunusedmaterials").evalAsInt()) != 0 and settings["import_mats"]
541 | settings["lock_geo"] = rootnode.parm("lockgeo").evalAsInt() != 0
542 | settings["import_non_geo"] = rootnode.parm("importnongeo").evalAsInt() != 0
543 | settings["scene"] = int(rootnode.parm("scene").evalAsString())
544 | settings["asset_folder"] = rootnode.parm("assetfolder").evalAsString()
545 | settings["flattenhierarchy"] = rootnode.parm("flattenhierarchy").evalAsInt() != 0
546 | settings["filename"] = filename
547 | settings["promote_point_attrs"] = rootnode.parm("promotepointattrs").evalAsInt() != 0
548 | settings["point_consolidation_dist"] = rootnode.parm("pointconsolidatedist").evalAsFloat();
549 |
550 | original_name = settings["asset_folder"]
551 | # Allow for both formats like $HIP/folder and $HIP/folder/
552 | if not settings["asset_folder"].endswith("/"):
553 | settings["asset_folder"] += "/"
554 |
555 | # Check if we have an external image at the start, so that if we can't
556 | # unpack it in the asset folder, we just bail
557 | has_external_image = False
558 |
559 | if not EnsureAssetFolder(settings["asset_folder"], original_name):
560 | ui.displayMessage(title='Unable to Access Asset Folder',
561 | text='Unable to access directory ' + original_name + '. A valid external directory is required to unpack glTF buffers.',
562 | severity=hou.severityType.Error)
563 | return
564 |
565 | (root, ext) = os.path.splitext(filename)
566 | if ext.lower() == ".gltf":
567 | LoadGLTF(rootnode, filename, settings)
568 | elif ext.lower() == ".glb":
569 | LoadGLB(rootnode, filename, settings)
570 | else:
571 | ui.displayMessage(title='Invalid glTF File Type',
572 | text='Please select a valid file with an extension of .gltf or .glb.',
573 | severity=hou.severityType.Error)
574 | return
575 |
576 | def ReloadGeometry(rootnode):
577 | _destroy_children(rootnode)
578 |
579 | filename = rootnode.parm("filename").evalAsString()
580 | flatten = rootnode.parm("flattenhierarchy").evalAsInt() != 0
581 |
582 | _gltf.gltfClearCache(filename)
583 |
584 | LoadHierarchy(filename, rootnode)
585 |
586 |
587 | #####################################
588 |
589 | def GenerateSceneMenu(rootnode):
590 | filename = rootnode.parm("filename").evalAsString()
591 | scenes = _gltf.gltfGetSceneList(filename)
592 | return scenes
593 |
594 | def EvaluateMenus(rootnode):
595 | scenes = GenerateSceneMenu(rootnode)
596 | if(len(scenes) == 0):
597 | if (ui):
598 | ui.displayMessage(title='No Scenes',
599 | text='Please select a GLTF file containing one or more scene',
600 | severity=hou.severityType.Error)
601 | return
602 |
603 | hou.pwd().parm("scene").set(GenerateSceneMenu(rootnode)[0])
--------------------------------------------------------------------------------
/src/SOP/SOP_GLTF.C:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020
3 | * Side Effects Software Inc. All rights reserved.
4 | *
5 | * Redistribution and use of Houdini Development Kit samples in source and
6 | * binary forms, with or without modification, are permitted provided that the
7 | * following conditions are met:
8 | * 1. Redistributions of source code must retain the above copyright notice,
9 | * this list of conditions and the following disclaimer.
10 | * 2. The name of Side Effects Software may not be used to endorse or
11 | * promote products derived from this software without specific prior
12 | * written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15 | * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17 | * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20 | * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23 | * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | *
25 | *----------------------------------------------------------------------------
26 | */
27 |
28 | #include "SOP_GLTF.h"
29 |
30 | #include
31 |
32 | #include
33 | #include
34 | #include
35 |
36 | #include