Monday, June 19, 2006


in AddRing:

int swcnt; // number of vertices in swarm polygon
int swidx; // index of swarm polygon's current vertex
int swrad; // radius of swarm polygon, in pixels

double theta = (PI * 2) * (double(swidx) / swcnt); // can be better optimized
Ring.Shift.x += sin(theta) * swrad;
Ring.Shift.y += cos(theta) * swrad;
swidx %= swcnt; // can be better optimized

NOTE that this feature requires the skew curve fix (see above), otherwise curves will be horribly distorted.

skew curve fix

Skew distorts curved rings; to avoid this, MakeCurves must use the skewed origin.

iorg = CPoint(round(org.x), round(org.y));

iorg = CPoint(round(xshift), round(yshift));

Monday, June 05, 2006

maximize list control within playlist dialog

void CPlaylistDlg::OnHideControls()
m_HideControls ^= 1;
CWnd *wp = GetWindow(GW_CHILD);
while (wp != NULL) {
if (wp != &m_List)
wp->ShowWindow(m_HideControls ? SW_HIDE : SW_SHOW);
wp = wp->GetNextWindow();

void CPlaylistDlg::OnSize(UINT nType, int cx, int cy)
CToolDlg::OnSize(nType, cx, cy);
if (m_HideControls) {
CRect r;
} else

void CPlaylistDlg::OnShowWindow(BOOL bShow, UINT nStatus)
CToolDlg::OnShowWindow(bShow, nStatus);
if (bShow && !m_HideControls)

Friday, June 02, 2006

MIDI support for video functions

Assuming MIDI ranges equal 5 (the default):

Video Select

num MIDI Video
pad Values Clip
0 0..12 0
1 13..25 1
2 26..38 2
3 39..51 3
4 52..63 4
5 64..76 5
6 77..89 6
7 90..101 7
8 102..114 8
9 115..126 9
. 127 None

Note that a value of 127 disables video. If this is undesirable, set Video Select's MIDI range to 4.99 instead of 5.

Video Blending

num MIDI
pad Values Blending description ROP code GDI name
0 0..12 ~Src & Dst AND inverted source with destination DSna
1 13..25 ~Src | Dst OR inverted source with destination DSno MERGEPAINT
2 26..38 Src & ~Dst AND source with inverted destination SDna SRCERASE
3 39..51 Src & ~Dst OR source with inverted destination SDno
4 52..63 Src & Dst AND source with destination DSa SRCAND
5 64..76 Src | Dst OR source with destination DSo SRCPAINT
6 77..89 Src ^ Dst XOR source with destination DSx SRCINVERT
7 90..101 ~(Src & Dst) AND source with destination, invert result DSan
8 102..114 ~(Src | Dst) OR source with destination, invert result DSon NOTSRCERASE
9 115..127 ~(Src ^ Dst) XOR source with destination, invert result DSxn

Video Cycle Length

num MIDI Cycle
pad Values Length
1 0..12 1
2 13..25 2
3 26..38 3
4 39..51 4
5 52..63 5
6 64..76 6
7 77..89 7
8 90..101 8
9 102..114 9
0 115..127 10

Note that numpad zero sets the cycle length to "all" which is effectively 10.

Tuesday, May 16, 2006

pre-fetching video

bool CPlaylistDlg::PreFetchVideo()
CAviToBmp vid;
for (int i = 0; i < BANKS; i++) {
int count = m_Bank[i].GetSize();
for (int j = 0; j < count; j++) {
LPCSTR Path = m_Bank[i][j].m_Path;
if (IsVideo(Path)) {
if (!vid.Open(Path))

This takes approx. 10 seconds for all 340 clips. We can't pre-open the clips because AVIStreamGetFrameOpen won't open more than 75 AVI files simultaneously (it fails with no error message). Not sure why just opening and closing the clips helps. Something to do with disk caching? Not sure how *much* it helps either.

The 10 second delay can be avoided by doing the pre-fetch in a background thread. Note the CoInitialize! AviFileOpen fails without it. This gives me a bad feeling that VfW isn't thread-safe...

UINT CPlaylistDlg::PreFetchVideoThread(LPVOID pParam)
CPlaylistDlg *pd = (CPlaylistDlg *)pParam;
if (!pd->PreFetchVideo())
AfxMessageBox("Can't pre-fetch video");

in CPlaylistDlg::Cache:
AfxBeginThread(PreFetchVideoThread, this,

The 75 clip limit is apparently NOT due to XVID, it occurs with Cinepack too.

Test to determine how much of Open delay is due to AVIStreamGetFrameOpen. Result for 100 Opens:

total avg min max
Open 4.915 .049 .007 .087
GetFr. 0.790 .008 .007 .009

A second run gave very similar results:

Open 3.119 .031 .007 .087
GetFr. 0.741 .007 .007 .009

Result: AVIStreamGetFrameOpen is less than 15% of the average, but more importantly, it has a static cost of ~8 milliseconds. This makes a reasonable case for trying the partial pre-open strategy (pre-open all clips but without doing AVIStreamGetFrameOpen, and then do AVIStreamGetFrameOpen when the clip is selected).

Wednesday, May 10, 2006

exporting a looped movie

Tried exporting a bidirectional movie (plays forward and then plays in reverse). It's smoother than just letting the video jump back to the beginning but there's still a visible transition when the rings change direction.

int PassFrames = m_RangeLast - m_RangeFirst + 1;
int TotalFrames = (PassFrames - 1) * 2;
int j = m_RangeLast;
pd.SetRange(0, TotalFrames);
for (int i = 0; i < TotalFrames; i++) {
if (i >= PassFrames)
HBITMAP bm = View->MakeDIB(&OutFrameSize, &m_InFrameSize, GetScaleToFit());
bool retc = bta.AddFrame(bm);
if (!m_DisplayOutput) // prevent view from painting
View->ValidateRect(NULL); // must revalidate before SetPos
if (pd.Canceled())


Tried it. It's too predictable and hugs the edge of the frame too much. Not so good! The random motion is better.

Tuesday, May 09, 2006

video mirroring quadrant selection

CSize FrmSz(m_Size);
CSize SrcSz(vp->GetFrameSize());
CPoint SrcPt;
if (m_st.Mirror) { = ( + 1) >> 1; = ( + 1) >> 1;
switch (Quadrant) {
case 0: // upper left
SrcPt = CPoint(0, - 1); >>= 1; >>= 1; =;
case 1: // upper right
SrcPt = CPoint( - 1, - 1); >>= 1; >>= 1; =; =;
case 2: // lower left
SrcPt = CPoint(0, 0); >>= 1; >>= 1;
case 3: // lower right
SrcPt = CPoint( - 1, 0); >>= 1; >>= 1; =;
} else {
SrcPt = CPoint(0, - 1); =;
HDC sdc;
StretchBlt(dc, 0, 0,,, sdc,
SrcPt.x, SrcPt.y,,, m_VideoList.GetROP());

Monday, April 24, 2006

more optimizations

In Draw:
replaced CRect::OffsetRect in Bounds calc with inline
replaced CRect::PtInPect in inner loop with inline (BIG difference)
only calculate iorg if making Curves

hot rod, 1024 x 768, maximized (but not full screen)
default patch, fill & outline, speed = 20 "curve fill bench.whl"
total Draw time for 1000 frames at 25 FPS, in seconds
average ring count = 193

v1.1.01 v1.4.04
12.643 12.831
12.630 12.641
12.629 12.639
12.629 12.830
12.638 12.651
12.633 12.638
12.637 12.833
12.636 12.645
12.630 12.840

12.634 12.698

1.4.04 has a noticeably larger deviation: most of the samples cluster around 12.64 but every fourth sample or so clusters around 12.83. No idea why! Generally the results are encouraging however. If we go by the averages, 1.4.04 is 64 microseconds slower per frame, which translates to an extra 1.6 milliseconds per second at 25 FPS. If we go by the worst case, 1.4.04 is 210 microseconds slower per frame, i.e. an extra 5.25 milliseconds per second (half a percent). Neither difference is likely to be significant.

Same exact test but without fill (still on hot rod):
v1.1.01 v1.4.04
0.428 0.413
0.428 0.413

For non-fill case, 1.4.04 is FASTER! Awesome. Presumably inlining PtInRect in the innermost loop made the big difference, let's see.

1.4.04 no fill, using CRect::PtInRect:

Yup, CRect::PtInRect was bad stuff. Not sure whether it was the function call or inefficiency within PtInRect itself, or maybe both.

An interesting question: why is the difference worse with fill/outline? Almost all of the added code gets executed regardless of draw mode. The only exceptions are the Convex test to decide rp.Color vs. PrevColor, and the (Curve || PrevCurve) test. Surely these can't account for 64..210 microseconds per frame?

per-ring curve decision benchmarks

Bottom line: The per-ring version (1.4.03) is very slightly faster than 1.4.02. The data shows a consistent improvement of between 5 and 10 microseconds per frame. This is the opposite of the expected result. Perhaps moving the curvature test into the main loop allowed the compiler to better optimize the initial pass (for trail)? It could also be a change in cache behavior.

#include "benchmark.h"
float sum;
int cnt;
void CWhorldView::Draw(HDC dc)
CBenchmark b;
sum += b.Elapsed();
if (cnt == 1000) {
CString s;
s.Format("%d %f %f\n", cnt, sum, sum / cnt);

total time (sum) in seconds for 1000 frames

default patch
1.4.02 1.4.03
1.448 1.436
1.448 1.436
1.442 1.435
1.442 1.439
1.446 1.440

default patch, speed and canvas scale at max
1.4.02 1.4.03
5.400 5.390
5.396 5.390

help changes for 1.4

add Odd Curve and Even Curve to parameters (done)
move Canvas Scale and Hue Loop Length from Options/General to Master (done)
change ReadFromPatch to Patch Mode and expand as needed (done)
update keyboard accelerators (done)

Sunday, April 16, 2006

using multimedia timer instead of windows timer

We can't use a multimedia timer all the time (as was suggested on the MFC forum), because it significantly increases CPU useage, e.g. 75% vs. 33%. Windows task-switching overhead is the most likely culprit, especially since using a custom timer thread instead of a multimedia timer produces identical behavior.

We could use a multimedia timer only during non-client modal states, but there's still a visible glitch, due to the phase difference between the windows timer and the multimedia timer. The difference varies from 0 to 1 timer periods, and I can't see any obvious way to avoid it. It's better than doing nothing, but it may have other side effects, so the SendMessage technique shown above may still be the best shot.

Friday, April 14, 2006

corrupt Mirror, Origin, Drawmode in patches

The first cases appeared on 10/08/2005. Many of the "frosty" patches had it, and the corrupt data propagated from them to other patches, via hybridization. All patches were fixed today. I can't replicate the behavior nor can I find any obvious cause in the current code.

Tuesday, April 11, 2006

closing aux frame displays file save dialog

In CMainFrame::DetachView, must remove aux view from document.

GetDoc()->RemoveView(m_AuxView); // remove aux view from our document

prevent non-client clicks from pausing app

The following works, provided the "Show window contents while dragging" system property is unchecked. The only side effects are a) left-clicking on the menu bar moves the cursor to the center of the menu bar (strange, but not really a problem), and b) close happens on button down instead of button up.

void CPersistDlg::OnNcLButtonDown(UINT nHitTest, CPoint point)
switch (nHitTest) {
CDialog::OnNcLButtonDown(nHitTest, point);

void CPersistDlg::OnNcRButtonDown(UINT nHitTest, CPoint point)
switch (nHitTest) {
SendMessage(WM_CONTEXTMENU, (LONG)m_hWnd, MAKELONG(point.x, point.y));
CDialog::OnNcRButtonDown(nHitTest, point);

Wednesday, April 05, 2006

benchmarks for drawing AVI frames

test video: "Movie_051112214725 comp.avi"
Release mode
Master Rings = 0
Window size: 632 x 459
# samples: 1000 (40 seconds)
averages (times in seconds)

Dell / Windows 2000
using BitBlt, SRCCOPY
total 0.032182 <- i.e. avg duration of DrawAviFrame
get frame 0.013667 (42.5%)
create bitmap 0.009647 (30.0%)
blit 0.008469 (26.3%)
misc 0.000400 (1.2%)

using StretchBlt, SRCCOPY
total 0.047449 <- exceeds timer period! 47% worse than BitBlt
get frame 0.013653 (28.8%)
create bitmap 0.009758 (20.6%)
blit 0.023656 (49.9%)
misc 0.000383 (0.8%)

using StretchBlt, SRCINVERT
total 0.059013 <- 148% of timer period! 25% worse than SRCCOPY
get frame 0.013646 (23.1%)
create bitmap 0.010457 (17.7%)
blit 0.034508 (58.5%)
misc 0.000402 (0.7%)

Hotrod / XP
using BitBlt, SRCCOPY
total 0.004478 <- i.e. avg duration of DrawAviFrame
get frame 0.002263 (50.5%)
create bitmap 0.001238 (27.6%)
blit 0.000881 (19.7%)
misc 0.000096 (2.1%)

using StretchBlt, SRCCOPY
total 0.006912 <- 54% worse than BitBlt
get frame 0.002257 (32.7%)
create bitmap 0.001240 (17.9%)
blit 0.003314 (48.0%)
misc 0.000100 (1.4%)

using StretchBlt, SRCINVERT
total 0.008200 <- 20% of timer period, 19% worse than SRCCOPY
get frame 0.002267 (27.6%)
create bitmap 0.001239 (15.1%)
blit 0.004592 (56.0%)
misc 0.000102 (1.2%)

Hotrod is 7.2 times faster, but StretchBlt mode is bad stuff.

#include "benchmark.h"
double blitsum;
double createsum;
double getfrmsum;
double totsum;
int samps;
totsum += b.Elapsed();
if (samps == 1000) {
CString s;
double total = totsum / samps;
double getfrm = getfrmsum / samps;
double create = createsum / samps;
double blit = blitsum / samps;
double misc = total - (getfrm + create + blit);
s.Format("total\t%f\ngetfrm\t%f (%.1f%%)\ncreate\t%f (%.1f%%)\nblit\t%f (%.1f%%)\nmisc\t%f (%.1f%%)",
getfrm, getfrm / total * 100,
create, create / total * 100,
blit, blit / total * 100,
misc, misc / total * 100);

Friday, March 31, 2006

effect of ROPs on background

reverse polish notation! read right to left!

P Selected pen
D Destination bitmap
a Bitwise AND
n Bitwise NOT (inverse)
o Bitwise OR
x Bitwise exclusive OR (XOR)

1 R2_BLACK 0 makes everything black
2 R2_NOTMERGEPEN DPon black outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is black, else darker)
3 R2_MASKNOTPEN DPna black outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is white, else darker)
4 R2_NOTCOPYPEN Pn overwrites bg, inverted drawing color
5 R2_MASKPENNOT PDna white outlining leaves bg unchanged, else pen is translucent (correct when bg is black, else darker)
6 R2_NOT Dn reverses bg: outlining or overlap shows bg unchanged (it's inverted twice)
7 R2_XORPEN DPx the coolest
8 R2_NOTMASKPEN DPan white outlining leaves bg unchanged, else pen is inverted and translucent (correct when bg is black, else lighter)
9 R2_MASKPEN DPa white outlining leaves bg unchanged, else pen is translucent (correct when bg is white, else darker)
10 R2_NOTXORPEN DPxn similar to xor
11 R2_NOP D drawing is invisible
12 R2_MERGENOTPEN DPno drawing in white leaves bg unchanged, else pen is inverted and translucent (opaque when bg is black)
13 R2_COPYPEN P straight copy
14 R2_MERGEPENNOT PDno black outlining leaves bg unchanged, else pen is translucent (correct when bg is white)
15 R2_MERGEPEN DPo black outlining leaves bg unchanged, else pen is translucent (correct when bg is black)
16 R2_WHITE 1 makes everything white

Saturday, March 25, 2006

more zoom/origin hell

The really weird thing is, it looks much better if the origin DOESN'T move in mirror mode!!! So it's actually not broken at the moment, except for

1) the move and return thing, and that's gone now that the normalized origin isn't zoomed
2) the problem with trail (outer rings move when they shouldn't)
3) make DIB must still use center zoom, otherwise scale to fit will screw up mirrored snapshots

Thursday, March 23, 2006

zoom/origin hell

Unmirrored zooming is supposed to be origin-centered, i.e. you zoom into or out of the ring origin. This isn't allowed in mirror mode because it would change the image. In mirror mode, you zoom into or out of the *mirror origin* (i.e. the center of the screen), and let the ring origin move as needed so that the image stays the same.

origin behavior with zooming, version 1.1:

mirror drag behavior
no no stationary: correct
yes no moves: correct
no yes stationary: correct
yes yes stationary (or with damping, moves and then returns): BOGUS

origin behavior with zooming, version 1.3 (trail):

mirror drag behavior
no no moves: BOGUS
yes no moves: correct
no yes stationary, but with damping, moves and then returns: BOGUS
yes yes stationary (or with damping, moves and then returns): BOGUS

Note that random origin may have the same problems as drag origin; it's hard to tell.

Trail complicates things further, because if the origin moves and returns, the trail pattern is lost.

Really there are two separate problems:

1) The damping feature of the origin motion prevents the ring origin from moving during zoom, or causes it to slip back. As a result, zooming in mirror mode changes the mirror imagery. This behavior always occurred in mirror mode, but somehow it went unnoticed; adding trail made it more noticeable, by causing it to affect unmirrored mode also.

The likely cause is that When you zoom in mirrored mode, the view's normalized origin changes, but the main frame's target origin (also in normalized space) DOESN'T change, so the view eventually slips back to the target origin. This can be solved by saving the normalized origin before zooming the view, and then correcting the target origin by the difference between the new normalized origin and the saved one.

2) Adding trail broke zooming into the ring origin. It could be argued that this was a bad idea anyway and that we should always zoom into the mirror origin, in which case this bug is actually a feature. However, this argument doesn't really hold up, because zooming into the ring origin is typically more interesting than zooming into whatever happens to be in the center of the screen.

This problem could possibly be solved by a temporary coordinate space change, so that the zoom scaling is done in ring-origin space instead of mirror-origin space. You could calculate the difference between the master ring origin and the center of the screen, and then for each ring, subtract the difference from its origin, multiply by zoom, and add the difference back on.

Monday, March 20, 2006

FillPath vs. StrokeAndFillPath

benchmark of FillPath vs. StrokeAndFillPath with null brush:
no significant difference

uniform curvature with respect to number of sides

// decrease curvature as number of sides increases
// behavior is somewhat more orderly but not as interesting either
double s = 1.0f / float(Ring.Sides - 2);
Ring.EvenCurve = float(m_Parms.EvenCurve * s);
Ring.OddCurve = float(m_Parms.OddCurve * s);

Tuesday, February 28, 2006

help changes for 1.3

add Random Ramp to Waveforms GIF (done)
add Trail to Master Controls (done)
add Convex to Trail (done)
add Convex to Effects (done)
modify Origin Motion for Trail (done)
modify Mouse Modes for Trail (done)
modify Canvas Scale for new origin limits (done)
modify Zoom for new origin limits and Window/Zoom (done)
modify Panic for Trail reset (done)
update shortcut tables (done)
add Trail to features list on web site (done)
idea: comprehensive list of menu commands?

help changes for 1.2

Master offsets are now saved. Affected topics: Master Offsets, Playlists
Add oscillator override (new topic)
Add MIDI setup's Advanced checkbox
Zero controllers / Panic reset oscillator frequency overrides

Monday, February 27, 2006

MIDI color oscillator overrides

Frequency AND amplitude, for each color component (HLS), for both foreground and background color is 2 * 3 * 2 = 12 knobs. It turns out to be too much to handle live. Better to control the frequencies only, and hard-code the amplitudes:

amp freq (range)
color speed 1 .1
lightness .25 .1
saturation .5 .1
bk hue 180 .01
bk lightness .25 .1
bk saturation .5 .1

Thursday, February 23, 2006

don't draw to video memory

Supposedly mirrored mode is faster because it reduces drawing by 3/4, but even if this difference is eliminated (by restricting drawing to the upper left quadrant), mirrored mode is STILL faster. The reason is that mirrored mode always draws to system memory. The solution is to draw to system memory in all cases.

Tuesday, February 07, 2006

awesome matt trail demo

patch "pinwheel fab color", master 158, zoom 119, damping 95.5, tempo 14.96, unmirrored, fill, outline (requires serious hotrod action)

This demo proves that trail is way better than odd shift and should be up next on the enhancement list.

Friday, February 03, 2006

allow user to register BmpToAvi

Cool idea, but unnecessary: now that the installer correctly handles upgrading (each release must have a new project GUID, but the same upgrade GUID), this situation can't arise anymore.

if (bta.GetLastError() == CBmpToAvi::ERR_CREATE_SOURCE
static const LPCSTR BmpToAviName = "";
CPathStr path = CMainFrame::GetAppPath();
if (PathFileExists(path)) {
CString cmd;
cmd.Format("regsvr32 /s \"%s\"", path);
int retc = system(cmd);
if (retc)
goto retry;
} else {
CString s;
s.Format(IDS_MEX_BTA_NOT_FOUND, BmpToAviName);

no more 2 GB limit

I took a look at the OpenDML stuff, but in the end I decided to solve this the MS way: I wrote my own DirectShow show source filter that inserts frames (as bitmaps) into a video stream.

That turned out to be the *easy* part! The SDK's "ball" example was a pretty good starting part for writing a DirectShow source filter. The only real problem is that the ball filter draws the frames itself, whereas I needed to draw the frames in the application, and then pass them to the source filter. Turns out MS thought of that: you can override the DoBufferProcessingLoop function, cool.

There's also the minor detail of how to call my filter's "add frame" function from the application: turns out that requires creating a new COM interface. A bit of research, but not too bad. And of course I need some application code to create a filter graph, connect all the "pins" together and run the graph. There are examples of this all over the place, nothing new here.

The real problem came from an unexpected direction: how to set the video compression options? The Vfw AVIFile API had a wonderful function called AVISaveOptions: it displayed a dialog listing all your video compressors, and allowed you to select one and configure it. And in DirectShow? Nothing like that exists. Insane but true.

So I wound up writing a DirectShow Video Compression dialog object. You pass it your source and destination filters, and it displays the dialog, and returns a fully-configured compressor filter.

The bottom line? I have developed two reusable technologies:
1) A DirectShow BMP to AVI converter.
2) A DirectShow Video Compression dialog

I suspect both could be useful to other developers, so I'm going to submit them to CodeProject later. Right now my priority is integrating them into a new version of Whorld.

Wednesday, February 01, 2006

data rates

In AVIFile's Video Compression dialog, tried setting data rate for a few encoders:
indeo video 5.1
Intel indeo video R3.2
used steve1, crop, 320x240, frames 5000-5200
In both cases, checking the box caused the data rate in the file to drop by almost half REGARDLESS of what number was typed for data rate. crazy!

Things that weren't obvious

1) IAMVfwCompressDialogs must be used AFTER THE COMPRESSOR IS CONNECTED, otherwise Configure ignores SetState and reinitializes the dialog every time
2) IAMVideoCompression must be created on the OUTPUT PIN of the compressor, not the on compressor itself
3) any size other than 320 x 240 causes first-chance exception in QCAP.DLL (debug only)

more problems: can't seem to set output bit rate, maybe that just doesn't work

IPin *pComprOut = CDSBmpToAvi::GetPin(ip->pCompr, PINDIR_OUTPUT);
if (pComprOut != NULL) { // if we got compressor's output pin
IAMStreamConfig *pStreamCfg = NULL;
m_hr = pComprOut->QueryInterface( // get IAMVideoCompression
IID_IAMStreamConfig, (void **)&pStreamCfg);
if (pStreamCfg != NULL) { // do GetFormat after input pin is connected
m_hr = pStreamCfg->GetFormat(&pmt);
if (SUCCEEDED(m_hr)) {
printf("get format ok\n");
if (pmt->formattype == FORMAT_VideoInfo) {
printf("%d\n", pvh->dwBitRate);
pvh->dwBitRate = 10000;
m_hr = pStreamCfg->SetFormat(pmt);
if (SUCCEEDED(m_hr)) {
printf("set format ok\n");
// DeleteMediaType(pmt);

Sunday, January 29, 2006

DirectShow source filter

CodeGuru's snake sample prog doesn't work, it hangs
to register or unregister a filter (e.g.
regsrv32 /u
then draw a filter graph using graphedt.exe
to view, connect ball -> video renderer
to create AVI file, connect ball -> AVI mux -> file writer
this works fine, but important questions remain
1) how do you run the filter chain without using graphedt?
2) how do you get the app to communicate the frames to the filter?
3) where does the source filter's GUID come from?

Tuesday, January 10, 2006

invalid pixel format

Ben C's Gateway won't run Whorld in DirectDraw mode: it gives the error DDERR_INVALIDPIXELFORMAT. He initially had DirectX 8.0, I upgraded to 8.1 but it didn't help. Checked the Display Settings, he had 24-bit 1024x768. Tried 640x480, didn't help. Try 16-bit color?

Tuesday, January 03, 2006


Using POINTF (e.g. for Pucker) saves 8 bytes in the RING struct, which reduces the disk throughput requirements for snapshot movies. It doesn't seem to measureably worsen CPU usage. The downside is trading 15 digits of precision for 7. For items that remain constant over the life of a ring (e.g. Pucker), it might not matter, but for items that are incremented per frame (e.g. origin), it definitely *does* matter.