1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2019 Blender Foundation.
17  * All rights reserved.
18  */
19 #include "usd_writer_abstract.h"
20 #include "usd_hierarchy_iterator.h"
21 
22 #include <pxr/base/tf/stringUtils.h>
23 
24 #include "BLI_assert.h"
25 
26 /* TfToken objects are not cheap to construct, so we do it once. */
27 namespace usdtokens {
28 /* Materials */
29 static const pxr::TfToken diffuse_color("diffuseColor", pxr::TfToken::Immortal);
30 static const pxr::TfToken metallic("metallic", pxr::TfToken::Immortal);
31 static const pxr::TfToken preview_shader("previewShader", pxr::TfToken::Immortal);
32 static const pxr::TfToken preview_surface("UsdPreviewSurface", pxr::TfToken::Immortal);
33 static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
34 static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
35 }  // namespace usdtokens
36 
37 namespace blender::io::usd {
38 
USDAbstractWriter(const USDExporterContext & usd_export_context)39 USDAbstractWriter::USDAbstractWriter(const USDExporterContext &usd_export_context)
40     : usd_export_context_(usd_export_context),
41       usd_value_writer_(),
42       frame_has_been_written_(false),
43       is_animated_(false)
44 {
45 }
46 
~USDAbstractWriter()47 USDAbstractWriter::~USDAbstractWriter()
48 {
49 }
50 
is_supported(const HierarchyContext *) const51 bool USDAbstractWriter::is_supported(const HierarchyContext * /*context*/) const
52 {
53   return true;
54 }
55 
get_export_time_code() const56 pxr::UsdTimeCode USDAbstractWriter::get_export_time_code() const
57 {
58   if (is_animated_) {
59     return usd_export_context_.hierarchy_iterator->get_export_time_code();
60   }
61   /* By using the default timecode USD won't even write a single `timeSample` for non-animated
62    * data. Instead, it writes it as non-timesampled. */
63   static pxr::UsdTimeCode default_timecode = pxr::UsdTimeCode::Default();
64   return default_timecode;
65 }
66 
write(HierarchyContext & context)67 void USDAbstractWriter::write(HierarchyContext &context)
68 {
69   if (!frame_has_been_written_) {
70     is_animated_ = usd_export_context_.export_params.export_animation &&
71                    check_is_animated(context);
72   }
73   else if (!is_animated_) {
74     /* A frame has already been written, and without animation one frame is enough. */
75     return;
76   }
77 
78   do_write(context);
79 
80   frame_has_been_written_ = true;
81 }
82 
usd_path() const83 const pxr::SdfPath &USDAbstractWriter::usd_path() const
84 {
85   return usd_export_context_.usd_path;
86 }
87 
ensure_usd_material(Material * material)88 pxr::UsdShadeMaterial USDAbstractWriter::ensure_usd_material(Material *material)
89 {
90   static pxr::SdfPath material_library_path("/_materials");
91   pxr::UsdStageRefPtr stage = usd_export_context_.stage;
92 
93   /* Construct the material. */
94   pxr::TfToken material_name(usd_export_context_.hierarchy_iterator->get_id_name(&material->id));
95   pxr::SdfPath usd_path = material_library_path.AppendChild(material_name);
96   pxr::UsdShadeMaterial usd_material = pxr::UsdShadeMaterial::Get(stage, usd_path);
97   if (usd_material) {
98     return usd_material;
99   }
100   usd_material = pxr::UsdShadeMaterial::Define(stage, usd_path);
101 
102   /* Construct the shader. */
103   pxr::SdfPath shader_path = usd_path.AppendChild(usdtokens::preview_shader);
104   pxr::UsdShadeShader shader = pxr::UsdShadeShader::Define(stage, shader_path);
105   shader.CreateIdAttr(pxr::VtValue(usdtokens::preview_surface));
106   shader.CreateInput(usdtokens::diffuse_color, pxr::SdfValueTypeNames->Color3f)
107       .Set(pxr::GfVec3f(material->r, material->g, material->b));
108   shader.CreateInput(usdtokens::roughness, pxr::SdfValueTypeNames->Float).Set(material->roughness);
109   shader.CreateInput(usdtokens::metallic, pxr::SdfValueTypeNames->Float).Set(material->metallic);
110 
111   /* Connect the shader and the material together. */
112   usd_material.CreateSurfaceOutput().ConnectToSource(shader, usdtokens::surface);
113 
114   return usd_material;
115 }
116 
write_visibility(const HierarchyContext & context,const pxr::UsdTimeCode timecode,pxr::UsdGeomImageable & usd_geometry)117 void USDAbstractWriter::write_visibility(const HierarchyContext &context,
118                                          const pxr::UsdTimeCode timecode,
119                                          pxr::UsdGeomImageable &usd_geometry)
120 {
121   pxr::UsdAttribute attr_visibility = usd_geometry.CreateVisibilityAttr(pxr::VtValue(), true);
122 
123   const bool is_visible = context.is_object_visible(
124       usd_export_context_.export_params.evaluation_mode);
125   const pxr::TfToken visibility = is_visible ? pxr::UsdGeomTokens->inherited :
126                                                pxr::UsdGeomTokens->invisible;
127 
128   usd_value_writer_.SetAttribute(attr_visibility, pxr::VtValue(visibility), timecode);
129 }
130 
131 /* Reference the original data instead of writing a copy. */
mark_as_instance(const HierarchyContext & context,const pxr::UsdPrim & prim)132 bool USDAbstractWriter::mark_as_instance(const HierarchyContext &context, const pxr::UsdPrim &prim)
133 {
134   BLI_assert(context.is_instance());
135 
136   if (context.export_path == context.original_export_path) {
137     printf("USD ref error: export path is reference path: %s\n", context.export_path.c_str());
138     BLI_assert(!"USD reference error");
139     return false;
140   }
141 
142   pxr::SdfPath ref_path(context.original_export_path);
143   if (!prim.GetReferences().AddInternalReference(ref_path)) {
144     /* See this URL for a description fo why referencing may fail"
145      * https://graphics.pixar.com/usd/docs/api/class_usd_references.html#Usd_Failing_References
146      */
147     printf("USD Export warning: unable to add reference from %s to %s, not instancing object\n",
148            context.export_path.c_str(),
149            context.original_export_path.c_str());
150     return false;
151   }
152 
153   return true;
154 }
155 
156 }  // namespace blender::io::usd
157