Saturday, June 10, 2017

sdlvnc redux

This time I was really planning to make something useful out of the fgui code and prove to myself that it was good enough for whatever.  But... once again something else came up that captured my attention and I lost focus.  Someday I swear I'll finish.  Just not today.

So what happened was this.  I was minding my own business, lurking on the zipit IRC channel, when someone mentioned how he wanted X11 on the zipit to serve as the remote display for some other gizmo.  And that got me thinking...  Sounds familiar?  Yeah I know, it's the same story every time.  Oh well.  I just roll with it and see where it takes me.  This time it took me back to sdlvnc -- another project I never quite finished.  But maybe things would be different this time.

Anyhow since a modern X server is a bit heavy for the poor little zipit I figured let the remote gizmo do the rendering to a local headless X11vnc server.  Then you can run the less resource hungry sdlvnc client on the zipit if you want to see it and interact with it.  After all, if that other gizmo has the horsepower to run X clients, the it can probably run an instance of the X11vnc server too.   That's a much better use of the available resources.  Of course I've visited this particular problem before, but I never quite finished, and left some serious quick-N-dirty hackery in the sdlvnc zoom code.  Also I never made it  into a real openwrt package.  It was finally time to get serious about it.

First I got the old sdlvnc code working again by compiling it right on the bleeding edge openwrt zipit. In the process I fixed a few bugs with the connection setup code that I'd known about previously, but never bothered to fix.  It felt real good to finally get those out of the way.  Next I created an openwrt project for it and got a real sdlvnc openwrt package built.  That was also quite rewarding. In the process I also fixed the ebindkeys mouse emulation code to make the zipit mouse tracking less jumpy, so there's a new package for that as well. And, since everything was moving right along, I thought I'd step it up and tackle the zoom code.

The problem here was that I'd done a quick paste of the sdl_gfx zoomSurface function into the sdlvnc code the first time around.  That function is really intended for rescaling small sprites and images, and not so much for working with the entire screen.  It worked, but the performance was awful.  That's  mostly because the linear interpolation code is only written for 32 bits per pixel surfaces, so the 16 bpp vnc screen surface had to be converted, zoomed, and then converted back for every refresh.  All of those surface copies were created and destroyed on the fly by zoomSurface, for every screen refresh.  Yikes!  That's a lot of memory, and a lot of copying. It was wasting a third of a second per refresh for a 640x480 screen on the vnc server, and was even more excruciating for bigger screens, when it didn't crash for lack of memory.  So, I decided to add a 16 bpp path through the sdl_gfx zoomSurface function to eliminate all those wasteful copies.

I scribbled lots of notes and pieced it together a tiny bit at a time in the evenings, starting from a copy of the 32 bpp zoomSurfaceRGBA subfunction in sdl_gfx. Surprisingly, two or three weeks later when I tried it on a whim, it actually worked.   That never happens.  So for the next few days I cleaned it up and ran some timing tests.  The 16bit path was running in about one third the time of the 32 bit path -- a tenth of a second or so to zoom the 640x480 screen.  Much better, but I was hoping for more.  What to do next?  I was pretty sure small constant array offsets were essentially free in modern instruction sets, whereas maintaining an extra set of nearly identical pointers was not.  So I worked up some inner loop optimizations and squeezed another 12% speed gain out of it.  I also discovered that the code runs much faster the more you zoom out because it skips over more of the inner loops.  That's a nice bonus.  So converting 640x480 to 320x240 only takes about 0.03 seconds.  I can live with that, so I think I'll call this one finished.  Here's what it looks like.

I love the way the mouse pointer also gets scaled in zoom mode.

It's almost useful at that scale.  But now you can zoom in and out pretty much at will, so that's not such a problem.

Now I just need to find some new project that'd be great to have on the zipit, if only the display fit in the 320x240 pixel zipit screen.  I'm sure I passed up a few of those over the years...

Saturday, April 1, 2017

April Fooling

It's April 1st, and the weather gods have once again played a nasty little joke, dumping a chilly mix of snow and ice onto the lawn.  So I decided to make the best of it, staying inside and tinkering with some zipit software that's just barely past the idea phase.

One such idea that I keep revisiting is how to make the gmenu2x GUI more versatile.  So this time I thought, perhaps I can do it from outside the box.  Maybe I can make some scripted modal dialogs that could launch from gmenu2x, but run outside of gmenu2x and simply display over it temporarily.  Or maybe we could find a use for popup notifications that post themselves over gmenu2x temporarily.

So I grabbed a few small example programs that open /dev/fb0 and write directly to the screen.  And that's where I am right now.  I can save the gmenu2x screen, write over part of it, and then restore it when I'm done.  Not too exciting, but lotsa potential, especially if I pull in the fgui code for some nice looking widgets.  Fgui makes for a nice fit because it's only requirements are a pair of get and put_pixel functions, so it's easy to graft onto any sort of graphical backend.

With any luck, I'll have an actual dialog screenshot before the snow melts.

Update April 2

And there it is...
Check out that shiny new icon for the ziptuner.  Pretty sweet eh?

Ok, it's not an actual dialog, but it is a small chunk of the fgui demo program -- reworked with /dev/fb0 as the backend instead of the original sdl backend.   The new backend  lets me pop it up over the gmenu2x display and tinker with the controls.  There may still be a few patches of snow left in the shadows beneath the trees, so I think I'll call this a success.

It'd be even better if it used the /dev/fb1 overlay screen so I could display the popup with no worries about contention with gmenu2x screen updates.  Unfortunately there appears to be some bugs in the PXA video driver that make the overlay less than optimal for my purposes.    Perhaps I can do a workaround someday for the overlay, but first I want to try and make what I've got do something useful.

Tuesday, March 14, 2017

Back in the Groove

Um... yeah, it's been a while since I wrote much of anything around here. For now I'll simply blame writers block.  I've got lots to say but just haven't found the motivation to say it.  Spending more time doing than talking about it, I suppose.  So, in order to keep this particular train rolling, I selected an encouraging title in the hope that I could somehow get "Back on Track".  Unfortunately I already used that title -- this must be a recurring problem -- so I tweaked it a little.  I hope it works.

To get things started I'm just gonna post a picture or two of my current pet project, the ziptuner -- a tiny internet radio browser program for the zipit.  For just about forever I've wanted something to make it easier to acquire, try, and save internet radio station urls for my playlists on the zipit.  This is what I eventually came up with.  It's uses the nice clean json API.

Here's the main screen.

Actually there's a few more choices now, but I'm too lazy to update the picture.

And here's the results of a Tag search for "disco".

Only 43 disco stations on the internet?  I find that hard to believe.

I'm truly impressed that someone -- more than one someone, actually -- found the need to devote a full 320Kbps to stream disco to the teeming masses on the internet.

Anyhow, that's it for now because it's still more fun to work on a project than to write about it.  So I'll try to revisit this page again when I run out of ideas.

Meanwhile openwrt packages are available in the usual place.

And here's an iz2s build:

Saturday, September 24, 2016

End of the Road

I think I'm finally gonna wrap up the never ending thread here about the zipit GPS.  Last time I tinkered with it was well over 6 months ago when I rearranged the pins in the zipit end of a Dell Axim cable.  I got a brief respite from lawn duties this summer when both the lawn and the garden shriveled up from the summer drought, leaving me free to play with the zipit for almost an entire weekend.  So I twisted the three wires together on the GPS end of the Axim cable and taped it up.

That's some crafty workmanship there.

I had to break out the kids optic / survival toy again so I could double up the lenses and see what I was doing.  I love that thing.  I swear one of these days I'm getting something just like it for myself, except with a light and a way to strap it on my head.

Yeah, I know.  Who needs a GPS when you got one of these beauties.

Surprisingly, the serial GPS actually worked right away.  Well sorta.  Apparently you have to flip the thing so the antenna side faces up.  That took me much, much longer than you might imagine to figure out.  And it still bites me about every other time I try to use it.  What are the odds of that?  Anyhow, I spent some time off and on over the next few weeks tinkering with the software and made my NMEA parsing code in pspmaps somewhat more robust before taking it out in the car for a road trip to Boston.  I had a brief moment of panic when I laid the GPS on the dash, antenna side down, and the map refused to move.  Darn it.
Antenna side up works better.

Here it is tracking one of those seemingly endless miles we covered on the highway.  You probably can't see it too well due to my shoddy camera work, but the route is mapped out in a blue overlay.

Beware.  Shaky-cam gives me a headache.

Sadly, this is the wife's car so the background music is provided by "Boston's favorite radio station", and not my awesome collection on the zipit.

This GPS runs off the +3.3V line exposed by the zipit expansion connector, so it works just fine on battery power only.  That's a distinct advantage over the USB GPS.  And now that the weather's cooled off a bit, my wife has started agitating to do some local hiking.  So I'm planning to take the zipit along to see how far its ancient battery will go with the GPS active.  Hopefully we won't get lost.

Another advantage the serial GPS over the USB GPS is that it works on an IZ2S SD card with the stock zipit kernel and bootloader, so no flashing is required.  Just pop in a mini SD card loaded up with IZ2S and boot.  No hardware mods are needed either, other than slicing up the Axim cable.  So I left the scary soldering gun in the closet.  You could conceivably do this with an off the shelf zipit, if you could find such a thing, and a mini SD card loaded up with IS2S.  I've got the new pspmaps installed now on the old zipit with rockbox that I keep in the car at all times for music that's never been heard on a ClearChannel playlist.

For the code I reworked my built-in NMEA serial polling module in pspmaps, so now it's configurable by editing a very simple gps.dat file in the pspmaps/data directory.  And it's also now robust enough to handle multiple GPS disconnect / reconnect events.  It'll still try to use gpsd if it finds the gpsdclient shared library, but I wouldn't recommend that unless you really need to multiplex the GPS data to more than one client program for some reason.  The pspmaps sources are updated on github.  And it's also bundled up in a package for the bleeding edge openwrt zipit linux distribution.

Last but not least, I wrangled some example code from the internet into a script so I can quickly set the system clock on the zipit from the GPS.  That might be handy if I were to go on an evening hike away from the wifi and just happened to lose my watch and phone, but not the zipit and GPS dongle.  :-)

Here's the script.  It also saves the fix location to a file in /tmp.  Maybe someday I'll add an option to the nightsky program to look for that file.

Here's a new IZ2S pspmaps executable. 

This is the new "bleeding edge openwrt" pspmaps package


I also have an executable for the older openwrt distribution (built on uclibc instead of musl) but I have to track it down and maybe make a package.  I'm pretty sure I posted a link to it, so you can search for it in the zipit IRC logs.  Or just make the move to the bleeding edge openwrt like everyone else.

Update October 1, 2016:

Did I say "end of the road"?  Oops.  No sooner had the words hit the internet when I was reminded on the zipit IRC channel about zgps, my specially resized version of cgps for the small screen.  So I bumped the patch to match the newer gpsd in the openwrt zipit bleeding edge repository and compiled it.   Mozzwald ran a build, so the tweaked cgps should now be in the gpsd-clients package, and I stashed an executable here for just in case.


I was also reminded that I left my Navit port in limbo, only lightly tested and full of potential.  So I dusted it off and fetched a new chunk of map to test it with.  It's still sorta slow, but apparently works better on the zipit than I remembered.  Here's a screen shot advertising the many varied attractions of downtown Springfield.
Plenty of parking, but watch out for that nuclear waste dump.

Is that a soccer ball?  Anyhow, now I'm gonna have to hook this up to the GPS and see what it can do...

Update October 12, 2016:

A few heavy duty road tests later, I can honestly say the performance of navit is not too shabby on the zipit.  I tested the IZ2S build first and had no real complaints.  Maybe the 3D view was a teensy bit laggy at highway speeds, but I can't really say, because my eyes were mostly glued to the road.   Really, I swear!  Safety first...  Next I created a build for the zipit openwrt bleeding edge and tried that out.  No complaints there either, so I made a package and hooked up a gmenu2x launcher for it.

Starting navit from the GUI is so much easier than a command line launch.

The openwrt package is in the usual place.

I made a new IZ2S package as well, refreshed to the newer source code that I used for the openwrt build. Since I don't use the locales on IZ2S, and they take up a boatload of space, I broke them out into a separate zip file.  The navit package also requires glib if you don't already have it.

Next, I'm considering borrowing the pspmaps google route fetching code to make navit textfile maps so you can get a route via wifi and then hit the road with navit.  And then if I feel really ambitious I might just try to make it speak.

Update November 31, 2016:

I made some progress on a small pspmaps to navit route converter app for gmenu2x.  You can find a link to it in the IRC logs, but I have to redo it somewhat to work  better with the openwrt flavor of gmenu2x.

Meanwhile mozzwald took the navit package, enabled lots of stuff and took some much cleaner videos than mine.   No shakey-cam.  Since he hasn't blogged about it yet himself, I feel compelled to show one here for now.  It's got the 3D display, OSD elements, Flite text to speech, and on the fly routing enabled. 

Altogether it's a bit of a jumbled mess, but some of it's actually useful in limited situations.  Apparently active routing actually works on short trips. I was a bit surprised by that.  Must be the zram that makes that possible.

Sunday, May 1, 2016

On the Edge

While I was off tinkering with my lego toys, the ever industrious folks on the zipit irc channel got together and started assembling a new set of software building blocks for the zipit.  We got a new modern kernel, a new build of uboot, and an updated openwrt distribution with musl instead of the decrepit old uclibc.  Mozzwald gathered it up, cobbled it all together and christened it "bleeding edge".

Now I'm not much of an early adopter.  In fact, I'm really much more of a time biding trailing lurker.  When I finally jump in and get busy, it's more than likely the project is on life support with one foot in the grave.  But mozzwald made a point of suggesting, more than once, that I could probably pinch in and help test some of the new bleeding edge stuff.  Eventually I got the message.

One feature of the new kernel is improved handling of the sd cards.  We've had some trouble in the past on the zipit with corrupted sd cards.  Many cards would give errors reading, writing, or both.  The problem didn't seem too bad way back in the olden days with the stock zipit software, and that's one of the reasons I've stuck with the iz2s distribution.  It's one giant kludge on top of another, but it does most of what I want from the zipit and doesn't munge the sd card. 

Now over the years I've built up a collection of assorted size sd cards loaded up with various flavors of the different zipit distributions.  I have some trouble keeping track of what's on all the sd cards because you can't attach a large label sticker to the teensy bits of plastic like you could with the sizable floppy disks of days gone by.  So I try to keep them in their little plastic enclosures along with a small scrap of paper suggesting what might actually be on them.  It's not a good system, things occasionally get into the wrong enclosure with the wrong scrap of paper, but it's all me. 

Anyhow, a few of the sd enclosures have paper scraps that say things like "reads on zipit, but writes fail".  So I cracked open one of those, loaded it up with mozzwalds bleeding edge openwrt distribution, and stuck it in an unsuspecting zipit.  It booted up nice and pretty in a somewhat minimalist gmenu2x setup.  So I wrote a few test files in the nano text editor, saved them and rebooted.  As expected, the test files were gone and the sd was corrupted.  Fortunately mozzwald fitted the bleeding edge openwrt with an alternate "sdfix" uboot script that starts the new kernel up with the new slower, but safer, sd card setting.  I made the switch, rebooted the zipit and tried again.

   mv /boot/uboot.script /boot/uboot.script.bak
   cp /boot/uboot.script.sdfix /boot/uboot.script

This time it was good.  My test files survived several reboots.  It also remembered my wifi settings and kept a few updated packages I installed.  Nice!

It's minimal and rough around the edges, but it sure is shiny.

Naturally I wanted more.  Nano is ok as minimal text editors go, but I prefer something that understands the emacs keystokes that my fingers know by heart.  I've got a fork of the qemacs source code on github that does the trick but I don't have the disk space right now to install another full openwrt build setup in order to compile it.  So I went where I usually go for a self contained native arm compiler to do it on the zipit, the aboriginal linux goodies over at  The latest arm compilers there have switched over to targeting the musl libc instead of uclibc.   That's perfect for bleeding-edge since openwrt also made that switch.  So it's just what I needed. 

I unzipped the aboriginal armv5l compiler into /usr/share on the bleeding edge sd card, renamed the long-winded directory it created to /usr/share/gcc, and added the /usr/share/gcc/usr/bin directory to my PATH.  Then I tried to compile a simple hello world program with the gcc -o hello hello.c command.  All went well until I tried to run it.  I got some weird "can't find the file to run" error.  It turns out the aboriginal compiler targets a slightly differently name for the musl dynamic linker so the hello program couldn't find it.  It wanted /lib/ instead of /lib/  That's not so bad.  You can fix that up with a soft link in /lib, but I wanted to compile programs that'd run right out out of the box so I went with a gcc command line option instead.  Like this:

  gcc -Wl,-dynamic-linker,/lib/ -o hello hello.c

It worked.  And that's ok for small things, but qemacs uses a configure script, so it's more difficult to add that switch to all the various Makefiles it creates.  So I started researching how to do it with a specs file so gcc would default to the right dynamic linker target, invisibly.  Also, I got mozzwald to zip up a tarball with all the includes and libs from his bleeding edge openwrt cross compiler setup so I could use them to build more interesting things with the native compiler on the zipit.  That required me to put the libs and includes in an odd location so they wouldn't interfere with the files installed by the package manager.  I unzipped the openwrt-libs-includes_bleeding-edge.tar.bz2 into /usr/share/gcc/ and renamed the longish bleeding edge directory name to /usr/share/gcc/wrt so it'd be easier to type.  Then I attempted to make it seamless.

First I fished a static linked arm4l strace executable from the extras folder in the old binaries download area at and ran:

  strace gcc

Then I sifted through the text it printed out to see where gcc was looking for the specs file.  I discovered one place it was looking was in /usr/share/gcc/usr/armv5l-unknown-linux-gnueabi/lib/gcc/ so I went there (I actually had to make the directory since it didn't exist yet) and told gcc to dump its built in specs:

  gcc -dumpspecs >specs

I edited that specs file (still using nano) and fixed the dynamic linker target.  This was a little tricky because all the hints on the internet said to do it in a different place than where I eventually  made a change that did work.  I suspected the link_libgcc setting, which appeared after the recommended change in the link_command, was overriding it with another -dynamic_linker setting.  Running gcc -v helped me move my preferred musl dynamic linker setting around and see when I got it to be the last one used, with the final say.  Once this was working, I also added some -I and -L paths to make it search in the /usr/share/gcc/wrt directories for includes and libs.

When I finished, it all seemed to behave just like you'd expect.  Seamless.  Well, at least I was able to build a nice qemacs executable that runs on the bleeding edge openwrt.  I also tested a tiny ncurses example.  Then I built fbgrab, a small framebuffer screen grabber program so I can produce some pretty pictures like the one above.  I didn't see it in the bleeding edge package repository so I figured maybe the bleeding edge Makefile possibly needed a tweak.  Later on I realized it was actually there, but in the separate zipit section of the package repo.  Oh well.

I also built some small real world programs -- nsudoku to test ncurses and fgui for SDL. The ncurses program was easy, but it revealed the default bleeding edge TERM=vt102 setting was inadequate for colored text.  TERM=linux worked better...  The SDL program required me to tweak the sdl-config script in the wrt/usr/bin directory to switch the cross compiler paths to the appropriate paths on the zipit itself.  No surprise there.

Here's the specs file.

And here's the qemacs executable.


Update 6/2/2016

Lately I've been fiddling with tinyfiledialogs for my lego project, but I took a quick break to compile tinyirc for the bleeding edge openwrt.  So here it is, source and executable.


Ok, back to tinyfiledialogs.  I swear I'm gonna get it working on OSX 10.3 panther if it's the last thing I do.  Gotta get my money's worth outta my ancient Mac IceBook before it dies...

Wednesday, February 17, 2016

Timeout for toys.

It turns out I serviced another interrupt from the zipit gps around the same time as the burgerspace work -- another case of idle hands getting into things.  I got the urge to check out the latest postings in the forum.  I haven't done anything there in years, but every so often I like to visit and see what's new.   How did it happen this time?  Well, for some reason the movie Frozen really appeals to my wife.  I don't get it myself, but I do like legos and they introduced some new lego sets this Christmas featuring characters and scenes from the Frozen movie.  So I got them for us to work on together.  As it turned out, we were assembling the sets around the same time I was working on burgerspace.  This turned out to be excellent timing, and I'll get to why in just a bit.

Anyhow, building with legos always makes me think about LDRAW, and so I paid a visit to the forum.  There were all sorts of new projects under development there, but one in particular caught my eye.  LPub3D is a new version of LPub that appears to incorporate leocad into the right side panel, I assume to assist in positioning the lego models live, as you're preparing the building instructions for publishing.  The latest release announcement mentioned some updates to the ldglite code, which is used as the quick and dirty low end rendering option.

What's that?  New ldglite code?  And there were bug reports too, complaining about the ldglite image quality and inaccuracies compared to LDView.  Wow.  My ldglite sourceforge repository's been mostly inactive for like 4 years now.  I had to get involved in this before people lost interest and moved on.  Sorry zipit gps, we'll get back to you later...

So I requested a copy of the ldglite code updates, dug an old pentium 3 Windows laptop out of cold storage (the garbage) and got to work installing ldraw programs and reconnecting with my sourceforge archives.  The new code adds support for more ldraw search directories, where I'd fallen far behind in support of the current LDRAW standards.  So I merged it into the CVS repository.  The new stuff pulls in some C++ code from LPub3D and adds it to the l3 parsing code, so I ended up splitting the l3 code into a C version and a C++ version  That way I can still build things with just my C compiler.  Eventually I'll try to duplicate the directory search code in C because I want it too.

Then I got to work on the bug reports.  I tried to duplicate some of the problems in LPub3D with the car.dat example model that comes free with the part files.  Here's what I did. 

First open the example car.dat model and go to the last step (step 8):
Then configure the preferred renderer in lpub3d preferences to LDView.
Do an alt-printscreen and paste into an mspaint window.

LDView looks pretty good.

Repeat configured for ldglite rendering and paste into the same mspaint window.
Then toggle back and forth with control-Z and control-Y to see the differences.
Ldglite is clearly the low quality image winner.  Darn it.

It looks like the same viewing angle, but the ldglite part images are slightly bigger.  The size difference is quite subtle.  It could be just different edge line width options, but I suspect it's a little more than that.

Now take a look at Step 3 of the instructions.  This shows how subtle differences in the part images can lead to a different layout of the parts inset box.
Here's step 3 using LDView.

And here's step 3 with ldglite.  The parts inset box is different.

You might have noticed the lighting is also different.  Both LDView and ldglite have some ambient light and a spotlight, but ldglite defaults to placing the spotlight at (x,y,z) = (-1000,1000,1000), probably for compatibility with the original LDRAW.EXE program, or the old LDLite.exe viewer.  The default places the spotlight above the viewer and off to the left.  Adding -lc0,1000,1000 on the command line makes it more like LDView default lighting which is above and directly behind the viewer.  But I didn't see anywhere that I could set this in LPub3D.  I thought I remembered a place in the old LPub for extra settings, once upon a time.  That'd make it much easier to test things.

Meanwhile, I could see where LPub3D keeps its work in the ldraw models directory.  It'd be nice if it had a debug setting to keep a running log there with all of the commands it sends to the external rendering programs.  However, the source code patch came with an example ldglite command line, so at least I had that to work with.  For rendering, LPpub3D spins the parts (or the model) before calling ldglite with command line settings to view it from really far away with a small view angle in order to simulate an orthographic view with a perspective view matrix.  My notes in the ldglite code suggest this is because the POV ray tracer doesn't, or didn't, support orthographic projections.  LDView might not either.  Examining the render.cpp code in LPub3D appears to confirm this.

Anyhow, I made a model with just one red 4x4 plate to see what differences other than the light source I could spot between LDView and ldglite.

The plate in LDView

The plate in ldglite
The plate in l3p/POV

I pasted the LDView red plate image over the ldglite image and zoomed in.

I think it's pretty clear that ldglite is being told to render on a white background with thicker anti-aliased edge lines.  You can't readily tell if it's a transparent png in mspaint, but viewing them here shows both images have transparency enabled for the background.  The edge line widths could add up to a pixel or 3 difference in size, but we see more than that, and it's proportional to the shape of the box, so it's a slightly different view.  It could simply be that ldglite uses the origin point (0,0,0) in the model as the look at point and LDView looks towards the center of the bounding sphere.   I'll have to experiment with this to get a better match in the generated images.

For now, I attempted to fix some of the easy stuff.  And this is where burgerspace plays a part.  The readme.txt file for ldglite tells how to scale up by 2 and dump an image that can be post processed by another program to downsample with antialiasing for a more pleasing appearance.  Once upon a time the ldraw parts tracker did just that.  But fresh from my success with burgerspace, I figured I had everything I needed to add a simple average and decimate by 2 output filter right into the ldglite code.  So I did, and ran it through LPub3D -- with hard coded ldglite settings for now -- to see how it stacked up.  It's still not LDView quality, but I think it's a vast improvement.  Perhaps medium-low quality.

Here's step 8 with the 2x AA Filter in ldglite.  I also moved the light source.

Here's step 3 in the improved ldglite.  The inset is more like LDView now.

Not too shabby.  The code is in CVS on the sourceforge, but needs some cleanup.  I'm thinking of trying a Gaussian blur filter, possibly with gamma correction, and maybe 3x oversampling and decimation.  Why not?

Meanwhile I did some math on the look at point and I don't see how it makes much of a difference if the origin is on the corner of the plate or in the center at the great distance we're viewing from.  Here's the setup used by LPub3d.  What is that, one light year off in the Z direction?

        -ca0.01 -cg0.0,0.0,3116890

The camera viewing (FOV?) angle is a teensy 0.01 (degrees?) so a tiny change to that number might be enough to create the difference in the plate images.  Perhaps I'm using floats instead of double precision somewhere, leading to larger noticable errors in the calculations.  More investigation is needed.  I hope LDView keeps a log of the commands it executes.   Then I can experiment directly in ldglite and LDView without even loading LPub3D.  That'd make this whole process go more quickly.

Anyhow, after looking at the inconsistent treatment of edge lines that fell evenly on the 2x2 pixel samples versus the ones that straddled the 2x2s, I decided the gaussian blur filter was a must.  The quick and dirty 2x2 was good enough for burgerspace, but this is important.  Appearance actually counts.   So I coded it up and replaced the 2x2 filter.

Here's a 2x scaled image of the car (-S2 -w1).  No downsampling, plenty of jaggies. 

Here's the same car scaled up by 4 (-s4 -w2), then decimated by 2 with the blur filter.

Overall the quality of the blur filtered image is better, and more consistent around the edge lines than it was with the 2x2 averaging filter.  There are still some issues with the overall image quality from ldglite.  You can see the blended edge lines on the black surfaces appear darker in the blur filtered image.  I'm gonna need to gamma correct before the blur and then reverse the gamma afterwards to make those grey on black edges pop.  The mtPaint Handbook suggests I should read "ITU-R recommendation 709" and implement that.  Ok, will do.
Here's the same thing but with 8/12 bit lookup table gamma corrected blurring.

Hmm, the gamma correction seems to have had more of an impact on the transparent parts than the edge lines.  I'm not sure if I like it.  Something seems fishy here.  Look at the faded colors of the head and tail lights where they sit over the background color.  For a while I thought I might have to gamma correct the alpha channel, but then I rendered one with with a black background and realized the problem.  The transparent parts that appear over the background color are blended with the alpha channel and the background color, which makes them too white if the background is white.  You can see the same effect in the grey edge lines that border the background.  The image must be rendered on a black background for best results with a transparent png.  Yeah, that's what I was trying to remember from years ago.  You can use either -b0x2000000 to set the background to absolute black, or use -b0 to go with the very dark ldraw color number zero.
I rendered one on a black background, and also moved the lighting to head on.

Anyhow, about those transparent parts...  Transparent parts are handled quite differently by ldglite than in LDView or POVray.  I imagine the POV ray tracer takes on a hint of the translucent color whenever a ray is cast through a translucent object.  So multiple passes through the glass will add more of the glass color.  LDView probably (but I'm not positive about this) applies a hint of the translucent color for each surface of of translucent object between the viewer and the background.  So you take a hit from both the front and backside of the glass.  Ldglite simply applies one stipple mask for any amount of translucent stuff between the viewer and the background.  Zoom in on the non-downsampled ldglite images and you can see the stipple pattern.  Downsampling blends the stipple pattern into whatever happens to lurk behind it.  This makes it pretty, but you still only get one hit of glass color -- from the glass nearest to you, the viewer -- no matter how many translucent things you're looking through.  I'm actually ok with this approach even though it's less realistic, because it lets you see better what's behind the glass.  Some might even say that makes for easier to follow assembly instructions.  And it's sorta retro, like me.  See?
Here's a nice link on gamma corrected image scaling.  Fun reading.

I've quantified the difference in scale between LDView and ldglite.  Adjusting the camera angle setting from -ca0.01 to -ca0.01021 is enough to make the plate images match up with the LDView plate image.  I'm still not sure where the error creeps in, but I have my suspicions...

Size up this filtered ldglite image (with front lighting and -ca0.01021) to the others above.

Unfortunately the suspicions didn't pan out.  I currently have no idea what's different, or for that matter, which program is at fault.  I was hoping to compare both ldglite and LDView with some l3p/POV renders, but POV runs out of memory and fails on my junky old Windows laptop.  384 Megabytes sure as heck don't go as far as they once did.  Maybe there's some lower quality reduced memory settings I could use, but I'll have to read up some POV documentation to figure that out. 

In the meantime I double checked the -ca0.01021 ldglite setting with the car model instead of just the plate.  The wider camera angle worked the same magic on the car, so at least the error is consistent.  Click on these images and compare the sizes yourself.  It's a pretty good match.
This car image was produced by LDView in LPub3

This car image was produced by ldglite (with hard coded settings) in LPub3.

I probably should add a note at this point that I've done nothing to the LDView settings since I started testing, simply because I'm more interested in improving the abysmal quality of the ldglite pictures.  So I'm certain these LDView images are not even close to representative of what it does with the proper settings selected for LPub.

Update February 28 2016:

Real life stepped in and forced me to back away from this for a bit.  So when I got back to it I started up on a different tack.  Examining the LPub3D render.cpp code I can see that all of the rendering options appear to use the exact same command line camera angle setting.  But the LDView camera distance setup code has a bunch of fudge factors in it.  First it multiplies the scale by 0.775 inside the LDView::cameraDistance function, and then whenever it uses this function it multiplies again by 1700/1000.  What is that?!?  Some sort of aspect ratio adjustment, or is it really two different fudges?  The cameraDistance function is only used in render.cpp so there's absolutely no good reason to fudge it in two different spots.  It must be some kind of subterfuge because there's also a hideously complex table of ratios in the comments right before the LDView::cameraDistance function that I cannot make heads or tails of.  I'd swear it's just put there as a distraction.  But if I had to make a bet, I'd say this is where the ldglite and LDView rendering options diverge.   Perhaps we need to add tiny nudge to the final fudge factor to make it all good?

I had to know.  So I loaded all the programs on a newer Windows laptop that I've got with more memory, but with a busted fan unit.   I let it rest on an ice pack fresh from the freezer and did a POV render, just hoping to finish rendering the plate image before the CPU burned up.   The laptop fan squealed an agonizing screech of death, but POV did whatever it does without a popup error message.  Nothing appeared in the LPub3D window (possibly a setup issue that I don't know about) but I rummaged around in the ldraw/models/LPub3D directories and found the image.  Now I can compare the output image sizes of all 3 rendering options and figure out where to go next.

The ray traced plate image from l3p/POV is pretty, but I find all the shadows distracting.

I had to clip the POV output image to compare it to the others.  I also added a picture of it up higher in the blog to make it easier to compare plates in the image viewer here, but I think it shows ldglite and l3p/POV are using the same view setup.  And once again the LDView rendered plate is smaller.

I pasted the LDView red plate image over the l3p/POV image and zoomed in.

So what's the story with the fudge factors wrapped around the LDView camera distance calculations?  I suspect they might just be cruft left over from a work around developed for an earlier version of LDView that perhaps only provided the -FOV command line option instead of the -ca option used by l3p and ldglite.  Or maybe the LDView -ca option is only sorta compatible.  But the truth is I don't really know, and that scary table of ratios in the LPub3D source code comments makes me nervous.

Oddly enough, when I visited the old LPub4 source repository I saw the comment for revision 1.10 said "Adjusted LDView scale to match LDGLite". Now that sounds pretty good.  In fact, it could be just what the doctor ordered.  So I took a peek at the code and discovered the gawdawful table of ratios was apparently added in that revision, and so were the fudge factors.   But the factors were different from those present in the LPub3D code.   When I multiply the old fudge factors together I get a number very close to 1, so they used to cancel each other out. That's weird!  Why bother?   Makes me think we should maybe just try to remove them in LPub3D.

Meanwhile, I think LDrawModelViewer.cpp is the LDView code I want to examine.  There's a few things I don't understand, like why does the getHFov function calculate hfov from fov if we gave it hfov in -ca on the command line? And why do we need all that aspect ratio stuff anyhow if we only care about the horizontal FOV?  All that business with the tiled rendering makes it even harder to follow.  The complexity of this code makes me feel inadequate and uneducated.  But if I had to guess, I'd say maybe -ca doesn't work quite right in LDView when the window width is less than the height.  I bet it'd all work just fine if we told it to render on a big square surface instead of a rectangle.  An aspect ratio of 1 can't do any harm, and all the fudge factors should simply disappear.

However, I guessed wrong.  I removed both fudge factors and with the same width, height, -ca, and -cg command line arguments LDView renders the exact same scene as ldglite and l3p/POV.  That should make for an easy fix in LPub3D, just use the same cameraDistance function for everything.

And since we're making things easy, I decided to restore the -s ldglite command line option to it's original purpose.  Then I added a new -2x,2g command line option to scale the image and edge lines up by 2 and also decimate by 2 with the Gaussian blur filter for the png output file(s).  For offscreen renders it doubles the size of the viewport as well, to ensure you don't lose anything outside the frame.  That should make things easy.  If you just want to decimate, use -2g alone.  If you only want to scale up, use -2x alone.  I did it this way because someday maybe I'll add more more scale factors, or filter options, all separated by commas.

Update March 5 2016:

At this point, I believe I've got solutions to all of the reported flaws concerning rendering with ldglite in LPub3D.  So I've moved on to assimilating the new ldglite code from the LPub3D patch.  It looks like I can simply incorporate the C code from the LDrawIni sourceforge repository with a few tiny modifications to make it compatible with the LPub3D patch.  No need to pull in all of QT.  That's a relief.  I really should've done this 12 years ago when I was actively engaged in the LDrawIni discussion on lugnet.  BTW, as far as I know that's the most up to date (only existing) documentation on how to use it, so I could've jumped on it right away instead of waiting until now.

These are the changes to the LDrawIni code that I've spotted so far.

- The filenames are lowercased.  Nice.  Bumpy case is pinky abuse, like emacs.

- The ldrawini.c file adds the base Unofficial directory to the default list.  ok...

- It uses an LDSEARCHDIRS environment variable instead of LDRAWSEARCH.

- The additional LDRAWDIRSEARCH01-99 environment variables are skipped.

For a while I also thought the L3PInput.cpp code was changed to reverse the search order of the unofficial parts and primitives from the order used for official files, and from LDrawIni default order.  I couldn't imagine why you'd want to do that, and it turns out some string compares were swapped in the code, but the search order was not actually changed.  That's a relief.

I'm still unsure why the new LDSEARCHDIRS environment variable was created.  I'd think the original environment variables should've sufficed, especially if LPub3D is already making use of the built in LDrawIni support with LDView and L3P.  But after further study, I see there's more to it.  It looks like the LPub3D search strategy makes use of the LDrawIni codebase, but takes some shortcuts on the LDrawIni approach.  The directories in the new LDSEARCHDIRS environment variable are used to supplement the baseline search directories, and might possibly include their own P, Parts, and Models directories without explicitly spelling it all out like in LDrawIni.  That would make LDSEARCHDIRS more of a variant on the LDRAWSEARCH01 through LDRAWSEARCH99 settings.

I guess maybe I'll have to support both strategies?  Thats a bit more work, but I think it's still feasible. I'm still checking, but hopefully that's it.  Once I'm certain I'll add some modified LDrawIni code to the ldglite project and call it a day.

Update March 20 2016:

Heh, I haven't actually made all that much progress on the LDrawIni stuff.  I discovered the existing "documentation" no longer matches the current API in the real LDrawIni code.  Darn it!  There's plenty of comments in the code itself --it's self documenting -- but no simple cut and paste example code that I can leverage.  I got confirmation that LDSEARCHDIRS code from LPub3D is in fact a modified/simplified variant of the original LDrawIni code.  So my plan is still good.  I think I'm just simply having trouble motivating myself because there are more interesting problems to solve.

It's mostly with the linux build.  I got an email telling me that I'd broken the linux build on some of the major distros with namespace collisions in the new stuff.  So I chose some different names and did some testing on my main LMDE linux PC.  It all compiled, but some of the new stuff didn't quite work as well on linux.  So I fixed a few bugs, enabled the Alpha transparency bits, and even tried linking to both the off screen osmesa driver and windowed opengl.  That always gave me problems (10 years ago) and so I got pretty excited to see it finally work, just like it always did on Windows.  It was pretty fast too.  As a result I decided to try and compile an older libOSMesa version 7.6 for the aging puppeee linux my ancient eeepc netbook to see if I could make it work there too.  And it did work.  I hope it's like that on other linux distros.

I stashed the osmesa config settings that I used for puppeee linux here, just in case.

Now I think I may have finally run out of fun excuses for avoiding the LDrawIni conversion.  Oh well, at least now I can experiment with it while lounging around the house with the tiny netbook.

Update March 26 2016:

Progress is slow, perhaps glacial, but things are moving.  I've started making test builds with progressively larger chunks of the ldrawini code enabled.  And I've already introduced my first hacks to the once pristine ldrawini.c file.  I had to lowercase some directory names (p, parts, and ldraw) and expose the previously static DirHasPandPARTS() function to make the basics work without having to install a painfully slow LDrawIniFileCaseCallback() function.  I refuse to go there.  The ldraw library should unpack in lowercase.  Linux users will just have to be responsible for matching mixed or upper case filenames and directories exactly if they choose to use them for any other files or directories on on case sensitive file systems.  I also added support for more sensible fallback directories under modern linux distributions.  So far things are working, but it's hard to fully test without more comprehensive documentation.  I did find some docs on the file format of the ldraw.ini file itself, mixed in with the l3p documentation, So now I've made a sample ldraw.ini file for linux to use for testing on my puppeee linux netbook.  I'll try a similar file on Windows.  Mac testing will have to wait a while.

But since things seemed to be working, I went ahead and made a small module in C to process the LDSEARCHDIR environment variable.  It simply tacks a few more search paths onto the end of the list from ldrawini.  I gave it a quick test on linux, then compiled it for Windows and substituted it for the ldglite 1.3.0 executable that was distributed with LPub3D.  I don't know for sure if LPub3D was exercising the LDSEARCHDIR code, but it was able to make instructions for the car model.  So just for fun I made another Windows ldglite with the blur filter, the black background, and the front lighting all enabled by default.  I substituted that one into LPub3D and regenerated the car instructions.  I think it ran a bit slower than the original ldglite on the ancient A30p laptop, but the resulting images were sharper.

Update April 9 2016:

I've still gotta run it myself on some less vintage hardware -- maybe something under 15 years old -- to see if there's any noticeable speed difference for more normal users.  But meanwhile I think I've tested it enough to call it a real release.  So I've updated the both the CVS and github repositories, and rolled out some release 1.3.1 executables for Windows on the ldglite homepage.  There's a normal release executable, and a special one with the -2g,2x option hardcoded for offscreen rendering so you can get the sharper looking images from ldglite with the current LPub3D release.  I've also tested this on OSX 10.4 Tiger.  So if I can remember how to make the dmg file then I should be able to roll out a new Mac release as well, although who knows if it'll run on a current Mac...

Here's a test file rendered offscreen with -2g,2x on my old PowerPC emac.

Then, just for fun, I fetched the Lpub4 sources from the ldraw-linux github repository, installed the required QT dev tools, and gave it a run.

   sudo apt-get install qt4-dev-tools libqt4-dev
   qmake -o Makefile

The resulting shiny new linux lpub complained about a missing parts.list file when it started up, so I tweaked the mklist.c sources from ldglite, built a mklist executable, and ran it in the ldraw directory to generate the missing file.  I think the ldraw-linux repo has a scripted version of mklist, but I went with what I already know...  Anyhow, I set the lpub4 config settings to use a special ldglite executable with -2g,2x hardwired, and got this nice screenshot.

Afterwards I felt like I was on a roll, so I made a Mac dmg file and tested the install on an old macbook the kids left behind with OSX 10.5 leopard.  It worked, but the dmg file could use some compression and autorun magic.  I forgot the secret dmg tricks, so I'll have to do some reading and tweak it up.  This time I'll save the dmg assembly instructions in the CVS repository...

Next I'll try and make a patch for lpub4 to pass the magic -2g,2x args to ldglite so you can use a vanilla ldglite v1.3.1 executable instead of the special one.

And that might be all for this project because I think I'm running outta steam.

Tuesday, February 16, 2016

Snack Attack

While awaiting the imminent arrival of my new zipit gps toy I decided to try and attempt to resurrect an ancient openwrt-zipit build setup that I'd installed long ago on a usb drive.  You know, just in case I needed to work some more magic on the openwrt-zipit gpsd code patches.  It actually went much better than I expected, really, so I figured maybe I could fix up some needy abandoned packages in my spare time, instead of twiddling my fingers.  Idle hands and all that...

One such needy package was the burgerspace game.  I found some minor makefile quirks that needed fixing to get it and it's companion game library libflatzebra to build.  So I patched them up and hoped for the best.  Nope.  Apparently nobody ever noticed it required a 672x520 SDL window.  How did that ever work on the nanonote?  I believe that's where the original openwrt package came from.  Anyhow, I verified it worked just fine at 640x480, so all we needed to do was cut everything in half for the 320x240 screen on the zipit.  So that's what I did.  I chopped out half the raster lines, and half of the pixels in the lines that were left.  It worked, but you couldn't read the instructions or the score.  That's no fun.

Fortunately I remembered some old raster tricks from days gone by and did a quick search for fast color pixel averaging.  Yeah, that's the trick I had in mind.  I overloaded the SDLflip function in libflatzebra, pasted in the code, and voila!  It works.
I love the sound it makes when chef gets bonked by the sausage.

You can read the score without too much trouble, and if you squint real hard you can almost make out the rest of the text.  Now I just need to find some more 640x480 SDL games to convert, quickly before I forget...

The packages in mozzwald's openwrt-zipit repository are up to date, so you can install it on an openwrt zipit with opkg.  I also made an IZ2S build.

* By the way, the title of this post is indeed a nod to perhaps one of the greatest record album covers, ever.  But I'm sure you knew that already.

Listening to "The Problem" or the last two minutes or so of "The Party" always brings back a flood of memories from weekends at engineering school.  Heh.  So long, Rick.