Miniwindows showcase (has a lot of graphics)

Posted by Nick Gammon on Sat 05 Jun 2010 06:53 AM — 19 posts, 103,032 views.

Australia Forum Administrator #0

This post shows off some of the things people have done with miniwindows in MUSHclient.

For more information about miniwindows see http://mushclient.com/mw


Inventory:

Mouse-over popups:

The overall experience:

More mouse-over windows:

Big map:

Big map with zone names:

Another bigmap. A hand-drawn map image underneath, with the player location superimposed on top. Looks nice as you walk around.

Information about the MUD:

Mapper:

Mapper, zoomed-out:

Button bar:

Button bar with cooldown times:

Health (stats) bar:

Help information:

Campaign tracker:

Another inventory plugin:

Quest tracker:

Experience bar (blue bar at bottom):

God Wars - mapper, stats, enemy info:

More mouse-over information

Room and mob info, status bar, mapper:

Gauges:

Avatar images:

Scrolling chat window (Names blurred for privacy).

3D window. Surely not?

Amended on Wed 09 Jun 2010 04:08 AM by Nick Gammon
USA #1
I demands evidence of 3D game that you show us in the latter screenshot! XD


on other hand, it really cool screenshot i have seen, keep up good work.
Australia Forum Administrator #2
Evidence other than your own eyes?

Just an experimental plugin, using WindowSetPixel to make a 3D view, based on the exit information from the MUD. Never totally got it to work properly, but maybe someone will be inspired?

Oh, and the MUD was Aardwolf.
Amended on Sat 05 Jun 2010 07:31 AM by Nick Gammon
USA #3
Bleeping bleep. How long have you been hiding that from us?
Australia Forum Administrator #4
Ah, about August 2008?

The thing is, I don't like releasing stuff until it works perfectly, and I wasn't happy with its overall effect. It worked fast enough, no problems there.

The whole thing was done with the released version of MUSHclient at the time, so anyone who wants to can do the same thing.
Australia Forum Administrator #5
Check out the release notes for August 2008 (version 4.36):

http://www.gammon.com.au/scripts/showrelnote.php?version=4.36&productid=0

Quote:

6. Added new function WindowSetPixel - this lets you set individual pixel colours in a miniwindow.


Why else would I want you to be able to set individual pixels? 3D, ah yes!
USA #6
I guess you effectively had to write the whole graphics pipeline there. I'm not much good at 3D maths, heh... that's not something I'd readily undertake.
Australia Forum Administrator #7
Also in the same release:

Quote:

16. Added new script function BlendPixel - this does the same thing as WindowBlendImage, except it operates on a single pixel (that is, a single RGB colour for the blend colour, and a single RGB colour for the base colour). This does the same thing, to a single pixel, as WindowBlendImage (with the same codes). This is intended for situations where you need to work on individual colours - either in a miniwindow image, or generally, to do things like merge them together in various ways.


That's how I did the shadows.
Australia Forum Administrator #8
Twisol said:

I'm not much good at 3D maths, heh...


Me neither. That was part of the problem.
USA #9
One of the things I was thinking about while perusing the code a week ago was the possibility of an API to use the Direct3D library instead of GDI. I hear Windows 7 doesn't use hardware acceleration for GDI anymore. I think that would help a lot with things like this, because D3D handles the bulk of the math for you.

I'd rather break up the God Object (CMUSHClientDoc) [1], though, so that's just another of my long-term hopes...

[1] http://en.wikipedia.org/wiki/God_object

EDIT: Ah, it's actually Vista where GDI became non-accelerated. Earlier than I thought!
Amended on Sat 05 Jun 2010 10:07 AM by Twisol
Australia Forum Administrator #10
Twisol said:

I guess you effectively had to write the whole graphics pipeline there.


I don't know about that. The whole plugin was in Lua.
USA #11
Nick Gammon said:

Twisol said:

I guess you effectively had to write the whole graphics pipeline there.


I don't know about that. The whole plugin was in Lua.


But I mean, you had to do the projection/transformation calculations yourself, I assume. You have a perspective view, and you have lighting (AND shadows). I'd be interested in seeing the source, even if just to boggle at.
Australia Forum Administrator #12
Ah yes, I wonder where I put it?
Australia Forum Administrator #13
Let me tell you about the problems with the 3D view ...

When you initially look at it you think "Holy Guacamole! - that's amazing!" ... but after a little while you think "hey, this is the Jungle of Verume- it looks like a dungeon, not a jungle".

Then you go back to the City (Aylor) and it still looks the same, like a badly implemented version of Castle Wolfenstein.

http://en.wikipedia.org/wiki/Wolfenstein_3D

Then you start to think "it says there are 5 mobs in the room - well, where are they?" - so the 3D graphics actually lower the "suspension of disbelief" that is important to playing games.

You go into a shop, but you don't see a shopkeeper, or the things he sells. Now all this could be overcome to an extent with a lot of extra graphics and sprites, but then it starts to turn into a 3D graphical MMO and not a MUD game.

You hit problems with things like stairs leading down, or up, which are hard to represent. In fact in some rooms which have exits north, south, east, west, northeast, northwest, southeast, southwest, up *and* down, it is hard to work out a way of showing that in 3D.

So it looks good initially, and shows off what the miniwindows are capable of drawing, but as a MUD experience, it doesn't quite work, unfortunately.

This is one of the reasons why, in my more recent postings about improving MUD game retention rates, I have kept quiet about trying to do a 3D view.
Amended on Sat 05 Jun 2010 11:22 AM by Nick Gammon
USA #14
Oh, of course. I'm just impressed that a 3D view could be done at all. ;)
Australia Forum Administrator #15
I can't get the 3D mapper to work right now (mainly because of the horrible way I was getting the mapping data into it), but this snippet (which you can run in the Immediate window) actually draws one 3D scene which a constant at the start of it. To try it out you need the texture files, which I don't want to supply because I'm not sure of the copyright. But it shows the general idea.


-- 3D mapper test 
-- Written by Nick Gammon 
-- Date: August 2008

-- test map

map = [==[
         ---     ---        
        |{+} {!} {+}|     
     -------     ---|        
    +{+} {+} {+} {+>|     
     -------     ---         
         [?] <!> [?]      
                 ---         
         [?] <#> < . [?]  
                 ---         
         [?] < > [?]      
     -------     -------     
     - - -!- < > -*- - -  
     -------     -------     
            |. .|    
          
            
                   
]==]


local mapWidth = 80
local mapHeight = 80

local texWidth = 64
local texHeight = 64

posX = 35; posY = 35;  --x and y start position
dirX = 0; dirY = -1; --initial direction vector
planeX = -0.7853; planeY = 0; --the 2d raycaster version of camera plane

HEIGHT_MULTIPLIER = 4

dir = "/Program Files/mushclient/"
  
require "tprint"
require "getlines"


-- make local for speed

local floor = math.floor
local ceil = math.ceil
local abs =  math.abs
local sqrt =  math.sqrt
local cos = math.cos
local sin = math.sin

local WindowLine = WindowLine

local function int (x)
  if x >= 0 then
    return floor (x)
  end  -- if positive

  return ceil (x)
end -- function int

function setup_map_window ()
  

  WINDOW_HEIGHT = 300
  WINDOW_WIDTH = 400 -- 320
  
  win = "3d"
  wins = "3dshadow"
  
  -- how many pixels per tile
  
  tilewidth = WINDOW_WIDTH / mapWidth
  tileheight = WINDOW_HEIGHT / mapHeight
  
  WindowCreate (win, 
    0, -- left
    0, -- top
    WINDOW_WIDTH, -- width
    WINDOW_HEIGHT, -- height
    8, -- mode
    0, -- flags
    0x383838)
  
  WindowCreate (wins, 
    0, -- left
    0, -- top
    WINDOW_WIDTH, -- width
    WINDOW_HEIGHT, -- height
    8, -- mode
    0, -- flags
    0xFFFFFF)
        
  WindowShow (win, true)
  
  local pencolour = 0x555555
  local centre = WINDOW_HEIGHT / 2
  
  -- sky
  WindowGradient (win, 0, 0, 0, centre, 
                  0xFFBF00,
                  ColourNameToRGB ("darkblue"), 
                  2)  -- top to bottom
  
  -- ground
  WindowGradient (win, 0, centre, 0, 0, 
          0x00091C, 
          ColourNameToRGB ("lightsalmon"), 
          2)  -- top to bottom
          
  -- noise        
  WindowFilter (win, 0, 0, 0, 0, 2, 15)
  
  WindowRectOp (win, 1,   -- frame rectangle
            0, 0, 
            0, 0, 
            0x00002F)
        
  images = {
      "eagle",        -- 1
      "redbrick",     -- 2
      "purplestone",  -- 3
      "greystone",    -- 4
      "bluestone",    -- 5
      "mossy",        -- 6
      "wood",         -- 7
      "colorstone",   -- 8
    }
  
  check (WindowLoadImage (win, "eagle", dir .. "eagle.bmp"))  
  check (WindowLoadImage (win, "redbrick", dir .. "redbrick.bmp"))
  check (WindowLoadImage (win, "purplestone", dir .. "purplestone.bmp"))
  check (WindowLoadImage (win, "greystone", dir .. "greystone.bmp"))
  check (WindowLoadImage (win, "bluestone", dir .. "bluestone.bmp"))
  check (WindowLoadImage (win, "mossy", dir .. "mossy.bmp"))
  check (WindowLoadImage (win, "wood", dir .. "wood.bmp"))
  check (WindowLoadImage (win, "colorstone", dir .. "colorstone.bmp"))
 
end -- setup_map_window

local colour_map = {
    [1] = ColourNameToRGB ("red"),
    [2] = ColourNameToRGB ("green"),
    [3] = ColourNameToRGB ("blue"),
    [4] = ColourNameToRGB ("white"),
    } -- end colour_map
    
        
local function hwall (wx, wy, which)
  for i = wx, wx + 9 do
    worldMap [i] [wy] = which
  end -- for
end -- hwall

local function vwall (wx, wy, which)
  for i = wy, wy + 9 do
    worldMap [wx] [i] = which
  end -- for
end -- hwall

    
function convert_map ()
  
  -- empty table
  maprooms = {}
  for x = 1, 8 do
    maprooms [x] = {}
  end -- for each x
  
  mapline = {}
  
  x = 1
  for line in getlines (map) do
    mapline [x] = line
    x = x + 1
  end -- for
  
  -- bleecccchh
  
  for x = 1, 8 do  
    for y = 1, 8 do
      local t = {}
      maprooms [x] [y] = t
      t.top = mapline [y * 2 - 1]:sub ((x - 1) * 4 + 1, (x - 1) * 4 + 5)
      t.middle = mapline [y * 2    ]:sub ((x - 1) * 4 + 1, (x - 1) * 4 + 5)
      t.bottom = mapline [y * 2 + 1]:sub ((x - 1) * 4 + 1, (x - 1) * 4 + 5)
    end -- each y
  end -- each x

  
  worldMap = {}
  
  for x = 1, mapWidth do
    local t = {}
    worldMap [x] = t
    for y = 1, mapHeight do
       t [y] = 0  -- nothing there yet
    end -- for
  end -- for
  
  for x = 1, 8 do  
    for y = 1, 8 do
      t = maprooms [x] [y]
      local wx = (x - 1) * 10 + 1
      local wy = (y - 1) * 10 + 1
     
      if t.top:sub (2, 3) == "--" then
        hwall (wx, wy, 4)
      end -- if
  
      if t.bottom:sub (2, 3) == "--" then
        hwall (wx, wy + 9, 4)
      end -- if

      if t.top:sub (2, 3) == "-+" then
        hwall (wx, wy, 2)
      end -- if

      if t.bottom:sub (2, 3) == "-+" then
        hwall (wx, wy, 2)
      end -- if

      if t.middle:sub (-1) == "|" then
        vwall (wx + 9, wy, 4)
      end -- if
      
      if t.middle:sub (1, 1) == "|" then
        vwall (wx, wy, 4)
      end -- if
       
      if t.middle == " [?] " then
        vwall (wx, wy, 7)
        vwall (wx + 9, wy, 7)
      end -- if
           
    end -- each y
  end -- each x  
     
end -- convert_map    

texture = {}
for x = 1, 64 do
  texture [x] = {}
  for y = 1, 64 do
    local c = bit.xor (x, y)
    texture [x] [y] = c + c * 0x000200 + x * 0x010000
  end -- for
end -- for

function Display_Map ()

  convert_map ()
  
  setup_map_window ()

  local w = WINDOW_WIDTH
  local h = WINDOW_HEIGHT
  
  for x = 0, w - 1 do
    --calculate ray position and direction 
   local cameraX = 2 * x / w - 1 --x-coordinate in camera space
   local rayPosX = posX
   local rayPosY = posY
   local rayDirX = dirX + planeX * cameraX
   local rayDirY = dirY + planeY * cameraX
    
  --which box of the map we're in  
  
   local mapX = int (rayPosX)
   local mapY = int (rayPosY)
     
    --length of ray from current position to next x or y-side
    local sideDistX
    local sideDistY
     
     --length of ray from one x or y-side to next x or y-side
    local deltaDistX = sqrt (1 + (rayDirY * rayDirY) / (rayDirX * rayDirX))
    local deltaDistY = sqrt (1 + (rayDirX * rayDirX) / (rayDirY * rayDirY))
    local perpWallDist
     
    --what direction to step in x or y-direction (either +1 or -1)
    local stepX
    local stepY

    local hit = false  --was there a wall hit?
    local YsideHit --was a NS or a EW wall hit?
    
    -- calculate step and initial sideDist
    
    if rayDirX < 0 then
      stepX = -1
      sideDistX = (rayPosX - mapX) * deltaDistX
    else
      stepX = 1
      sideDistX = (mapX + 1.0 - rayPosX) * deltaDistX
    end -- if
    
    if rayDirY < 0 then
      stepY = -1
      sideDistY = (rayPosY - mapY) * deltaDistY
    else
      stepY = 1
      sideDistY = (mapY + 1.0 - rayPosY) * deltaDistY
    end -- if
        
   --perform DDA
   while not hit do
    
    --jump to next map square, OR in x-direction, OR in y-direction
    if sideDistX < sideDistY then
      sideDistX = sideDistX + deltaDistX
      mapX = int (mapX + stepX)
      YsideHit = false
    else
      sideDistY = sideDistY + deltaDistY
      mapY = int (mapY + stepY)
      YsideHit = true
    end -- if
    
    if mapX < 0 or 
       mapY < 0 or
       mapX > mapWidth or
       mapY > mapHeight then
       break
     end -- outside map
       
    if worldMap[mapX] == nil then
      break
    end -- if off map  
    
   if worldMap[mapX] [mapY] == nil then
      break
    end -- if off map  
    
    --Check if ray has hit a wall
    if worldMap[mapX][mapY] > 0 then
     hit = true
    end -- if hit
    
  end -- while 
  
 local wallX --where exactly the wall was hit
 local drawEnd
     
   --calculate value of wallX
    
    if YsideHit then
      wallX = rayPosX + ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY) * rayDirX
    else       
      wallX = rayPosY + ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX) * rayDirY
    end -- if 
    
    -- Calculate distance projected on camera direction (oblique distance will give fisheye effect!)
    
    if not YsideHit then
      perpWallDist = abs ((mapX - rayPosX + (1 - stepX) / 2) / rayDirX)
    else
      perpWallDist = abs ((mapY - rayPosY + (1 - stepY) / 2) / rayDirY)
    end -- if
 
      
    --Calculate height of line to draw on screen
    local lineHeight = int (abs  (h / perpWallDist) * HEIGHT_MULTIPLIER)
     
    --calculate lowest and highest pixel to fill in current stripe
    local drawStart = -lineHeight / 2 + h / 2
    if drawStart < 0 then
      drawStart = 0
    end -- if
    
    drawEnd = lineHeight / 2 + h / 2
    if drawEnd >= h then
      drawEnd = h - 1
    end -- if
    
   local adjwallX = wallX / 10
   adjwallX = adjwallX - floor((adjwallX))

   if hit then
 
      --texturing calculations
      local texNum = worldMap [mapX][mapY]
       
      --x coordinate on the texture
      local texX = int (adjwallX * texWidth)
      
      if (not YsideHit) and (rayDirX > 0) then
         texX = texWidth - texX - 1
      end -- if
       
      if YsideHit and (rayDirY < 0) then
       texX = texWidth - texX - 1
      end -- if
      
      check (WindowDrawImage(win, images [texNum], x + 1, drawStart , x + 2, drawEnd, 
             2, 
              texX, 0, texX + 1, 0))
          
      check (WindowFilter (win,  x + 1, drawStart , x + 2, drawEnd, 7, -perpWallDist * 4)) -- darken
      
      if x >  w / 2 then
        check (WindowFilter (win,  x + 1, drawStart , x + 2, drawEnd, 9, 1.4)) -- gamma (darken)
      else
        check (WindowFilter (win,  x + 1, drawStart , x + 2, drawEnd, 9, .8)) -- gamma (lighten)
      end -- if on RH side
               
   end -- of hit on wall
   
    --FLOOR CASTING

    local floorXWall, floorYWall -- x, y position of the floor texel at the bottom of the wall

    --4 different wall directions possible
    
    if not YsideHit and (rayDirX > 0) then
      floorXWall = mapX
      floorYWall = mapY + wallX
    elseif not YsideHit and (rayDirX < 0) then
      floorXWall = mapX + 1.0
      floorYWall = mapY + wallX
    elseif YsideHit and (rayDirY > 0) then
      floorXWall = mapX + wallX 
      floorYWall = mapY
    else
      floorXWall = mapX + wallX 
      floorYWall = mapY + 1.0
    end -- if
        
    local distWall, currentDist  

    distWall = perpWallDist
   
    if drawEnd < 0 then
      drawEnd = h -- becomes < 0 when the integer overflows
    end -- if
    
    --draw the floor from drawEnd to the bottom of the screen
    for y = int (drawEnd + 1), h do
      currentDist = h / (2 * y - h) -- you could make a small lookup table for this instead
      
      --floor
      WindowSetPixel (win, x, y, 0x0000FF / currentDist)
     
      -- ceiling (symmetrical!)
      WindowSetPixel (win, x, h- y, int (256 / currentDist) * 256)
      
    end -- for loop
         
    -- shadow
    
    if x > w / 2 then
      WindowLine(wins, x + 1, drawEnd , x - abs (drawStart - drawEnd), drawEnd, 0x777777, 0, 1)
    end -- if 
        
  end -- for each x
  
  WindowFilter(wins, 0, 0, 0, 0, 3, 0)
  WindowImageFromWindow (win, "shadows", wins)
  
  WindowBlendImage (win, "shadows", 4, 0, 0, 0, 6, 1)

end -- Display_Map

Display_Map ()



That's under 500 lines of Lua code to render a 3D window.

Below is the output as rendered by the above code.

That took 0.17 seconds to do the Display_Map part.
Amended on Sat 05 Jun 2010 11:11 PM by Nick Gammon
Australia Forum Administrator #16

Australia Forum Administrator #17
The image files are basically 64 x 64 pixel 24 bits-per-pixel bitmap (bmp) files, so any file along those lines should be OK if you want to try to run it.
Australia Forum Administrator #18

I think I got a lot of inspiration from here:

http://www.student.kuleuven.be/~m0216922/CG/raycasting.html

and:

http://www.student.kuleuven.be/~m0216922/CG/raycasting2.html

I think you can also download the graphics files (textures) from that page too.