1 //////////////////////////////////////////////////////////////////////
2 //
3 // Pixie
4 //
5 // Copyright � 1999 - 2003, Okan Arikan
6 //
7 // Contact: okan@cs.utexas.edu
8 //
9 // This library is free software; you can redistribute it and/or
10 // modify it under the terms of the GNU Lesser General Public
11 // License as published by the Free Software Foundation; either
12 // version 2.1 of the License, or (at your option) any later version.
13 //
14 // This library is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 // Lesser General Public License for more details.
18 //
19 // You should have received a copy of the GNU Lesser General Public
20 // License along with this library; if not, write to the Free Software
21 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 //
23 ///////////////////////////////////////////////////////////////////////
24 ///////////////////////////////////////////////////////////////////////
25 //
26 // File : shader.cpp
27 // Classes : CShader
28 // CShaderInstance
29 // Description : Implementation
30 //
31 ////////////////////////////////////////////////////////////////////////
32 #include <math.h>
33 #include <stdio.h>
34 #include <string.h>
35
36 #include "error.h"
37 #include "shader.h"
38 #include "stats.h"
39 #include "shading.h"
40 #include "bundles.h"
41 #include "memory.h"
42 #include "renderer.h"
43 #include "attributes.h"
44 #include "common/align.h"
45
46
47 ///////////////////////////////////////////////////////////////////////
48 // Class : CShader
49 // Method : CShader
50 // Description : Constructor
51 // Return Value : -
52 // Comments :
CShader(const char * name)53 CShader::CShader(const char *name) : CFileResource(name) {
54 atomicIncrement(&stats.numShaders);
55
56 name = NULL;
57 memory = NULL;
58 codeArea = NULL;
59 constantEntries = NULL;
60 varyingSizes = NULL;
61 strings = NULL;
62 parameters = NULL;
63 flags = 0;
64 data = NULL;
65 }
66
67 ///////////////////////////////////////////////////////////////////////
68 // Class : CShader
69 // Method : ~CShader
70 // Description : Dtor
71 // Return Value : -
72 // Comments :
~CShader()73 CShader::~CShader() {
74 int i;
75 CVariable *cParameter;
76
77 atomicDecrement(&stats.numShaders);
78
79 // Ditch the parameters
80 while((cParameter = parameters) != NULL) {
81 parameters = parameters->next;
82
83 // Delete the default values
84 if (cParameter->defaultValue != NULL) delete [] (float *) cParameter->defaultValue;
85
86 // Delete the parameter
87 delete cParameter;
88 }
89
90 // Delete the strings allocated for this shader
91 for (i=0;i<numStrings;i++) {
92 free(strings[i]);
93 }
94
95 // Delete additional data
96 if (data != NULL) delete data;
97
98 // Ditch the memory baby
99 if (memory != NULL) free_untyped(memory);
100 }
101
102
103
104 ///////////////////////////////////////////////////////////////////////
105 // Class : CShader
106 // Method : analyse
107 // Description : work out if there are special parameters
108 // Return Value : -
109 // Comments :
analyse()110 void CShader::analyse() {
111 CVariable *cVariable;
112 int varIndex = numGlobals-1;
113
114 // BEWARE that this is reversed compareed to the instance order
115 // hence the reversed counting of globalIndex
116 for (cVariable=parameters;cVariable!=NULL;cVariable=cVariable->next) {
117 // Check for special parameters
118 if (cVariable->storage == STORAGE_MUTABLEPARAMETER || cVariable->storage == STORAGE_GLOBAL) {
119 if (type == SL_LIGHTSOURCE) {
120 if (!strcmp(cVariable->name,"__nondiffuse")) {
121 if ((cVariable->numItems == 1) && cVariable->type == TYPE_FLOAT) {
122 flags |= SHADERFLAGS_NONDIFFUSE;
123
124 if (data == NULL) data = new CLightShaderData;
125 CLightShaderData *lightData = (CLightShaderData*) data;
126
127 lightData->nonDiffuseIndex = varIndex;
128 lightData->nonDiffuseStep = ((cVariable->container == CONTAINER_CONSTANT) || (cVariable->container == CONTAINER_UNIFORM)) ? 0 : 1;
129 } else {
130 warning(CODE_BADTOKEN,"warning type mismatch for expected definition of __nondiffuse in shader \"%s\"",name);
131 }
132 } else if (!strcmp(cVariable->name,"__nonspecular")) {
133 if ((cVariable->numItems == 1) && cVariable->type == TYPE_FLOAT) {
134 flags |= SHADERFLAGS_NONSPECULAR;
135
136 if (data == NULL) data = new CLightShaderData;
137 CLightShaderData *lightData = (CLightShaderData*) data;
138
139 lightData->nonSpecularIndex = varIndex;
140 lightData->nonSpecularStep = ((cVariable->container == CONTAINER_CONSTANT) || (cVariable->container == CONTAINER_UNIFORM)) ? 0 : 1;
141 } else {
142 warning(CODE_BADTOKEN,"warning type mismatch for expected definition of __nonspecular in shader \"%s\"",name);
143 }
144 }
145 }
146 varIndex--;
147 }
148 }
149
150 if (usedParameters & PARAMETER_NONAMBIENT)
151 flags |= SHADERFLAGS_NONAMBIENT;
152 }
153
154
155
156 ///////////////////////////////////////////////////////////////////////
157 // Class : CShaderInstance
158 // Method : CShaderInstance
159 // Description : Ctor
160 // Return Value : -
161 // Comments :
CShaderInstance(CAttributes * a,CXform * x)162 CShaderInstance::CShaderInstance(CAttributes *a,CXform *x) {
163 atomicIncrement(&stats.numShaderInstances);
164
165 attach();
166
167 xform = x;
168 xform->attach();
169
170 categories = NULL;
171 parameters = NULL; // The children class may want to clone the parent's parameters here
172 }
173
174 ///////////////////////////////////////////////////////////////////////
175 // Class : CShaderInstance
176 // Method : CShaderInstance
177 // Description : Ctor
178 // Return Value : -
179 // Comments :
~CShaderInstance()180 CShaderInstance::~CShaderInstance() {
181 atomicDecrement(&stats.numShaderInstances);
182
183 xform->detach();
184
185 if (categories != NULL) delete[] categories;
186
187 // Note: data is not owned by the instance
188
189 // The children class must clear the parameter list here
190 }
191
192
193
194 ///////////////////////////////////////////////////////////////////////
195 // Function : getToken
196 // Description : This function implements "strsep" which is non portable
197 // Return Value :
198 // Comments :
token(char ** str,const char * tok)199 static char *token(char **str,const char *tok) {
200 char *cStr = *str;
201 char *oStr = cStr;
202 int n = (int) strlen(tok);
203 int i;
204
205 if (cStr == NULL) return NULL;
206
207 // Walk the string
208 for (;;) {
209
210 // Did we run out of string ?
211 if (*cStr == '\0') {
212 *str = NULL;
213 break;
214 }
215
216 // Check for a delimiter
217 for (i=0;i<n;i++) {
218 if (*cStr == tok[i]) break;
219 }
220
221 if (i == n) cStr++;
222 else {
223 // Delimiter found, terminate
224 *cStr = '\0';
225 *str = cStr+1;
226 break;
227 }
228 }
229
230 return oStr;
231 }
232
233 ///////////////////////////////////////////////////////////////////////
234 // Class : CShaderInstance
235 // Method : CShaderInstance
236 // Description : Ctor
237 // Return Value : -
238 // Comments :
createCategories()239 void CShaderInstance::createCategories() {
240 char *categoryString,*cCat,*tmp;
241 int i,numCategories;
242
243 if (getParameter("__category",&categoryString,NULL,NULL) == TRUE) {
244 tmp = categoryString = strdup(categoryString);
245
246 numCategories = 2;
247 while (*tmp!='\0') {
248 if (*tmp == ',') numCategories++;
249 tmp++;
250 }
251 tmp = categoryString;
252
253 categories = new int[numCategories];
254
255 i=0;
256 do {
257 cCat = token(&tmp,",\t ");
258 if (*cCat != '\0') categories[i++] = CRenderer::getGlobalID(cCat);
259 } while (tmp != NULL);
260 categories[i++] = 0; // terminate the list
261
262 free(categoryString);
263 }
264 }
265
266
267
268
269
270
271
272
273
274
275 ///////////////////////////////////////////////////////////////////////
276 // Class : CProgrammableShaderInstance
277 // Method : CProgrammableShaderInstance
278 // Description : Ctor
279 // Return Value : -
280 // Comments :
CProgrammableShaderInstance(CShader * p,CAttributes * a,CXform * x)281 CProgrammableShaderInstance::CProgrammableShaderInstance(CShader *p,CAttributes *a,CXform *x) : CShaderInstance(a,x) {
282 CVariable *cVariable;
283
284 strings = NULL;
285 parent = p;
286 flags = parent->flags;
287 data = parent->data;
288
289 // Clone the parent's parameter list
290 // BEWARE that this reverses the order (which matters when counting the globalIndex)
291 for (cVariable=parent->parameters;cVariable!=NULL;cVariable=cVariable->next) {
292 CVariable *nVariable = new CVariable;
293
294 *nVariable = *cVariable;
295 nVariable->next = parameters;
296 parameters = nVariable;
297
298 // Allocate a new default value
299 if (nVariable->type == TYPE_STRING) {
300 nVariable->defaultValue = new char *[nVariable->numFloats];
301 memcpy(nVariable->defaultValue,cVariable->defaultValue,nVariable->numFloats*sizeof(char*));
302 } else {
303 nVariable->defaultValue = new float[nVariable->numFloats];
304 memcpy(nVariable->defaultValue,cVariable->defaultValue,nVariable->numFloats*sizeof(float));
305 }
306 }
307 }
308
309 ///////////////////////////////////////////////////////////////////////
310 // Class : CProgrammableShaderInstance
311 // Method : ~CProgrammableShaderInstance
312 // Description : Dtor
313 // Return Value : -
314 // Comments :
~CProgrammableShaderInstance()315 CProgrammableShaderInstance::~CProgrammableShaderInstance() {
316 CVariable *cParameter;
317 CAllocatedString *cString;
318
319 // Ditch the parameters
320 while((cParameter = parameters) != NULL) {
321 parameters = parameters->next;
322
323 // Delete the default values
324 if (cParameter->defaultValue != NULL) {
325 if (cParameter->type == TYPE_STRING) delete [] (char**) cParameter->defaultValue;
326 else delete [] (float*) cParameter->defaultValue;
327 }
328
329 // Delete the parameter
330 delete cParameter;
331 }
332
333 // Ditch the allocated strings
334 while((cString = strings) != NULL) {
335 strings = cString->next;
336
337 free(cString->string);
338 delete cString;
339 }
340
341 }
342
343
344 ///////////////////////////////////////////////////////////////////////
345 // Class : CProgrammableShaderInstance
346 // Method : setParameters
347 // Description : Set the values of the parameters
348 // Return Value : -
349 // Comments :
setParameter(const char * param,const void * val)350 int CProgrammableShaderInstance::setParameter(const char *param,const void *val) {
351 CVariable *cParameter;
352
353 for (cParameter=parameters;cParameter!=NULL;cParameter=cParameter->next) {
354
355 if (strcmp(param,cParameter->name) == 0) {
356 switch(cParameter->type) {
357 case TYPE_FLOAT:
358 {
359 const float *src = (const float *) val;
360 float *dest = (float *) cParameter->defaultValue;
361 memcpy(dest,src,cParameter->numItems*sizeof(float));
362 }
363 break;
364 case TYPE_COLOR:
365 {
366 int p;
367 const float *src = (const float *) val;
368 float *dest = (float *) cParameter->defaultValue;
369
370 for (p=cParameter->numItems;p>0;p--,dest+=3,src+=3) {
371 movvv(dest,src);
372 }
373 }
374 break;
375 case TYPE_VECTOR:
376 {
377 int p;
378 const float *src = (const float *) val;
379 float *dest = (float *) cParameter->defaultValue;
380
381 for (p=cParameter->numItems;p>0;p--,dest+=3,src+=3) {
382 mulmv(dest,xform->from,src);
383 }
384 }
385 break;
386 case TYPE_NORMAL:
387 {
388 int p;
389 const float *src = (const float *) val;
390 float *dest = (float *) cParameter->defaultValue;
391
392 for (p=cParameter->numItems;p>0;p--,dest+=3,src+=3) {
393 mulmn(dest,xform->to,src);
394 }
395 }
396 break;
397 case TYPE_POINT:
398 {
399 int p;
400 const float *src = (const float *) val;
401 float *dest = (float *) cParameter->defaultValue;
402
403 for (p=cParameter->numItems;p>0;p--,dest+=3,src+=3) {
404 mulmp(dest,xform->from,src);
405 }
406 }
407 break;
408 case TYPE_MATRIX:
409 {
410 const float *src = (const float *) val;
411 float *dest = (float *) cParameter->defaultValue;
412 memcpy(dest,src,cParameter->numItems*sizeof(matrix));
413 }
414 break;
415 case TYPE_QUAD:
416 {
417 const float *src = (const float *) val;
418 float *dest = (float *) cParameter->defaultValue;
419 memcpy(dest,src,cParameter->numItems*sizeof(float)*4);
420 }
421 break;
422 case TYPE_DOUBLE:
423 {
424 const float *src = (const float *) val;
425 float *dest = (float *) cParameter->defaultValue;
426 memcpy(dest,src,cParameter->numItems*sizeof(float)*2);
427 }
428 break;
429 case TYPE_STRING:
430 {
431 const char **src = (const char **) val;
432 char **dest = (char **) cParameter->defaultValue;
433 int t;
434 CAllocatedString *nString;
435
436 for (t=cParameter->numItems;t>0;t--) {
437
438 nString = new CAllocatedString;
439 nString->string = strdup(*src++);
440 nString->next = strings;
441 strings = nString;
442
443 *dest++ = nString->string;
444 }
445 }
446 break;
447 case TYPE_INTEGER:
448 {
449 // This should not be possible
450 error(CODE_BUG,"Integer shader variable in shader \"%s\"\n",parent->name);
451 const int *src = (const int *) val;
452 int *dest = (int *) cParameter->defaultValue;
453 memcpy(dest,src,cParameter->numItems*sizeof(int));
454 }
455 break;
456 default:
457 break;
458 }
459
460 break;
461 }
462 }
463
464 return cParameter != NULL;
465 }
466
467 ///////////////////////////////////////////////////////////////////////
468 // Class : CProgrammableShaderInstance
469 // Method : setParameters
470 // Description : Set the values of the parameters
471 // Return Value : -
472 // Comments :
setParameters(int np,const char ** params,const void ** vals)473 void CProgrammableShaderInstance::setParameters(int np,const char **params,const void **vals) {
474 int i;
475
476
477 // Set the parameter defaults
478 for (i=0;i<np;i++) {
479 if (setParameter(params[i],vals[i]) == FALSE) {
480 CVariable var;
481
482 if (parseVariable(&var,NULL,params[i]) == TRUE) {
483 if (setParameter(var.name,vals[i]) == FALSE) {
484 error(CODE_BADTOKEN,"Parameter \"%s\" not found in the shader\n",var.name);
485 }
486 } else {
487 error(CODE_BADTOKEN,"Parameter \"%s\" not found in the shader\n",params[i]);
488 }
489 }
490 }
491 }
492
493 ///////////////////////////////////////////////////////////////////////
494 // Class : CProgrammableShaderInstance
495 // Method : getParameter
496 // Description : Get the current value of a parameter
497 // Return Value : TRUE if successful, FALSE othervise
498 // Comments : The second void * must be float *
499 // if the type of the parameter is float/vector/matrix
500 // and char ** if the type of the parameter is string.
501 // iff var or globalIndex is NULL, we skip any parameters
502 // which are mutable
getParameter(const char * name,void * dest,CVariable ** var,int * globalIndex)503 int CProgrammableShaderInstance::getParameter(const char *name,void *dest,CVariable **var,int *globalIndex) {
504 int j;
505 int globalNumber = 0;
506 float *destFloat;
507 const float *srcFloat;
508 const char **destString;
509 const char **srcString;
510 int *destInt;
511 const int *srcInt;
512 CVariable *cParameter;
513
514 // BEWARE!
515 // the instance parameters are stored in the opposite order to the
516 // parent shader parameters. The counting scheme for globals used here must
517 // match that used when saving lights. In both cases, we loop the instance
518 // not the parent parameters
519
520 for (cParameter=parameters;cParameter!=NULL;cParameter=cParameter->next) {
521
522 // retrieve the storage in which the parameter lives
523 const int storage = cParameter->storage;
524 if (strcmp(name,cParameter->name) == 0) {
525
526 // Note: all parameters have storage, but for lights
527 // all we have to save in the light cache are the mutable ones
528 // We also only return defaults for interior and exterior (var == NULL)
529 if (!(storage == STORAGE_PARAMETER && parent->type == SL_LIGHTSOURCE) && (var != NULL) && (globalIndex != NULL)) {
530 *var = cParameter;
531 *globalIndex = globalNumber;
532 return TRUE;
533 }
534
535 switch(cParameter->type) {
536 case TYPE_FLOAT:
537 destFloat = (float *) dest;
538 srcFloat = (const float *) cParameter->defaultValue;
539 for (j=cParameter->numItems;j>0;j--)
540 *destFloat++ = *srcFloat++;
541 break;
542 case TYPE_COLOR:
543 case TYPE_VECTOR:
544 case TYPE_NORMAL:
545 case TYPE_POINT:
546 destFloat = (float *) dest;
547 srcFloat = (const float *) cParameter->defaultValue;
548 for (j=cParameter->numItems;j>0;j--,destFloat+=3,srcFloat+=3)
549 movvv(destFloat,srcFloat);
550 break;
551 case TYPE_MATRIX:
552 destFloat = (float *) dest;
553 srcFloat = (const float *) cParameter->defaultValue;
554 for (j=cParameter->numItems;j>0;j--,destFloat+=16,srcFloat+=16)
555 movmm(destFloat,srcFloat);
556 break;
557 case TYPE_QUAD:
558 destFloat = (float *) dest;
559 srcFloat = (const float *) cParameter->defaultValue;
560 for (j=cParameter->numItems;j>0;j--,destFloat+=4,srcFloat+=4)
561 movqq(destFloat,srcFloat);
562 break;
563 case TYPE_DOUBLE:
564 destFloat = (float *) dest;
565 srcFloat = (const float *) cParameter->defaultValue;
566 for (j=cParameter->numItems;j>0;j--) {
567 *destFloat++ = *srcFloat++;
568 *destFloat++ = *srcFloat++;
569 }
570 break;
571 case TYPE_STRING:
572 destString = (const char **) dest;
573 srcString = (const char **) cParameter->defaultValue;
574 for (j=cParameter->numItems;j>0;j--)
575 *destString++ = *srcString++;
576 break;
577 case TYPE_INTEGER:
578 // This should not be possible
579 error(CODE_BUG,"Integer shader variable in shader \"%s\"\n",name);
580 destInt = (int *) dest;
581 srcInt = (const int *) cParameter->defaultValue;
582 for (j=cParameter->numItems;j>0;j--)
583 *destInt++ = *srcInt++;
584 break;
585 default:
586 break;
587 }
588
589 return TRUE;
590 } else {
591 if (!(storage == STORAGE_PARAMETER && parent->type == SL_LIGHTSOURCE)) globalNumber++;
592 }
593 }
594
595 return FALSE;
596 }
597
598
599 ///////////////////////////////////////////////////////////////////////
600 // Class : CProgrammableShaderInstance
601 // Method : execute
602 // Description : Actually execute the shader
603 // Return Value : -
604 // Comments :
execute(CShadingContext * context,float ** locals)605 void CProgrammableShaderInstance::execute(CShadingContext *context,float **locals) {
606 context->execute(this,locals);
607 }
608
609 ///////////////////////////////////////////////////////////////////////
610 // Class : CProgrammableShaderInstance
611 // Method : requiredParameters
612 // Description : Return the required parameters
613 // Return Value : -
614 // Comments :
requiredParameters()615 unsigned int CProgrammableShaderInstance::requiredParameters() {
616 return parent->usedParameters;
617 }
618
619
620
621 ///////////////////////////////////////////////////////////////////////
622 // Class : CProgrammableShaderInstance
623 // Method : getName
624 // Description : Get the name of the shader
625 // Return Value : -
626 // Comments :
getName()627 const char *CProgrammableShaderInstance::getName() {
628 return parent->name;
629 }
630
631 ///////////////////////////////////////////////////////////////////////
632 // Class : CProgrammableShaderInstance
633 // Method : illuminate
634 // Description : Illuminate the shading state is a lightsource shader
635 // Return Value : -
636 // Comments :
illuminate(CShadingContext * context,float ** locals)637 void CProgrammableShaderInstance::illuminate(CShadingContext *context,float **locals) {
638
639 // This function should never be called for non-light shaders
640 assert(parent->type == SL_LIGHTSOURCE);
641 context->execute(this,locals);
642 }
643
644
645
646 ///////////////////////////////////////////////////////////////////////
647 // Class : CProgrammableShaderInstance
648 // Method : prepare
649 // Description : Allocate the cache and set the parameter defaults
650 // Return Value : -
651 // Comments :
prepare(CMemPage * & namedMemory,float ** varying,int numVertices)652 float **CProgrammableShaderInstance::prepare(CMemPage *&namedMemory,float **varying,int numVertices) {
653 CVariable *cVariable;
654 char *data;
655 float **locals;
656 int totalVaryingSize;
657 int i;
658
659 // Get const pointers for fast access
660 const int numVariables = parent->numVariables;
661 const int *varyingSizes = parent->varyingSizes;
662
663 // Compute the total memory we will need for the shader parameters
664 for (totalVaryingSize=0,i=0;i<numVariables;i++) {
665 if (varyingSizes[i] < 0) totalVaryingSize += -varyingSizes[i];
666 else totalVaryingSize += varyingSizes[i]*numVertices*3;
667 }
668
669 // Allocate memory for the temporary shader variables
670 // Allocate some extra space for alignment
671 data = (char *) ralloc(totalVaryingSize + numVariables*(sizeof(float *) + sizeof(float)),namedMemory);
672 locals = (float **) data;
673 data += numVariables*sizeof(float*);
674
675 // Save the memory
676 for (i=0;i<numVariables;i++) {
677
678 // Align the data to 64 bits
679 data = (char *) align64(data);
680
681 locals[i] = (float*) data;
682 if (varyingSizes[i] < 0) data += -varyingSizes[i];
683 else data += varyingSizes[i]*numVertices*3;
684 }
685
686 // For each parameter, copy over the default value of the parameter
687 for (cVariable=parameters;cVariable!=NULL;cVariable=cVariable->next) {
688 float *destf;
689 const float *srcf;
690 const char **dests;
691 const char **srcs;
692
693 // Find where we're writing
694 if (cVariable->storage == STORAGE_GLOBAL) {
695 destf = (float *) varying[cVariable->entry];
696 dests = (const char **) varying[cVariable->entry];
697 } else {
698 assert(cVariable->entry < numVariables);
699 destf = (float *) locals[cVariable->entry];
700 dests = (const char **) locals[cVariable->entry];
701 }
702
703 // This is the repetition amount
704 if ((cVariable->container == CONTAINER_UNIFORM) || (cVariable->container == CONTAINER_CONSTANT)) {
705
706 //assert(cVariable->numFloats == -parent->varyingSizes[cVariable->entry]);
707
708 if (cVariable->type == TYPE_STRING) {
709 if ((srcs = (const char **) cVariable->defaultValue) != NULL) {
710 int i;
711 for (i=cVariable->numFloats;i>0;i--) *dests++ = *srcs++;
712 }
713 } else {
714 if ((srcf = (const float *) cVariable->defaultValue) != NULL) {
715 int i;
716 for (i=cVariable->numFloats;i>0;i--) *destf++ = *srcf++;
717 }
718 }
719 } else {
720
721 //assert(cVariable->numFloats == parent->varyingSizes[cVariable->entry]);
722
723 if (cVariable->type == TYPE_STRING) {
724 if ((srcs = (const char **) cVariable->defaultValue) != NULL) {
725 int n;
726 const int c = cVariable->numFloats;
727
728 // FIXME: We may want to unroll these two loops
729 for(n=numVertices*3;n>0;n--) {
730 for (i=0;i<c;i++) *dests++ = srcs[i];
731 }
732 }
733 } else {
734 if ((srcf = (const float *) cVariable->defaultValue) != NULL) {
735 int n;
736 const int c = cVariable->numFloats;
737
738 // FIXME: We may want to unroll these two loops
739 for(n=numVertices*3;n>0;n--) {
740 for (i=0;i<c;i++) *destf++ = srcf[i];
741 }
742 }
743 }
744 }
745 }
746
747 return (float **) locals;
748 }
749
750
751
752
753
754
755 ///////////////////////////////////////////////////////////////////////
756 // Function : debugFunction
757 // Description : This function is used to debugging purposes
758 // Return Value : -
759 // Comments : You can trigger this function from the compiled shader
760 // code by debug ("f=o")
debugFunction(float * op)761 void debugFunction(float *op) {
762 fprintf(stderr,"Debug\n");
763 }
764
765