Tuesday, January 16, 2018

Long Time No Talk

I haven't done much worth writing about lately.  Or maybe I've just simply been too busy doing other things.  I don't know...  So I'm just gonna jot down a few brief notes for now to keep this zombie blog twitching.

On the zipit IRC we recently started tinkering with Devuan.  So far, it feels pretty similar to the old Arch distro from two years ago, except you have to mount the SD card on your PC in a qemu chroot to fetch packages.  That's not as difficult as it sounds, so it's not huge burden.  I noticed that several of the executables I built for Arch, like qemacs and gmu, actually work on Devuan as is.  Probably because they're both based on glibc.  Anyhow, that's handy.  But I'm still getting up to speed compiling things in the Devuan chroot.  So far I've built a working version of the ziptuner, but not much else.

Meanwhile there's a few new tidbits for the bleeding edge openwrt.  Here's a picture of the new wavemon package running on the zipit with the 5x8 font. 

The Fn keys shown on the botton don't exist on the zipit, but you can still get to the other screens by pressing the first letter of the screen name.  So the keyboard support seems good enough for me on the zipit.  However, a few strings in the messages could possibly be shortened to make it all fit.  Maybe I'll whip up a patch for that.  I'd also like to make a patch to build it with libnl-tiny to make it smaller, but that's not really a priority

What else is there?  About the only bits I remember are a small update to the ebindkeys package to make the stop button work with the default gmu shift-esc key combo.  Maybe I did some more work on the unfinished stppc puzzles package?

Part 2.  The Long Lost Holiday Special.

As you can see above, somehow despite a personal record, due to procrastination, of 2.5 weeks off for Christmas and another full week for Thanksgiving, I managed to accomplish next to nothing during the holiday season of 2017.  Well, maybe I got caught up on some classic science fiction reading from the 50's, 60's, and 70's.  But nothing worth mentioning here.  So instead I decided to relive some former glory and attempt to recollect the events that transpired near the end of 2016.  Around that time I had a really nice burst of energy and enthusiasm that has gone undocumented here for far too long.  I need to sort it out and finally start tidying up some of the loose ends before I forget it all completely.

 At the very least I'd like to rediscover my recipe for building an iwmmxt enhanced mplayer executable for openwrt.  I'm almost 99% certain that a zipit running bleeding edge openwrt was publicly demonstrated to play video streams (with proper lipsync) that were located with the links browser through mozzwalds zipit friendly youtube search portal, then fetched with movegrab and piped into an iwmmxt enhanced mplayer for a seamless streaming experience.  For now I might be happy if I could simply recover enough notes to make an iwmmxt howtobuild.txt file and check it into the files directory of the openwrt mplayer github project.  Unfortunately it turns out the whole sequence that culminated in that public demo involved more cheating and hackery than I care to remember.  The bleeding edge movgrab package was even more challenging to put together than the iwmmxt enhanced mplayer, but we'll get to that later.

Anyhow, I'd also like figure out what sort of loose ends I left when I created the openwrt rockbox package around the same time, so can pick up the slack and clean it up.  Right now I'm thinking the battery monitor code was unfinished and maybe I need to borrow some eq files from the iz2s build?  This could turn out to be really important to me.  Here's why.  After 10 years in the same old car I finally gave up and got a new one.  Apparently the new car can play music off a usb stick, or an iphone, or whatever else you might have via the aux input jack.  But the software is a total mess.  The USB interface only recognizes mp3 files and presents them on screen in a confusing mishmash of unrecognizable numbered folders.  I mean who in the world thought that listing random numbers for folder names instead of the *actual* folder names from the USB stick was a good idea?  At least the auto maker Honda was nice enough to include an aux port, so the zipit is still golden if I can keep it running for another 10 years or so.  By then I expect to have a robot valet to drive my flying car and manage my music collection.

So, here's the story.  It started in the run up to Christmas 2016, when I managed to put out a few updates to various zipit goodies, mostly for the bleeding edge openwrt.  For example, at some point I made a bleeding edge tinyirc executable.  It's handy for a zipit internal flash installation, if for some reason you can't get an SD card going.  There's better IRC clients in the bleeding edge package repo if you're running from an SD card. Than in May 2016 I finally fixed a rather annoying "feature" of qemacs that had been driving me nuts for some time.  Normally, in full up gnu emacs, whenever you type a close bracket (be it round, squiggly, or square) the electric parenthesis matching function flashes the cursor briefly over the matching opening bracket and then returns to your current position.  I love that feature when developing code.  But the qemacs version of this was failing miserably on the return.  You can probably imagine the confusion that causes when you're feverishly coding, only to realize the text you're typing is inserting itself into the wrong place, and has been for quite a while -- ever since you closed your last parenthesis.  It was especially horrifying pasting a large block of code only to see it crumple in upon itself in slow motion, creating a total jumbled mess of nonsense to clean up.  I finally had enough and patched the code and uploaded a bleeding edge executable.  Eventually I remembered to update the github repositories too, so the current bleeding edge openwrt package should also contain the fix.  I probably ought to upload a new iz2s executable, or maybe even make an update the old uz2s image from 2015, when I find some time.

In October I picked up speed heading into the holiday season and compiled a zgps executable for bleeding edge, and also updated the pspmaps and navit executables for iz2s. Toward the end of October we got a request for opus codec support, so I built libopus and gmu10 to make use of it for the bleeding edge  openwrt.  These might be real packages at this point, but I can't remember.

As the holidays approached I determined to get the video player integrated into bleeding edge openwrt, just like it was in IZ2S and the older openwrt system based on uClibc.  I wanted to browse for music videos on mozzwald's youtube search portal and have them play in realtime, right from the links browser.  We had a working mplayer package, right?  So I figured we just needed to fix up some musl compile issues in the movgrab package and add some configuration glue to the links package.  How naive...

After fixing some minor compile issues with musl header files, I discovered movgrab had a problem with the musl implementation of select or poll.  Been there, done that with gpsd and such.  So I rewrote a FDselect wrapper function in the movgrab libUseful code to add a timeout. otherwise it would block for something like forever when printing out the available video formats, and when fetching the actual video stream url.  Or so I thought... Eventually I got to thinking it was a socket reuse problem in musl, and not actually another problem with select.

Unfortunately I never solved it.  Instead I compiled a cheezy static version movgrab on a zipit with the old openwrt.  That got it built with uclibc instead of musl.  But I didn't have the ssl or crypto libs and include files on the zipit where I did the compiling, so instead I built a really bare bones movgrab.  What a mess, but it seemed to work, even on the bleeding edge zipit. If you look real close at the bleeding edge movegrab package files on github you might notice the static movgrab executable in the files directory.  When you build the package, it goes to all the trouble to compile a musl executable and then slips in the old static executable instead.  :)  Wow.  I am a total cheater.

Sadly it longer seems to work with youtube, probably due to the missing ssl or whatever restrictions fell out of the recent Amazon Google spat over access rights.  But maybe I can finally spend some quality time now and fix up the musl build the right way.  Or else try to compile an up to date static build in the devuan chroot we've been using lately.  Hmm...possibilities.

Anyhow next up was the links config glue.  However while testing various implementations it was revealed that the lipsync was off.  The sound would play at normal speed, but the video would gradually fall further and further behind.  Apparently it was like this in the iz2s and the old openwrt.  We were so close, so we twiddled with all the obscure mplayer mplayer runtime config settings, but it could NOT be convinced to coax it into sync.  So we had to go for some compile
optimization, the tricky iwmmxt code buried deep in the guts of mplayer.

At first I thought, naively again, that it was simply a matter of fixing the openwrt mplayer package makefile to add the mplayer iwmmxt config setting.  But no...  nothing is ever so simple with the zipit.  Openwrt insisted on adding -march=armv5te and -mcpu=armv5te to the compiler switches, overriding my iwmmxt setting.

So I made a script to do a swap in the makefile and then a rebuild.  That almost worked.  But -- and this is where my memory is failing me -- I could swear that I also had some compile problems somewhere in that mess that forced me to turn off iwmmxt for a couple of files.  But I don't remember if I did that by editing the source code, or by recompiling them individually with the armv5te settings.  I also vaguely remember getting it to build and then having to debug some segfaults caused by the iwmmxt code.  Maybe that's what triggered me to rebuild some of it with the armv5te settings.  I need to dig this up from the IRC logs and from my openwrt build area so I can reproduce it.  Anyhow at some point I got it to build.  The lipsync worked, so I stashed the executable and finished up the glue code.

And here's the proof.

High on success, I buried all memory of my sins and moved onto rockbox in an insane attempt to turn that into a viable bleeding edge openwrt package.  Apparently rockbox also had a problem with the musl select function.  I wish I could remember the details of that fix.  I couldn't even remember if I built the plugins.  Anyhow, I got rockbox to work, and got it to build as an openwrt package from a much more recent rockbox source snapshot.  I also managed somehow to include all the plugins in that package.  But I believe the keymaps for those plugins were just some quick and dirty hacks to make them compile, and so they probably still need some serious work.  And there were some changes to the rockbox battery handler code that prevented the zipit battery detection from working. However I abandoned rockbox at this point, and moved on.  A year later, I couldn't remember why.  But most likely the reason I stopped was that openwrt was fighting me whenever I tried to recompile it with the standard openwrt trick:

  make package/rockbox/compile V=99

Normally that would build only the rockbox package, and give me some nice verbose compiler output for it.  But instead of the detailed compiler messages I needed, it was feeding me some useless error message that told me absolutely nothing.  This got to be very frustrating, so I moved on in March (without polishing up the loose ends) when I got a request to help integrate my ancient ldglite project with into LPub3D.

Part 3: Moving Forward

I now suspect my woes with the openwrt build system might've been caused by some extra directories like junk and gitfiles that I'd created inside the rockbox package feed directory to hold some temporary backups and test files.  Weird!  In retrospect, I'm thinking maybe gitfiles was a bad choice on my part. I gotta remember from now on to hide any backup or working directories in the files directory.  That seems to be a safe place to stash temporary junk without breaking the build.

Once the build system was working, I finally felt like I could make some progress again with rockbox.  So examined the android battery support code and fixed up the zipit battery code in a similar way to make it work once again.  Only a year or so late...  Anyhow, the rockbox package has finally been updated with a working battery monitor.  There are some hints in the android code that rockbox could be made to work off the system volume control and also possibly use the system headphone detection, so I may tinker with that.

I also want take another look at movgrab to try and match up socket closes with each and every open.  See if that makes it allocate new sockets properly in musl...  I might also add more calls to fflush(stdout) so musl doesn't block it on printf calls with no linefeed, kinda like I did once upon a time with tinyfiledialogs.

Meanwhile, there's also a few other odds and ends that I'd like to remember here if I can find the time.

I made handy setclock utility for times when I want to edit files with proper timestamps, but the network is down.

I compiled drumtoy but did not make it a full package yet.

I also compiled a very simple game, ativayeben as sort of a turkey day special.

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 http://www.radio-browser.info 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:  ziptuner-iz2s.zip

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 landley.net.  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/ld-musl.so.0 instead of /lib/ld-musl-arm.so.1.  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/ld-musl-arm.so.1 -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 landley.net 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 ldraw.org 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 ldraw.org 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 lpub.pro

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.