home *** CD-ROM | disk | FTP | other *** search
-
- #import "WWSampleComponentList.h"
- #import "Protocol_WWRenderable.h"
-
- @implementation WWSampleComponentList
-
- - init
- {
- [super init];
-
- timestamp = -1.;
- cachedSample = [[WWSample alloc] init];
- // this isn't too smart, but to avoid surprises, you could...
- //[cachedSample setFreeData:NO];
-
- cacheIsDirty = YES;
-
- return self;
-
- }
-
- - initCount:(unsigned)numSlots
- {
- [super initCount:numSlots];
-
- timestamp = -1.;
- cachedSample = [[WWSample alloc] init];
- // this isn't too smart, but to avoid surprises, you could...
- [cachedSample setFreeData:NO];
- cacheIsDirty = YES;
-
- return self;
-
- }
-
- - awake
- {
- [super awake];
-
- timestamp = -1.;
- cachedSample = [[WWSample alloc] init];
- cacheIsDirty = YES;
-
- return self;
-
- }
-
-
- - free
- {
- //NXLogError("WWSampleComponentList %p at time %f being free'ed\n", self, timestamp);
- //NXLogError("WWSample freeing cachedSample %p\n", cachedSample);
- [cachedSample free];
- return [super free];
- }
-
-
- - addObject_:anObject { cacheIsDirty = TRUE; return [super addObject:anObject]; }
-
- - setData:newData timestamp:(float)floatTime generator:(const char *)genName weight:(float)floatWeight
- {
- WWSample *newSample, *oldSample;
- BOOL notFound;
- int i, howMany;
- const char *previousGenerator;
-
-
- // okay, so here's the deal. Whenever we get sent this msg, we
- // first check and see if we have any data, i.e. ([self count]). If we
- // don't, we malloc up a new WWSample using this info, stick it in our
- // list, set our timestamp to be this, then set the contents of our cache
- // to new sample, mark the cache clean, and return. Actually, we should be
- // lazy about it and only compute the cache as necessary...
-
- if (![self count])
- { timestamp = floatTime;
- newSample = [[[WWSample alloc] init] setData:newData timestamp:floatTime generator:genName weight:floatWeight];
- [self addObject_:newSample];
- }
- else
- { // need to see if we already have a sample from this source
- notFound = YES;
- i = 0;
- howMany = [self count];
-
- while (notFound && (i < howMany))
- { oldSample = [self objectAt:i];
- previousGenerator = [oldSample generatorName];
- if (!genName)
- { if (!previousGenerator) // they're both NULL; that's it; reuse the old sample
- { [oldSample setData:newData timestamp:floatTime generator:genName weight:floatWeight];
- notFound = FALSE;
- }
- else // genName is NULL but this sample's not; it's not it
- {}
- }
- else // genName isn't NULL...
- { if (previousGenerator) // they're both not NULL; check the strings
- { if (!strcmp(genName, previousGenerator)) // same generators; that's it
- { [oldSample setData:newData timestamp:floatTime generator:genName weight:floatWeight];
- notFound = FALSE;
- }
- else // nope, they're not the same...
- {}
- }
- else // genName is NULL but this sample's not; it's not it
- {}
- }
- i++;
- }
- if (notFound)
- { newSample = [[[WWSample alloc] init] setData:newData timestamp:floatTime generator:genName weight:floatWeight];
- [self addObject_:newSample];
- }
- }
- cacheIsDirty = YES;
- return self;
- }
-
-
- - setFreeData:(BOOL)flag
- {
- return self;
- }
-
-
- - (BOOL)freeData
- {
- return YES;
- }
-
-
- - (const char *)generatorName { return (const char *)NULL; }
-
-
- - addObject:(id <WWSample>)anObject
- {
- if ([anObject conformsTo:@protocol(WWSample)])
- { cacheIsDirty = TRUE;
- return [self setData:[anObject data] timestamp:[anObject timestamp] generator:[anObject generatorName] weight:[anObject weight]];
- }
- NXLogError("object %s doesn't conform to the WWSample protocol: can't add it to the WWSampleComponentList", [[anObject class] name]);
- return nil;
- }
-
-
- - insertObject:anObject at:(unsigned)index { cacheIsDirty = TRUE; return [super insertObject:anObject at:(unsigned)index]; }
- - removeObjectAt:(unsigned)index { cacheIsDirty = TRUE; return [super removeObjectAt:(unsigned)index]; }
- - removeLastObject { cacheIsDirty = TRUE; return [super removeLastObject]; }
- - replaceObjectAt:(unsigned)index with:newObject { cacheIsDirty = TRUE; return [super replaceObjectAt:(unsigned)index with:newObject]; }
- - appendList: (List *)otherList { cacheIsDirty = TRUE; return [super appendList: (List *)otherList]; }
- - addObjectIfAbsent:anObject { cacheIsDirty = TRUE; return [super addObjectIfAbsent:anObject]; }
- - removeObject:anObject { cacheIsDirty = TRUE; return [super removeObject:anObject]; }
- - replaceObject:anObject with:newObject { cacheIsDirty = TRUE; return [super replaceObject:anObject with:newObject]; }
- - empty { cacheIsDirty = TRUE; timestamp = -1; return [super empty]; }
-
- - fillCache
- {
- int i = 1,
- howMany = [self count];
- float u, weightThusFar;
- id <WWSample> b = nil,
- newData = nil;
-
-
- if (cacheIsDirty)
- { // yow. time to fill dat der cache...
-
- if (timestamp == -1)
- { // yow. never set my timestamp
- timestamp = [[self objectAt:0] timestamp];
- }
-
- // okay, let's say we have 3 samples, the first two with weights
- // of 1, and the third has a weight of 2
- // a == .2, b == 3; c == 1
- // so we want d = ((.2 * 1) + (3 * 1) + (1 * 2))/(1 + 1 + 2) == (.2 + 3 + 2)/4 == 5.2/4
-
- // can we recreate this incrementally
- // d1 = lerp between a and b
- // hmm... let's take a simpler example: 1 4 4; 9/3; 3
- // lerp the first two; .5 between
- // lerp the result and the next; .333
-
- // hmm... let's take a simpler example: 1 4 4 7; 16/4; 4
- // lerp the first two; .5 between (2.5)
- // lerp the result and the next; .333 ((2.5 + (1.5/3)) == 3)
- // lerp the result and the next; .25 ((3 + (4/4)) == 4)
-
-
- // copy the first element to the cache
- newData = [[(WWSample *)[self objectAt:0] data] copy];
- [cachedSample setData:newData timestamp:timestamp generator:"cache" weight:1.0];
- weightThusFar = [[self objectAt:0] weight];
- while (i < howMany)
- { b = [self objectAt:i];
- //u = (1./(float)(i+1)) * ([b weight]/(weightThusFar/i));
-
- u = 1./((float)(i+1)); // this version of calculating u ignores weights
- [[cachedSample data] lerpSelfWith:[b data] by:u];
- weightThusFar += [b weight];
- i++;
- }
- }
- cacheIsDirty = NO;
-
- return self;
- }
-
-
- - (float)timestamp { [self fillCache]; return timestamp; }
-
- - sample { [self fillCache]; return cachedSample; }
-
-
- - data { [self fillCache]; return [cachedSample data]; }
-
-
- - setWeight:(float)newWeight { return self; }
-
-
- - (float)weight { return 1.0; }
-
- - (int)generatorCount { return [self count]; }
-
- - writeEve:(NXStream *)stream atTabLevel:(int)tab
- {
- int howMany = [self count],
- i, j = 0;
-
-
- //for (i = 0; i < tab; i++)
- //{ NXPrintf(stream, "\t");
- //}
-
- if (howMany)
- { if (howMany > 1)
- { [(id <WWRenderable>)[self objectAt:0] writeEve:stream atTabLevel:0];
- NXPrintf(stream, " \\\n");
- for (j = 1; j < (howMany-1); j++)
- { for (i = 0; i < tab; i++)
- { NXPrintf(stream, "\t");
- }
- for (i = 0; i < 33; i++) // magic numbers are evil, wave!!
- { NXPrintf(stream, " ");
- }
- [(id <WWRenderable>)[self objectAt:j] writeEve:stream atTabLevel:0];
- NXPrintf(stream, " \\\n");
- }
- for (i = 0; i < tab; i++)
- { NXPrintf(stream, "\t");
- }
- for (i = 0; i < 33; i++) // magic numbers are evil, wave!!
- { NXPrintf(stream, " ");
- }
- }
- [(id <WWRenderable>)[self objectAt:j] writeEve:stream atTabLevel:0];
- }
-
- return self;
- }
-
- - write3DTextScene:(NXStream *)stream atTabLevel:(int)tab index:(int)index time:(float)time until:(float)lastTime
- {
- int howMany = [self count],
- i;
-
-
- for (i = 0; i < tab; i++)
- { NXPrintf(stream, "\t");
- }
-
- // should look like this:
- // {$owner1 {$compiledSample1}} {$owner2 {$compiledSample1}} {$owner3 {$compiledSample3}}
- // so we want to actually get our samples to give us this info directly, and so I shouldn't
- // recurse down calling the same routine as this. We need a new routine... actuallly, why
- // not just get the sample to write its eve code into the stream...
-
-
- NXPrintf(stream, "startShape %s; ", [[[self objectAt:i] class] name]);
- // need tab
- // need index (position in current list)
- NXPrintf(stream, "set __text__(colorTime%f) {1 1 1};", time);
- NXPrintf(stream, "set __text__(colorSurface%f) plastic;", time);
- NXPrintf(stream, "animatable: {Color $__text__(colorTime%f) }; ", time); // to make animation easy
- NXPrintf(stream, "animatable: {Surface $__text__(colorSurface%f)}; ", time); // to make animation easy
-
- NXPrintf(stream,
- "animatable: {Translate [expr { %d * $__text__(tabLength)}] ", tab);
- NXPrintf(stream,
- "[expr {$__text__(spacingFactor) * %d * $__text__(spacing) * $__text__(fontSize)}] ", index);
- NXPrintf(stream,
- "[expr {$__text__(sampleOffset) + ($__text__(timeFactor) * %f)}]};\n", time);
- NXPrintf(stream, " animatable: {WW3DText $__text__(fontName) $__text__(fontSize) {");
-
-
- for (i = 0; i < howMany; i++)
- { [(id <WWRenderable>)[self objectAt:i] writeEve:stream atTabLevel:tab];
- // need to be moving stuff to the left here - no, since it's one string, it will be fine......
- }
- NXPrintf(stream, "} left;}\n");
- NXPrintf(stream, "endShape;\n");
-
- return self;
- }
-
-
-
- // boy, this is dumb... This is to get around the stupid warnings from the compiler - ask wave for details
- - class { return [super class]; }
- - (BOOL) conformsTo: (Protocol *)aProtocolObject { return [super conformsTo:aProtocolObject]; }
-
- @end
-