1#include "nsi_procedural.h"
2
3#include "nsi_dynamic.hpp"
4#include "nsi_util.h"
5
6#include <math.h>
7
8
9// Extends NSIProcedural_t to store private data
10struct GearProcedural : public NSIProcedural_t
11{
12 explicit GearProcedural(const char* nsi_library_path)
13 : api(nsi_library_path)
14 {
15 }
16
17 /*
18 This loads symbols from the calling NSI library directly.
19 It avoids having to link the procedural against an NSI library and
20 letting the operating system locate it later.
21 If linking the procedural against NSI is not a problem, then this can
22 be omitted and the regular NSI API can be used directly.
23 */
24 NSI::DynamicAPI api;
25};
26
27
28static NSI_PROCEDURAL_UNLOAD(gear_unload)
29{
30 /*
31 The "proc" parameter is actually a pointer to the GearProcedural object
32 allocated in NSIProceduralLoad.
33 */
34 GearProcedural* gproc = (GearProcedural*)proc;
35 delete gproc;
36}
37
38
39static NSI_PROCEDURAL_EXECUTE(gear_execute)
40{
41 /*
42 The "proc" parameter is actually a pointer to the GearProcedural object
43 allocated in NSIProceduralLoad.
44 */
45 GearProcedural* gproc = (GearProcedural*)proc;
46
47 // Retrieve parameters using utility functions from nsi_util.h
48 const char* parent_node =
49 NSI::FindStringParameter("parentnode", nparams, params);
50 const char* node =
51 NSI::FindStringParameter("node", nparams, params);
52 const int* nb_teeth =
53 NSI::FindIntegerParameter("nb_teeth", nparams, params);
54 const float* inner_radius =
55 NSI::FindFloatParameter("inner_radius", nparams, params);
56 const float* outer_radius =
57 NSI::FindFloatParameter("outer_radius", nparams, params);
58 const float* teeth_slope =
59 NSI::FindFloatParameter("teeth_slope", nparams, params);
60
61 // Validate parameters and report errors
62
63 if(!parent_node)
64 {
65 /*
66 This is normal when the procedural is executed through a procedural
67 node instead of a call to NSIEvaluate.
68 */
69 parent_node = NSI_SCENE_ROOT;
70 }
71
72 if(!node)
73 {
74 /*
75 This is normal when the procedural is executed through a procedural
76 node instead of a call to NSIEvaluate.
77 In that case, since the procedural is evaluated inside its own NSI
78 context, in isolation from the main scene, there is no need to
79 specify the new node's handle from outside the procedural.
80 */
81 node = "gear";
82 }
83
84 if(!nb_teeth || *nb_teeth < 6)
85 {
86 report(ctx, NSIErrError, "gear : invalid number of teeth");
87 return;
88 }
89
90 if(!inner_radius || *inner_radius <= 0.0f)
91 {
92 report(ctx, NSIErrError, "gear : invalid inner radius");
93 return;
94 }
95
96 if(!outer_radius || *outer_radius <= *inner_radius)
97 {
98 report(ctx, NSIErrError, "gear : invalid outer radius");
99 return;
100 }
101
102 float slope = teeth_slope ? *teeth_slope : 0.75f;
103 if(slope <= 0.0f || slope > 1.0f)
104 {
105 report(ctx, NSIErrError, "gear : invalid teeth slope");
106 return;
107 }
108
109 // Build the positions vector
110 std::vector<float> P;
111 float pitch = 2.0f * float(M_PI) / float(*nb_teeth);
112 float inner_offset = pitch * (1.0f / (1.0f+slope)) / 2.0f;
113 float outer_offset =
114 pitch * (slope / (1.0f+slope)) / 2.0f * *inner_radius / *outer_radius;
115 for(int v = 0; v < *nb_teeth; v++)
116 {
117 float tooth_axis = v*pitch;
118
119 P.push_back(-sinf(tooth_axis-inner_offset) * *inner_radius);
120 P.push_back(cosf(tooth_axis-inner_offset) * *inner_radius);
121 P.push_back(0.0f);
122
123 P.push_back(-sinf(tooth_axis-outer_offset) * *outer_radius);
124 P.push_back(cosf(tooth_axis-outer_offset) * *outer_radius);
125 P.push_back(0.0f);
126
127 P.push_back(-sinf(tooth_axis+outer_offset) * *outer_radius);
128 P.push_back(cosf(tooth_axis+outer_offset) * *outer_radius);
129 P.push_back(0.0f);
130
131 P.push_back(-sinf(tooth_axis+inner_offset) * *inner_radius);
132 P.push_back(cosf(tooth_axis+inner_offset) * *inner_radius);
133 P.push_back(0.0f);
134 }
135
136 /*
137 Initialize a NSI::Context wrapper object from the DynamicAPI and the
138 actual context handle (see comment in NSIProceduralLoad).
139 If the procedural was to be linked directly against an NSI
140 implementation, we could have omitted NSI::Context's constructor
141 parameter.
142 */
143 NSI::Context nsi(gproc->api);
144 nsi.SetHandle(ctx);
145
146 // Create the mesh and set its attributes
147 nsi.Create(node, "mesh");
148 nsi.SetAttribute(node,
149 (
150 NSI::IntegerArg("nvertices", P.size()/3),
151 NSI::PointsArg("P", &P[0], P.size()/3)
152 ) );
153
154 // Connect it to its parent transform
155 nsi.Connect(node, "", parent_node, "objects");
156}
157
158
159// Main procedural entry point
160NSI_PROCEDURAL_LOAD
161{
162 GearProcedural* proc = new GearProcedural(nsi_library_path);
163 NSI_PROCEDURAL_INIT(*proc, gear_unload, gear_execute);
164
165 return proc;
166}