gear.cpp

  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}