1 /*
  2   Copyright (c) 2008 Seneca College
  3   Licenced under the MIT License (http://www.c3dl.org/index.php/mit-license/)
  4 */
  5 
  6 /**
  7 	@class 
  8 	
  9 	<p>An EffectTemplate is a template for creating many Effects which have
 10 	similar rendering results. Effects instantiated from EffectTemplates 
 11 	achieve different rendering results by changing the parameters of the
 12 	EffectTemplate.</p>
 13 	
 14 	<p>Effects cannot instantiate EffectTemplates until the EffectTemplate has been
 15 	initialized.  However, once initialized, the EffectTemplate can no longer be modified.
 16 	This includes changing the callback, parameters, shaders, etc.</p>
 17 	
 18 	<p>When an effect is instantiated from an effect template, any parameters the effect template
 19 	has which are do not have default values must be set before the object with the effect is rendered.</p>
 20 */
 21 c3dl.EffectTemplate = function()
 22 {
 23 	this.vertexShaders = [];
 24 	this.fragmentShaders = [];
 25 	this.isInitialized = false;
 26 	
 27 	// array of objects.
 28 	this.params = [];
 29 	this.renderingCB = null;
 30 	
 31 	//
 32 	//
 33 	this.programObjects = [];
 34 	
 35 	
 36 	/**
 37 		Call this once the vertex shader, fragment shader and rendering callback have been 
 38 		set and all parameters have been created. Once the EffectTemplate has been initialized,
 39 		neither the shaders, callback or parameters can be changed.
 40 		
 41 		@returns {bool} true if the EffectTemplate was initialized, false otherwise.
 42 	*/
 43 	this.init = function()
 44 	{
 45 		var rc = false;
 46 
 47 		// can only be initialize once
 48 		if(	this.isInitialized == false)
 49 		{
 50 			// these variables had to be set, parameters variable
 51 			// is optional.
 52 			if(	this.renderingCB && this.vertexShaders.length > 0 && 
 53 				this.fragmentShaders.length > 0)
 54 			{
 55 				this.isInitialized = true;
 56 				rc = true;
 57 			}
 58 		}
 59 		return rc;
 60 	}
 61 
 62 	/**
 63 		Adds a fragment shader to the list of fragment shaders which need to be compiled.
 64 		The fragment shader strings are literally added together, therefore the order
 65 		which they are added is important. For example, if the material struct it used,
 66 		the variable holding the material string code should be added before the light
 67 		string code since the light code depends on material code.
 68 
 69 		@param {String} fragmentShader
 70 	*/
 71 	this.addFragmentShader = function(fragmentShader)
 72 	{
 73 		if(this.isInitialized == false)
 74 		{
 75 			if(fragmentShader && typeof(fragmentShader) == "string")
 76 			{
 77 				this.fragmentShaders.push(fragmentShader);
 78 			}
 79 			else
 80 			{
 81 				c3dl.debug.logWarning("Invalid argument passed to Effect's addFragmentShader().");
 82 			}
 83 		}
 84 	}
 85 
 86 	/**
 87 		Add a parameter which and instance effect can modify. Parameters should be simple, built-in
 88 		types such as Boolean, Number, Array etc. They should not be Objects created with {}.
 89 		
 90 		@param {String} paramName 
 91 		@param paramType Constructor used to create the object such as Boolean, Number, Array, etc.
 92 		@param paramDefaultValue The default value to be used if the user
 93 		does not specify any value. If null, user will have to specify a value, 
 94 		otherwise the object with this effect will not be rendered.
 95 	*/
 96 	this.addParameter = function(paramName, paramType, paramDefaultValue)
 97 	{
 98 		if(this.isInitialized == false)
 99 		{
100 			if(paramName && typeof(paramName) == "string")
101 			{
102 				var val;
103 				
104 				if(paramType == Array)
105 				{
106 					val = c3dl.copyObj(paramDefaultValue);				
107 				}
108 				else
109 				{
110 					val = paramDefaultValue;
111 				}
112 				
113 				// Each parameter will be an object, so when copying is done
114 				// we can iterate the list by simply incrementing by one.
115 				this.params.push({name:paramName, type:paramType, value:val});
116 			}
117 			else
118 			{
119 				c3dl.debug.logWarning("Invalid argument(s) passed to Effect's addParameter().");
120 			}
121 		}
122 		else
123 		{
124 			c3dl.debug.logWarning("Effect addParameter(): cannot be called once an effect has been initialized.");
125 		}
126 	}
127 
128 	/**
129 		Adds a vertex shader to the list of vertex shaders which need to be compiled.
130 		The vertex shader strings are literally added together, therefore the order
131 		which they are added is important. For example, if the material struct it used,
132 		the variable holding the material string code should be added before the light
133 		string code since the light code depends on material code.
134 		
135 		@param {String} vertexShader
136 	*/
137 	this.addVertexShader = function(vertexShader)
138 	{
139 		if(this.isInitialized == false)
140 		{
141 			if(vertexShader && typeof(vertexShader) == "string")
142 			{
143 				this.vertexShaders.push(vertexShader);
144 			}
145 			else
146 			{
147 				c3dl.debug.logWarning("Invalid argument passed to Effect's addVertexShader().");
148 			}
149 		}
150 	}
151 	
152 	/**
153 		@private
154 		Renderer will call this when it needs to compile the vertex shaders.
155 		
156 		@returns {String[]} vertex shaders.
157 	*/
158 	this.getVertexShaders = function()
159 	{
160 		return this.vertexShaders;
161 	}
162 	
163 	/**
164 		@private
165 		
166 		Get the array of all the parameters of this effect template.
167 		
168 		@returns {Array} array of objects.
169 	*/
170 	this.getParameters = function()
171 	{
172 		var ret = [];
173 		
174 		for(var i =0; i < this.params.length; i++)
175 		{
176 			var val;
177 			
178 			if(typeof this.params[i].value == "Array")
179 			{
180 				val = c3dl.copyObj(this.params[i].value);
181 			}
182 			else
183 			{
184 				val = this.params[i].value;
185 			}
186 
187 			ret.push(
188 						{name:this.params[i].name,
189 						type:this.params[i].type,
190 						value:val}
191 					);
192 		}
193 
194 		return ret;
195 	}
196 	
197 	/**
198 		@private
199 		
200 		Renderer will call this when it needs to compile the fragment shaders.
201 		
202 		@returns {String[]} fragment shaders
203 	*/
204 	this.getFragmentShaders = function()
205 	{
206 		return this.fragmentShaders;
207 	}
208 	
209 	/**
210 		Get the callback which is to be called when the
211 		geometric object with an effect created from this effect template is rendered.
212 
213 		@returns {Function} The function which will render the geometric object
214 		with an effect created from this template effect.
215 	*/
216 	this.getRenderingCallback = function()
217 	{
218 		return this.renderingCB;
219 	}
220 
221 	/**
222 		Set the rendering callback which will be called by the renderer when the
223 		object with this effect needs to be rendered. The renderer will pass a 
224 		renderingObject to the function which can be queried for context, renderer,
225 		effect, etc.
226 
227 		@see c3dl.RenderingObject.
228 		
229 		@param {Function} func
230 	*/
231 	this.setRenderingCallback = function(func)
232 	{
233 		if(this.isInitialized == false)
234 		{
235 			if(func instanceof Function)
236 			{
237 				this.renderingCB = func;
238 			}
239 			else
240 			{
241 				c3dl.debug.logWarning("Invalid argument passed to Effect's setRenderingCB().");
242 			}
243 		}
244 	}
245 	
246 	/**
247 		@private
248 
249 		When the renderer compiles the shaders, it returns a program object id. Each
250 		renderer will have its own id for this particular effect.
251 		
252 		@returns {int} -1 if the rendererID was not found in the list
253 	*/
254 	this.getProgramID = function(rendererID)
255 	{
256 		var programID = -1;
257     var found = false;
258 
259     for(var i = 0; found == false && i < this.programObjects.length; i++)
260 		{
261       if(found === false)
262       {
263         if( rendererID == this.programObjects[i].getRendererID())
264         {
265           found = true;
266           programID = this.programObjects[i].getProgramID();
267         }
268       }
269 		}
270     return programID;
271 	}
272   	
273 	/**
274 		@private
275 		
276 		@param {c3dl.ProgramObject} programObject
277 	*/
278 	this.addProgramObject = function(programObject)
279 	{
280 		this.programObjects.push(programObject);
281 	}
282 
283 	/**
284 		Get a string representation of this object.
285 		
286 		@param {String} [delimiter=","]  A string which will separate values.
287 	*/
288 	this.toString = function(delimiter)
289 	{
290 		if(!delimiter && typeof(delimiter) != "string")
291 		{
292 			delimiter = ",";
293 		}
294 		
295 		return	"Initialized = " + this.isInitialized + delimiter + 
296 				"Vertex Shaders = " + this.vertexShaders + delimiter +
297 				"Fragment Shaders = " + this.fragmentShaders + delimiter +
298 				"Rendering Callback = " + this.renderingCB + delimiter +
299 				"Parameters = " + this.parameters;
300 	}
301 }
302