1 /* -------------------------------------------------------------------------- *
2 * OpenSim: Geometry.cpp *
3 * -------------------------------------------------------------------------- *
4 * The OpenSim API is a toolkit for musculoskeletal modeling and simulation. *
5 * See http://opensim.stanford.edu and the NOTICE file for more information. *
6 * OpenSim is developed at Stanford University and supported by the US *
7 * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA *
8 * through the Warrior Web program. *
9 * *
10 * Copyright (c) 2005-2017 Stanford University and the Authors *
11 * Author(s): Ayman Habib *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24 //=============================================================================
25 // INCLUDES
26 //=============================================================================
27 #include <fstream>
28 #include "Frame.h"
29 #include "Geometry.h"
30 #include "Model.h"
31 //=============================================================================
32 // STATICS
33 //=============================================================================
34 using namespace std;
35 using namespace OpenSim;
36 using namespace SimTK;
37
38 OpenSim_DEFINE_SOCKET_FD(frame, Geometry);
39
Geometry()40 Geometry::Geometry() {
41 setNull();
42 constructProperties();
43 }
44
setFrame(const Frame & frame)45 void Geometry::setFrame(const Frame& frame)
46 {
47 updSocket<Frame>("frame").setConnecteePath(frame.getRelativePathString(*this));
48 }
49
getFrame() const50 const OpenSim::Frame& Geometry::getFrame() const
51 {
52 return getSocket<Frame>("frame").getConnectee();
53 }
54
extendFinalizeConnections(Component & root)55 void Geometry::extendFinalizeConnections(Component& root)
56 {
57 Super::extendFinalizeConnections(root);
58
59 bool attachedToFrame = getSocket<Frame>("frame").isConnected();
60 bool hasInputTransform = getInput("transform").isConnected();
61 // Being both attached to a Frame (i.e. Socket<Frame> connected)
62 // and the Input transform connected has ambiguous behavior so disallow it
63 if (attachedToFrame && hasInputTransform ) {
64 OPENSIM_THROW(Exception, getConcreteClassName() + " '" + getName()
65 + "' cannot be attached to a Frame and have its "
66 "Input `transform` set.");
67 }
68 else if (!attachedToFrame && !hasInputTransform) {
69 OPENSIM_THROW(Exception, getConcreteClassName() + " '" + getName()
70 + "' must be attached to a Frame OR have its "
71 "Input `transform` set.");
72 }
73 }
74
generateDecorations(bool fixed,const ModelDisplayHints & hints,const SimTK::State & state,SimTK::Array_<SimTK::DecorativeGeometry> & appendToThis) const75 void FrameGeometry::generateDecorations(bool fixed,
76 const ModelDisplayHints& hints,
77 const SimTK::State& state,
78 SimTK::Array_<SimTK::DecorativeGeometry>& appendToThis) const
79 {
80 if (!hints.get_show_frames())
81 return;
82 // Call base class
83 Super::generateDecorations(fixed, hints, state, appendToThis);
84
85 }
generateDecorations(bool fixed,const ModelDisplayHints & hints,const SimTK::State & state,SimTK::Array_<SimTK::DecorativeGeometry> & appendToThis) const86 void Geometry::generateDecorations(bool fixed,
87 const ModelDisplayHints& hints,
88 const SimTK::State& state,
89 SimTK::Array_<SimTK::DecorativeGeometry>& appendToThis) const
90 {
91 // serialized Geometry is assumed fixed
92 // if it has a Transform input then it is not "attached" geometry
93 // and fixed to a body but floating w.r.t Ground.
94 if (!fixed && !getInput("transform").isConnected())
95 return;
96
97 if (!get_Appearance().get_visible()) return;
98
99 SimTK::Array_<SimTK::DecorativeGeometry> decos;
100 implementCreateDecorativeGeometry(decos);
101 if (decos.size() == 0) return;
102 setDecorativeGeometryTransform(decos, state);
103 for (unsigned i = 0; i < decos.size(); i++){
104 setDecorativeGeometryAppearance(decos[i]);
105 appendToThis.push_back(decos[i]);
106 }
107 }
108
109 /*
110 * Apply the Transform of the Frame the Geometry is attached to,
111 * OR use the Transform supplied to the Geometry via its Input.
112 */
setDecorativeGeometryTransform(SimTK::Array_<SimTK::DecorativeGeometry> & decorations,const SimTK::State & state) const113 void Geometry::setDecorativeGeometryTransform(
114 SimTK::Array_<SimTK::DecorativeGeometry>& decorations,
115 const SimTK::State& state) const
116 {
117 auto& input = getInput<SimTK::Transform>("transform");
118
119 SimTK::Transform transformInBaseFrame;
120 SimTK::MobilizedBodyIndex mbidx;
121
122 if (input.isConnected()) {
123 transformInBaseFrame = input.getValue(state);
124 mbidx = SimTK::MobilizedBodyIndex(0);
125 }
126 else {
127 const Frame& myFrame = getFrame();
128 const Frame& bFrame = myFrame.findBaseFrame();
129 const PhysicalFrame* bPhysicalFrame =
130 dynamic_cast<const PhysicalFrame*>(&bFrame);
131 if (bPhysicalFrame == nullptr) {
132 // throw exception something is wrong
133 throw (Exception("Frame for Geometry " + getName() +
134 " is not attached to a PhysicalFrame."));
135 }
136 mbidx = bPhysicalFrame->getMobilizedBodyIndex();
137 transformInBaseFrame = myFrame.findTransformInBaseFrame();
138 }
139
140 for (unsigned i = 0; i < decorations.size(); i++){
141 decorations[i].setBodyId(mbidx);
142 decorations[i].setTransform(transformInBaseFrame);
143 decorations[i].setIndexOnBody(i);
144 }
145 }
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const146 void Sphere::implementCreateDecorativeGeometry(
147 SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
148 {
149 const Vec3 netScale = get_scale_factors();
150 DecorativeSphere deco(get_radius());
151 deco.setScaleFactors(netScale);
152 decoGeoms.push_back(deco);
153 }
154
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const155 void Cylinder::implementCreateDecorativeGeometry(
156 SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
157 {
158 const Vec3 netScale = get_scale_factors();
159 DecorativeCylinder deco(get_radius(), get_half_height());
160 deco.setScaleFactors(netScale);
161 decoGeoms.push_back(deco);
162 }
163
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const164 void Cone::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
165 {
166 const Vec3 netScale = get_scale_factors();
167 DecorativeCone deco(get_origin(), SimTK::UnitVec3(get_direction()), get_height(), get_base_radius());
168 deco.setScaleFactors(netScale);
169 decoGeoms.push_back(deco);
170 }
171
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const172 void LineGeometry::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
173 {
174 const Vec3 netScale = get_scale_factors();
175 DecorativeLine deco(get_start_point(), get_end_point());
176 deco.setScaleFactors(netScale);
177 decoGeoms.push_back(deco);
178 }
179
180
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const181 void Arrow::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
182 {
183 const Vec3 netScale = get_scale_factors();
184 SimTK::Vec3 endPt(get_length()*get_direction());
185 DecorativeArrow deco(SimTK::Vec3(0), endPt);
186 deco.setLineThickness(0.05);
187 deco.setScaleFactors(netScale);
188 decoGeoms.push_back(deco);
189 }
190
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const191 void Ellipsoid::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
192 {
193 const Vec3 netScale = get_scale_factors();
194 DecorativeEllipsoid deco(get_radii());
195 deco.setScaleFactors(netScale);
196 decoGeoms.push_back(deco);
197 }
198
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const199 void Brick::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
200 {
201 const Vec3 netScale = get_scale_factors();
202 DecorativeBrick deco(get_half_lengths());
203 deco.setScaleFactors(netScale);
204 decoGeoms.push_back(deco);
205 }
206
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const207 void FrameGeometry::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
208 {
209 const Vec3 netScale = get_scale_factors();
210 DecorativeFrame deco(1.0);
211 deco.setLineThickness(get_display_radius());
212 deco.setScaleFactors(netScale);
213 decoGeoms.push_back(deco);
214 }
215
extendFinalizeFromProperties()216 void Mesh::extendFinalizeFromProperties() {
217
218 if (!isObjectUpToDateWithProperties()) {
219 const Component* rootModel = nullptr;
220 if (!hasOwner()) {
221 std::cout << "Mesh " << get_mesh_file() << " not connected to model..ignoring" << std::endl;
222 return; // Orphan Mesh not part of a model yet
223 }
224 const Component* owner = &getOwner();
225 while (owner != nullptr) {
226 if (dynamic_cast<const Model*>(owner) != nullptr) {
227 rootModel = owner;
228 break;
229 }
230 if (owner->hasOwner())
231 owner = &(owner->getOwner()); // traverse up Component tree
232 else
233 break; // can't traverse up.
234 }
235
236 if (rootModel == nullptr) {
237 std::cout << "Mesh " << get_mesh_file() << " not connected to model..ignoring" << std::endl;
238 return; // Orphan Mesh not descendant of a model
239 }
240
241 // Current interface to Visualizer calls generateDecorations on every
242 // frame. On first time through, load file and create DecorativeMeshFile
243 // and cache it so we don't load files from disk during live rendering.
244 const std::string& file = get_mesh_file();
245 if (file.empty() || file.compare(PropertyStr::getDefaultStr()) == 0)
246 return; // Return immediately if no file has been specified.
247
248 bool isAbsolutePath; string directory, fileName, extension;
249 SimTK::Pathname::deconstructPathname(file,
250 isAbsolutePath, directory, fileName, extension);
251 const string lowerExtension = SimTK::String::toLower(extension);
252 if (lowerExtension != ".vtp" && lowerExtension != ".obj" && lowerExtension != ".stl") {
253 std::cout << "ModelVisualizer ignoring '" << file
254 << "'; only .vtp, .stl, and .obj files currently supported." << std::endl;
255 return;
256 }
257
258 // File is a .vtp, .stl, or .obj; attempt to find it.
259 Array_<string> attempts;
260 const Model& model = dynamic_cast<const Model&>(*rootModel);
261 bool foundIt = ModelVisualizer::findGeometryFile(model, file, isAbsolutePath, attempts);
262
263 if (!foundIt) {
264 if (!warningGiven) {
265 std::cout << "Couldn't find file '" << file << "'." << std::endl;
266 warningGiven = true;
267 }
268 if (getDebugLevel() <= 0) { return; }
269 std::cout << "The following locations were tried:\n";
270 for (unsigned i = 0; i < attempts.size(); ++i)
271 std::cout << "\n " << attempts[i];
272 std::cout << std::endl;
273 if (!isAbsolutePath &&
274 !Pathname::environmentVariableExists("OPENSIM_HOME"))
275 std::cout << "Set environment variable OPENSIM_HOME "
276 << "to search $OPENSIM_HOME/Geometry." << std::endl;
277 return;
278 }
279
280 try {
281 std::ifstream objFile;
282 objFile.open(attempts.back().c_str());
283 // objFile closes when destructed
284 // if the file can be opened but had bad contents e.g. binary vtp
285 // it will be handled downstream
286 }
287 catch (const std::exception& e) {
288 std::cout << "Visualizer couldn't open "
289 << attempts.back() << " because:\n"
290 << e.what() << std::endl;
291 return;
292 }
293
294 cachedMesh.reset(new DecorativeMeshFile(attempts.back().c_str()));
295 }
296 }
297
298
implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry> & decoGeoms) const299 void Mesh::implementCreateDecorativeGeometry(SimTK::Array_<SimTK::DecorativeGeometry>& decoGeoms) const
300 {
301 if (cachedMesh.get() != nullptr) {
302 try {
303 // Force the loading of the mesh to see if it has bad contents
304 // (e.g., binary vtp).
305 // We do not want to do this in extendFinalizeFromProperties b/c
306 // it's expensive to repeatedly load meshes.
307 cachedMesh->getMesh();
308 } catch (const std::exception& e) {
309 std::cout << "Visualizer couldn't open "
310 << get_mesh_file() << " because:\n"
311 << e.what() << std::endl;
312 // No longer try to visualize this mesh.
313 cachedMesh.reset();
314 return;
315 }
316 cachedMesh->setScaleFactors(get_scale_factors());
317 decoGeoms.push_back(*cachedMesh);
318 }
319 }
320