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