1/*
2Open Asset Import Library (assimp)
3----------------------------------------------------------------------
4
5Copyright (c) 2006-2017, assimp team
6
7All rights reserved.
8
9Redistribution and use of this software in source and binary forms,
10with or without modification, are permitted provided that the
11following conditions are met:
12
13* Redistributions of source code must retain the above
14 copyright notice, this list of conditions and the
15 following disclaimer.
16
17* Redistributions in binary form must reproduce the above
18 copyright notice, this list of conditions and the
19 following disclaimer in the documentation and/or other
20 materials provided with the distribution.
21
22* Neither the name of the assimp team, nor the names of its
23 contributors may be used to endorse or promote products
24 derived from this software without specific prior
25 written permission of the assimp team.
26
27THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
28"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
29LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
31OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
32SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
33LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
34DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
35THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
36(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
37OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38
39@author: Richard Steffen, 2015
40----------------------------------------------------------------------
41*/
42
43
44#ifndef ASSIMP_BUILD_NO_EXPORT
45#ifndef ASSIMP_BUILD_NO_STEP_EXPORTER
46
47#include "StepExporter.h"
48#include "ConvertToLHProcess.h"
49#include "Bitmap.h"
50#include "BaseImporter.h"
51#include "fast_atof.h"
52#include <assimp/SceneCombiner.h>
53#include <iostream>
54#include <ctime>
55#include <set>
56#include <map>
57#include <list>
58#include <memory>
59#include "Exceptional.h"
60#include <assimp/DefaultIOSystem.h>
61#include <assimp/IOSystem.hpp>
62#include <assimp/scene.h>
63#include <assimp/light.h>
64
65//
66#if _MSC_VER > 1500 || (defined __GNUC___)
67# define ASSIMP_STEP_USE_UNORDERED_MULTIMAP
68# else
69# define step_unordered_map map
70# define step_unordered_multimap multimap
71#endif
72
73#ifdef ASSIMP_STEP_USE_UNORDERED_MULTIMAP
74# include <unordered_map>
75# if _MSC_VER > 1600
76# define step_unordered_map unordered_map
77# define step_unordered_multimap unordered_multimap
78# else
79# define step_unordered_map tr1::unordered_map
80# define step_unordered_multimap tr1::unordered_multimap
81# endif
82#endif
83
84typedef std::step_unordered_map<aiVector3D*, int> VectorIndexUMap;
85
86/* Tested with Step viewer v4 from www.ida-step.net */
87
88using namespace Assimp;
89
90namespace Assimp
91{
92
93// ------------------------------------------------------------------------------------------------
94// Worker function for exporting a scene to Collada. Prototyped and registered in Exporter.cpp
95void ExportSceneStep(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* pProperties)
96{
97 std::string path = DefaultIOSystem::absolutePath(std::string(pFile));
98 std::string file = DefaultIOSystem::completeBaseName(std::string(pFile));
99
100 // create/copy Properties
101 ExportProperties props(*pProperties);
102
103 // invoke the exporter
104 StepExporter iDoTheExportThing( pScene, pIOSystem, path, file, &props);
105
106 // we're still here - export successfully completed. Write result to the given IOSYstem
107 std::unique_ptr<IOStream> outfile (pIOSystem->Open(pFile,"wt"));
108 if(outfile == NULL) {
109 throw DeadlyExportError("could not open output .stp file: " + std::string(pFile));
110 }
111
112 // XXX maybe use a small wrapper around IOStream that behaves like std::stringstream in order to avoid the extra copy.
113 outfile->Write( iDoTheExportThing.mOutput.str().c_str(), static_cast<size_t>(iDoTheExportThing.mOutput.tellp()),1);
114}
115
116} // end of namespace Assimp
117
118
119namespace {
120 // Collect world transformations for each node
121 void CollectTrafos(const aiNode* node, std::map<const aiNode*, aiMatrix4x4>& trafos) {
122 const aiMatrix4x4& parent = node->mParent ? trafos[node->mParent] : aiMatrix4x4();
123 trafos[node] = parent * node->mTransformation;
124 for (unsigned int i = 0; i < node->mNumChildren; ++i) {
125 CollectTrafos(node->mChildren[i], trafos);
126 }
127 }
128
129 // Generate a flat list of the meshes (by index) assigned to each node
130 void CollectMeshes(const aiNode* node, std::multimap<const aiNode*, unsigned int>& meshes) {
131 for (unsigned int i = 0; i < node->mNumMeshes; ++i) {
132 meshes.insert(std::make_pair(node, node->mMeshes[i]));
133 }
134 for (unsigned int i = 0; i < node->mNumChildren; ++i) {
135 CollectMeshes(node->mChildren[i], meshes);
136 }
137 }
138}
139
140// ------------------------------------------------------------------------------------------------
141// Constructor for a specific scene to export
142StepExporter::StepExporter(const aiScene* pScene, IOSystem* pIOSystem, const std::string& path,
143 const std::string& file, const ExportProperties* pProperties):
144 mProperties(pProperties),mIOSystem(pIOSystem),mFile(file), mPath(path),
145 mScene(pScene), endstr(";\n") {
146 CollectTrafos(pScene->mRootNode, trafos);
147 CollectMeshes(pScene->mRootNode, meshes);
148
149 // make sure that all formatting happens using the standard, C locale and not the user's current locale
150 mOutput.imbue( std::locale("C") );
151 mOutput.precision(16);
152
153 // start writing
154 WriteFile();
155}
156
157// ------------------------------------------------------------------------------------------------
158// Starts writing the contents
159void StepExporter::WriteFile()
160{
161 // see http://shodhganga.inflibnet.ac.in:8080/jspui/bitstream/10603/14116/11/11_chapter%203.pdf
162 // note, that all realnumber values must be comma separated in x files
163 mOutput.setf(std::ios::fixed);
164 // precission for double
165 // see http://stackoverflow.com/questions/554063/how-do-i-print-a-double-value-with-full-precision-using-cout
166 mOutput.precision(16);
167
168 // standard color
169 aiColor4D fColor;
170 fColor.r = 0.8f;
171 fColor.g = 0.8f;
172 fColor.b = 0.8f;
173
174 int ind = 100; // the start index to be used
175 int faceEntryLen = 30; // number of entries for a triangle/face
176 // prepare unique (count triangles and vertices)
177
178 VectorIndexUMap uniqueVerts; // use a map to reduce find complexity to log(n)
179 VectorIndexUMap::iterator it;
180 int countFace = 0;
181
182 for (unsigned int i=0; i<mScene->mNumMeshes; ++i)
183 {
184 aiMesh* mesh = mScene->mMeshes[i];
185 for (unsigned int j=0; j<mesh->mNumFaces; ++j)
186 {
187 aiFace* face = &(mesh->mFaces[j]);
188
189 if (face->mNumIndices == 3) countFace++;
190 }
191 for (unsigned int j=0; j<mesh->mNumVertices; ++j)
192 {
193 aiVector3D* v = &(mesh->mVertices[j]);
194 it =uniqueVerts.find(v);
195 if (it == uniqueVerts.end())
196 {
197 uniqueVerts[v] = -1; // first mark the vector as not transformed
198 }
199 }
200 }
201
202 static const unsigned int date_nb_chars = 20;
203 char date_str[date_nb_chars];
204 std::time_t date = std::time(NULL);
205 std::strftime(date_str, date_nb_chars, "%Y-%m-%dT%H:%M:%S", std::localtime(&date));
206
207 // write the header
208 mOutput << "ISO-10303-21" << endstr;
209 mOutput << "HEADER" << endstr;
210 mOutput << "FILE_DESCRIPTION(('STEP AP214'),'1')" << endstr;
211 mOutput << "FILE_NAME('" << mFile << ".stp','" << date_str << "',(' '),(' '),'Spatial InterOp 3D',' ',' ')" << endstr;
212 mOutput << "FILE_SCHEMA(('automotive_design'))" << endstr;
213 mOutput << "ENDSEC" << endstr;
214
215 // write the top of data
216 mOutput << "DATA" << endstr;
217 mOutput << "#1=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION(' ',(";
218 for (int i=0; i<countFace; ++i)
219 {
220 mOutput << "#" << i*faceEntryLen + ind + 2*uniqueVerts.size();
221 if (i!=countFace-1) mOutput << ",";
222 }
223 mOutput << "),#6)" << endstr;
224
225 mOutput << "#2=PRODUCT_DEFINITION_CONTEXT('',#7,'design')" << endstr;
226 mOutput << "#3=APPLICATION_PROTOCOL_DEFINITION('INTERNATIONAL STANDARD','automotive_design',1994,#7)" << endstr;
227 mOutput << "#4=PRODUCT_CATEGORY_RELATIONSHIP('NONE','NONE',#8,#9)" << endstr;
228 mOutput << "#5=SHAPE_DEFINITION_REPRESENTATION(#10,#11)" << endstr;
229 mOutput << "#6= (GEOMETRIC_REPRESENTATION_CONTEXT(3)GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#12))GLOBAL_UNIT_ASSIGNED_CONTEXT((#13,#14,#15))REPRESENTATION_CONTEXT('NONE','WORKSPACE'))" << endstr;
230 mOutput << "#7=APPLICATION_CONTEXT(' ')" << endstr;
231 mOutput << "#8=PRODUCT_CATEGORY('part','NONE')" << endstr;
232 mOutput << "#9=PRODUCT_RELATED_PRODUCT_CATEGORY('detail',' ',(#17))" << endstr;
233 mOutput << "#10=PRODUCT_DEFINITION_SHAPE('NONE','NONE',#18)" << endstr;
234 mOutput << "#11=MANIFOLD_SURFACE_SHAPE_REPRESENTATION('Root',(#16,#19),#6)" << endstr;
235 mOutput << "#12=UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.0E-006),#13,'','')" << endstr;
236 mOutput << "#13=(CONVERSION_BASED_UNIT('METRE',#20)LENGTH_UNIT()NAMED_UNIT(#21))" << endstr;
237 mOutput << "#14=(NAMED_UNIT(#22)PLANE_ANGLE_UNIT()SI_UNIT($,.RADIAN.))" << endstr;
238 mOutput << "#15=(NAMED_UNIT(#22)SOLID_ANGLE_UNIT()SI_UNIT($,.STERADIAN.))" << endstr;
239 mOutput << "#16=SHELL_BASED_SURFACE_MODEL('Root',(#29))" << endstr;
240 mOutput << "#17=PRODUCT('Root','Root','Root',(#23))" << endstr;
241 mOutput << "#18=PRODUCT_DEFINITION('NONE','NONE',#24,#2)" << endstr;
242 mOutput << "#19=AXIS2_PLACEMENT_3D('',#25,#26,#27)" << endstr;
243 mOutput << "#20=LENGTH_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.0),#28)" << endstr;
244 mOutput << "#21=DIMENSIONAL_EXPONENTS(1.0,0.0,0.0,0.0,0.0,0.0,0.0)" << endstr;
245 mOutput << "#22=DIMENSIONAL_EXPONENTS(0.0,0.0,0.0,0.0,0.0,0.0,0.0)" << endstr;
246 mOutput << "#23=PRODUCT_CONTEXT('',#7,'mechanical')" << endstr;
247 mOutput << "#24=PRODUCT_DEFINITION_FORMATION_WITH_SPECIFIED_SOURCE(' ','NONE',#17,.NOT_KNOWN.)" << endstr;
248 mOutput << "#25=CARTESIAN_POINT('',(0.0,0.0,0.0))" << endstr;
249 mOutput << "#26=DIRECTION('',(0.0,0.0,1.0))" << endstr;
250 mOutput << "#27=DIRECTION('',(1.0,0.0,0.0))" << endstr;
251 mOutput << "#28= (NAMED_UNIT(#21)LENGTH_UNIT()SI_UNIT(.MILLI.,.METRE.))" << endstr;
252 mOutput << "#29=CLOSED_SHELL('',(";
253 for (int i=0; i<countFace; ++i)
254 {
255 mOutput << "#" << i*faceEntryLen + ind + 2*uniqueVerts.size() + 8;
256 if (i!=countFace-1) mOutput << ",";
257 }
258 mOutput << "))" << endstr;
259
260 // write all the unique transformed CARTESIAN and VERTEX
261 for (MeshesByNodeMap::const_iterator it2 = meshes.begin(); it2 != meshes.end(); ++it2)
262 {
263 const aiNode& node = *(*it2).first;
264 unsigned int mesh_idx = (*it2).second;
265
266 const aiMesh* mesh = mScene->mMeshes[mesh_idx];
267 aiMatrix4x4& trafo = trafos[&node];
268 for (unsigned int i = 0; i < mesh->mNumVertices; ++i)
269 {
270 aiVector3D* v = &(mesh->mVertices[i]);
271 it = uniqueVerts.find(v);
272 if (it->second >=0 ) continue;
273 it->second = ind; // this one is new, so set the index (ind)
274 aiVector3D vt = trafo * (*v); // transform the coordinate
275 mOutput << "#" << it->second << "=CARTESIAN_POINT('',(" << vt.x << "," << vt.y << "," << vt.z << "))" << endstr;
276 mOutput << "#" << it->second+1 << "=VERTEX_POINT('',#" << it->second << ")" << endstr;
277 ind += 2;
278 }
279 }
280
281 // write the triangles
282 for (unsigned int i=0; i<mScene->mNumMeshes; ++i)
283 {
284 aiMesh* mesh = mScene->mMeshes[i];
285 for (unsigned int j=0; j<mesh->mNumFaces; ++j)
286 {
287 aiFace* face = &(mesh->mFaces[j]);
288
289 if (face->mNumIndices != 3) continue;
290
291 aiVector3D* v1 = &(mesh->mVertices[face->mIndices[0]]);
292 aiVector3D* v2 = &(mesh->mVertices[face->mIndices[1]]);
293 aiVector3D* v3 = &(mesh->mVertices[face->mIndices[2]]);
294 aiVector3D dv12 = *v2 - *v1;
295 aiVector3D dv23 = *v3 - *v2;
296 aiVector3D dv31 = *v1 - *v3;
297 aiVector3D dv13 = *v3 - *v1;
298 dv12.Normalize();
299 dv23.Normalize();
300 dv31.Normalize();
301 dv13.Normalize();
302
303 int pid1 = uniqueVerts.find(v1)->second;
304 int pid2 = uniqueVerts.find(v2)->second;
305 int pid3 = uniqueVerts.find(v3)->second;
306
307 // mean vertex color for the face if available
308 if (mesh->HasVertexColors(0))
309 {
310 fColor.r = 0.0;
311 fColor.g = 0.0;
312 fColor.b = 0.0;
313 fColor += mesh->mColors[0][face->mIndices[0]];
314 fColor += mesh->mColors[0][face->mIndices[1]];
315 fColor += mesh->mColors[0][face->mIndices[2]];
316 fColor /= 3.0f;
317 }
318
319 int sid = ind; // the sub index
320 mOutput << "#" << sid << "=STYLED_ITEM('',(#" << sid+1 << "),#" << sid+8 << ")" << endstr; /* the item that must be referenced in #1 */
321 /* This is the color information of the Triangle */
322 mOutput << "#" << sid+1 << "=PRESENTATION_STYLE_ASSIGNMENT((#" << sid+2 << "))" << endstr;
323 mOutput << "#" << sid+2 << "=SURFACE_STYLE_USAGE(.BOTH.,#" << sid+3 << ")" << endstr;
324 mOutput << "#" << sid+3 << "=SURFACE_SIDE_STYLE('',(#" << sid+4 << "))" << endstr;
325 mOutput << "#" << sid+4 << "=SURFACE_STYLE_FILL_AREA(#" << sid+5 << ")" << endstr;
326 mOutput << "#" << sid+5 << "=FILL_AREA_STYLE('',(#" << sid+6 << "))" << endstr;
327 mOutput << "#" << sid+6 << "=FILL_AREA_STYLE_COLOUR('',#" << sid+7 << ")" << endstr;
328 mOutput << "#" << sid+7 << "=COLOUR_RGB(''," << fColor.r << "," << fColor.g << "," << fColor.b << ")" << endstr;
329
330 /* this is the geometry */
331 mOutput << "#" << sid+8 << "=FACE_SURFACE('',(#" << sid+13 << "),#" << sid+9<< ",.T.)" << endstr; /* the face that must be referenced in 29 */
332
333 /* 2 directions of the plane */
334 mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr;
335 mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr;
336
337 mOutput << "#" << sid+11 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr;
338 mOutput << "#" << sid+12 << "=DIRECTION('',(" << dv13.x << "," << dv13.y << "," << dv13.z << "))" << endstr;
339
340 mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr;
341 mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr;
342
343 /* edge loop */
344 mOutput << "#" << sid+15 << "=ORIENTED_EDGE('',*,*,#" << sid+18 << ",.T.)" << endstr;
345 mOutput << "#" << sid+16 << "=ORIENTED_EDGE('',*,*,#" << sid+19 << ",.T.)" << endstr;
346 mOutput << "#" << sid+17 << "=ORIENTED_EDGE('',*,*,#" << sid+20 << ",.T.)" << endstr;
347
348 /* oriented edges */
349 mOutput << "#" << sid+18 << "=EDGE_CURVE('',#" << pid1+1 << ",#" << pid2+1 << ",#" << sid+21 << ",.F.)" << endstr;
350 mOutput << "#" << sid+19 << "=EDGE_CURVE('',#" << pid2+1 << ",#" << pid3+1 << ",#" << sid+22 << ",.T.)" << endstr;
351 mOutput << "#" << sid+20 << "=EDGE_CURVE('',#" << pid3+1 << ",#" << pid1+1 << ",#" << sid+23 << ",.T.)" << endstr;
352
353 /* 3 lines and 3 vectors for the lines for the 3 edge curves */
354 mOutput << "#" << sid+21 << "=LINE('',#" << pid1 << ",#" << sid+24 << ")" << endstr;
355 mOutput << "#" << sid+22 << "=LINE('',#" << pid2 << ",#" << sid+25 << ")" << endstr;
356 mOutput << "#" << sid+23 << "=LINE('',#" << pid3 << ",#" << sid+26 << ")" << endstr;
357 mOutput << "#" << sid+24 << "=VECTOR('',#" << sid+27 << ",1.0)" << endstr;
358 mOutput << "#" << sid+25 << "=VECTOR('',#" << sid+28 << ",1.0)" << endstr;
359 mOutput << "#" << sid+26 << "=VECTOR('',#" << sid+29 << ",1.0)" << endstr;
360 mOutput << "#" << sid+27 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr;
361 mOutput << "#" << sid+28 << "=DIRECTION('',(" << dv23.x << "," << dv23.y << "," << dv23.z << "))" << endstr;
362 mOutput << "#" << sid+29 << "=DIRECTION('',(" << dv31.x << "," << dv31.y << "," << dv31.z << "))" << endstr;
363 ind += faceEntryLen; // increase counter
364 }
365 }
366
367 mOutput << "ENDSEC" << endstr; // end of data section
368 mOutput << "END-ISO-10303-21" << endstr; // end of file
369}
370
371#endif
372#endif
373