Posts: 31
Threads: 5
Joined: 27.05.2024
24.09.2025, 11:33 PM
(This post was last modified: 24.09.2025, 11:41 PM by Jules Marchildon.)
Posted in source code forum.
Nice one Kevin, yours looks better. I didn't know I had a friendly stalker!
Patrice, I gave it a try, while I've run out of socks from trying all your other demo's out, I did feel the whoosh and then goosebumps! Impressive! Seems much much faster! Did you use the native LZNT1 in the last release?
Since I know diddly about obj files I downloaded a free one of the curiosity Rover...
https://free3d.com/3d-model/curiosity-rover-46907.html
...but it's missing the MTL file, so I made one up, unfortunately the conversion still failed.
Code: # dummy MTL for Curiosity Rover
newmtl default
Ka 0.200000 0.200000 0.200000
Kd 0.666667 0.666667 0.666667
Ks 0.000000 0.000000 0.000000
Ns 1.000000
illum 2
Pl
P
Posts: 4
Threads: 0
Joined: 30.01.2025
(24.09.2025, 11:33 PM)Jules Marchildon Wrote: Nice one Kevin, yours looks better. I didn't know I had a friendly stalker! 
Certainly not a stalker, friendly or otherwise- just a decades long fan of your code.
Keep 'em coming!
Posts: 8
Threads: 1
Joined: 27.01.2025
25.09.2025, 10:15 AM
(This post was last modified: 25.09.2025, 11:33 AM by Patrice Terrier.)
1. On the speed / compression:
Yes, starting with the last release, OR can take advantage of native LZNT1 compression on Windows. That’s why the load/unload feels noticeably snappier. It avoids the overhead of external zlib and integrates directly with the system’s fast block compression API.
2. On the Curiosity Rover model:
Many free .obj downloads come without a proper .mtl file, or with broken texture paths. OR expects at least one .mtl entry (even if it’s minimal). - If you invent an .mtl, make sure the material name matches the “usemtl” statements in the .obj. Otherwise, the loader won’t be able to attach materials to faces, and the conversion will fail.
- A barebones .mtl could be as simple as:
Code: newmtl Default
Kd 0.8 0.8 0.8
Ka 0.2 0.2 0.2
Ks 0.0 0.0 0.0
d 1.0
illum 2
…and then in the .obj, replace any usemtl ... lines with usemtl Default.
That should let OR import it cleanly (you’ll just get a plain-colored rover, but you can layer in textures later).
But that only, won't fix the bad conversion.
Do you want me to prep a ready-to-use dummy .mtl material, altogether with fixed .obj file, so you can see it in OR?
Added:
BTW, the model is full of small normal errors.
www.objreader.com
GDImage.dll (graphics library), WinLIFT.dll (skin engine)
Posts: 31
Threads: 5
Joined: 27.05.2024
Patrice, in reference to your PlgBlt() suggestion, I can't seem to get my needle to draw correctly, does anything pop out at you in this snippet?
Code: '=== draw needle using PlgBlt ===
angle = (225 - (value * 2.7!)) * cherryPi / 180
'-memory DC and bitmap for the needle
LOCAL hMemDC, hBitmap, hOldBitmap AS DWORD
LOCAL hMaskDC, hMaskBitmap, hOldMaskBitmap AS DWORD
LOCAL needleHeight AS LONG
needleWidth = 6
needleHeight = radius * 0.75
hMemDC = CreateCompatibleDC(hDC)
hBitmap = CreateCompatibleBitmap(hDC, needleWidth, needleHeight)
hOldBitmap = SelectObject(hMemDC, hBitmap)
'-draw a triangular needle in the source bitmap
hBrush = CreateSolidBrush(RGB(255, 0, 0))
hOldBrush = SelectObject(hMemDC, hBrush)
hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0))
hOldPen = SelectObject(hMemDC, hPen)
'-triangle points
DIM bmpPts(0 TO 2) AS POINTAPI
bmpPts(0).x = needleWidth \ 2 : bmpPts(0).y = needleHeight '-tip *bottom center
bmpPts(1).x = 0 : bmpPts(1).y = 0 '-baseL *top left
bmpPts(2).x = needleWidth : bmpPts(2).y = 0 '-baseR *top right
POLYGON hMemDC, bmpPts(0), 3
SelectObject hMemDC, hOldBrush
DeleteObject hBrush
SelectObject hMemDC, hOldPen
DeleteObject hPen
'-a 1-bit mask for transparency
hMaskDC = CreateCompatibleDC(hDC)
hMaskBitmap = CreateBitmap(needleWidth, needleHeight, 1, 1, BYVAL %NULL)
hOldMaskBitmap = SelectObject(hMaskDC, hMaskBitmap)
hBrush = CreateSolidBrush(RGB(255, 255, 255))
hOldBrush = SelectObject(hMaskDC, hBrush)
POLYGON hMaskDC, bmpPts(0), 3
SelectObject hMaskDC, hOldBrush
DeleteObject hBrush
SetStretchBltMode hDC, %HALFTONE '<-set stretch mode for better quality
DIM pts(0 TO 2) AS POINTAPI
pts(0).x = cx + (radius * 0.75) * COS(angle)
pts(0).y = cy - (radius * 0.75) * SIN(angle)
offsetRad = 90 * cherryPi / 180
pts(1).x = cx + needleWidth * COS(angle + offsetRad)
pts(1).y = cy - needleWidth * SIN(angle + offsetRad)
pts(2).x = cx + needleWidth * COS(angle - offsetRad)
pts(2).y = cy - needleWidth * SIN(angle - offsetRad)
PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0
SelectObject hMemDC, hOldBitmap
DeleteObject hBitmap
DeleteDC hMemDC
SelectObject hMaskDC, hOldMaskBitmap
DeleteObject hMaskBitmap
DeleteDC hMaskDC
'=== center cap ===
hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0)) '-outline
hOldPen = SelectObject(hDC, hPen)
ELLIPSE hDC, cx - 8, cy - 8, cx + 8, cy + 8
SelectObject hDC, hOldPen
DeleteObject hPen
Posts: 8
Threads: 1
Joined: 27.01.2025
Jules
I have a new ASUS laptop, but not had time yet to install PowerBASIC on it (Only VS2022).
Thus about your problem
Short answer: the three destination points you pass to PlgBlt don’t correspond to the expected corners of the source rectangle.
PlgBlt maps the source RECT (0,0,w,h) to a parallelogram whose corners must be given in this order:
pts(0) → upper-left of the dest parallelogram (maps source (0,0))
pts(1) → upper-right (maps source (w,0))
pts(2) → lower-left (maps source (0,h))
Your code sets pts(0) at the needle tip and the other two as small offsets around the center, so the source bitmap (which has base on top and tip at the bottom) is being warped incorrectly. You also use the full needleWidth instead of half-width for the side offsets.
Fix
Keep the source bitmap exactly as you drew it (base at the top, tip at the bottom). Compute a unit vector along the needle (u) and a perpendicular unit vector (v), then place the three corners as a rotated rectangle whose top edge is the base and whose bottom edge reaches the tip.
PowerBASIC-style math (same symbols you used):
' angle already in radians, pointing from center to tip
ux = COS(angle) : uy = -SIN(angle) ' forward (toward tip)
vx = COS(angle + cherryPi/2) : vy = -SIN(angle + cherryPi/2) ' left normal
halfW = needleWidth / 2
h = needleHeight
' Base is at the gauge center (cx,cy)
baseX = cx : baseY = cy
' === Parallelogram corners for PlgBlt ===
' UL (maps source 0,0) → base - halfW * v
pts(0).x = baseX - halfW * vx
pts(0).y = baseY - halfW * vy
' UR (maps source w,0) → base + halfW * v
pts(1).x = baseX + halfW * vx
pts(1).y = baseY + halfW * vy
' LL (maps source 0,h) → base + h * u - halfW * v
pts(2).x = baseX + h * ux - halfW * vx
pts(2).y = baseY + h * uy - halfW * vy
PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0
Mask note
You create a 1-bit mask and paint the same triangle white on black. That’s fine for PlgBlt (white bits in the mask select the pixels to transfer). If you see inverted transparency, just invert the mask (fill white, draw the triangle black).
Other small nits
Use half width for the side offsets (fixed above).
SetStretchBltMode(hDC, %HALFTONE) is harmless here but not required unless you actually stretch the source.
Ensure you re-select the old bitmap/brush/pen into the DCs before deleting (you already do that—good).
With the corrected corner mapping the needle will rotate cleanly around (cx,cy) while still using your triangular source + mask.
www.objreader.com
GDImage.dll (graphics library), WinLIFT.dll (skin engine)
Posts: 31
Threads: 5
Joined: 27.05.2024
Patrice, I'm not getting any better results, perhaps in this case. It's OK, I do have a custom GDI+ round gauge to finish working on later. For now, this simple gauge is good enough for the testing PCB application at work.
Code: '=== draw needle using PlgBlt with corrected points ===
angle = (225 - (value * 2.7!)) * cherryPi / 180
'-create a memory DC and bitmap for the needle
LOCAL hMemDC AS DWORD, hBitmap AS DWORD, hOldBitmap AS DWORD
LOCAL hMaskDC AS DWORD, hMaskBitmap AS DWORD, hOldMaskBitmap AS DWORD
LOCAL needleHeight AS LONG
needleWidth = 14
needleHeight = radius * 0.80
hMemDC = CreateCompatibleDC(hDC)
hBitmap = CreateCompatibleBitmap(hDC, needleWidth, needleHeight)
hOldBitmap = SelectObject(hMemDC, hBitmap)
'-draw a triangular needle in the source bitmap
hBrush = CreateSolidBrush(RGB(255, 0, 0)) '-red fill
hOldBrush = SelectObject(hMemDC, hBrush)
hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0)) '-black outline
hOldPen = SelectObject(hMemDC, hPen)
'-define triangle points in the bitmap, *base at top, tip at bottom
DIM bmpPts(0 TO 2) AS POINTAPI
bmpPts(0).x = needleWidth \ 2 : bmpPts(0).y = needleHeight '-tip, bottom center
bmpPts(1).x = 0 : bmpPts(1).y = 0 '-base, left top left
bmpPts(2).x = needleWidth : bmpPts(2).y = 0 '-base, right top right
POLYGON hMemDC, bmpPts(0), 3
SelectObject hMemDC, hOldBrush
DeleteObject hBrush
SelectObject hMemDC, hOldPen
DeleteObject hPen
' -create a 1-bit mask for transparency
hMaskDC = CreateCompatibleDC(hDC)
hMaskBitmap = CreateBitmap(needleWidth, needleHeight, 1, 1, BYVAL %NULL)
hOldMaskBitmap = SelectObject(hMaskDC, hMaskBitmap)
hBrush = CreateSolidBrush(RGB(255, 255, 255)) '-white = opaque
hOldBrush = SelectObject(hMaskDC, hBrush)
POLYGON hMaskDC, bmpPts(0), 3 '-same triangle for mask
SelectObject hMaskDC, hOldBrush
DeleteObject hBrush
'-set stretch mode for better quality
'SetStretchBltMode hDC, %HALFTONE
'-define destination points using unit vectors (per Patrice)
DIM pts(0 TO 2) AS POINTAPI
LOCAL ux, uy, vx, vy, halfW, baseX, baseY AS SINGLE
ux = COS(angle) : uy = -SIN(angle) '-forward, toward tip
vx = COS(angle + cherryPi / 2) : vy = -SIN(angle + cherryPi / 2) '-left normal
halfW = needleWidth / 2
LOCAL h AS LONG
h = needleHeight
baseX = cx : baseY = cy
'-parallelogram corners for PlgBlt
' UL (maps source 0,0) base - halfW * v
pts(0).x = baseX - halfW * vx
pts(0).y = baseY - halfW * vy
' UR (maps source w,0) base + halfW * v
pts(1).x = baseX + halfW * vx
pts(1).y = baseY + halfW * vy
' LL (maps source 0,h) base + h * u - halfW * v
pts(2).x = baseX + h * ux - halfW * vx
pts(2).y = baseY + h * uy - halfW * vy
'-do the PlgBlt with mask
PlgBlt hDC, pts(0), hMemDC, 0, 0, needleWidth, needleHeight, hMaskBitmap, 0, 0
'-clean up
SelectObject hMemDC, hOldBitmap
DeleteObject hBitmap
DeleteDC hMemDC
SelectObject hMaskDC, hOldMaskBitmap
DeleteObject hMaskBitmap
DeleteDC hMaskDC
'=== center cap ===
hPen = CreatePen(%PS_SOLID, 1, RGB(0, 0, 0)) '-outline
hOldPen = SelectObject(hDC, hPen)
ELLIPSE hDC, cx - 8, cy - 8, cx + 8, cy + 8
SelectObject hDC, hOldPen
DeleteObject hPen
|