Voxel Slicer (Fake 3D in Max2D)
Community Forums/Showcase/Voxel Slicer (Fake 3D in Max2D)
| ||
I started playing around with MagicaVoxel and wanted to import the .VOX files to BlitzMax but not use any 3D engine. A friend of mine showed me this method to get "3D" in GameMaker. So I decided to use a similar method to get the .VOX files into BlitzMax using nothing but BlitzMax's standard Max2D module. Here's the result: Basically I just load the .VOX file and create slices, like a CRT scan, and render those ontop of each other. You can load the .VOX files directly, or you can output a PNG file and load that, which is a lot faster! You can also use palettes and change the camera angle of the voxel model. The shadow I put in there is just for show, you can use it but it's pretty "cheap". Here's the final module: https://bitbucket.org/Hezkore/vox-loader The module comes with some examples and .VOX files to get you started. Though it's so simple to use, I doubt anyone will need the examples heh. In the end you just use it like a normal TImage. SetRotation, SetScale, SetColor etc. all works like normal with it, just that rotation is now 3D. Here's a module for Monkey X that can load PNG slices: https://bitbucket.org/Hezkore/monkey-vox-slice-loader I hope someone uses this to make something cool! |
| ||
Cool and interesting mod Hezkore. Any chance of handling incbin files? I wonder how would that handle animations, as a separate sprite? |
| ||
Very nice stuff ! |
| ||
@RustyKristi The downside to voxels have always been the animations. You could do, as you said, separate images for each frame, but It's mostly used for static things. I might be able to get incbin working for PNG slices, probably not VOX files though. @Bobysait Thank you! :) I've added a VOX viewer you can use to save PNG slices directly from VOX files, then import the slices to your project. |
| ||
@RustyKristi I've made LoadSlices work with IncBin now. Check out example 7 if you want to know how it works. |
| ||
Awesome! |
| ||
I've updated the module. The shading is now much nicer and drawing has been optimized. It now also comes with a simple demo showing everything in action. You can grab the Windows EXE for that demo right here: https://dl.dropboxusercontent.com/u/2842751/vox_demo.zip Hold left mouse button to move. Hold right mouse button to rotate the camera and change angle. |
| ||
Superb coding. very sneaky and clever! Well impressed... |
| ||
Awesome Hezkore! Source code please? :B |
| ||
@RustyKristi check my first post. :) |
| ||
..hey..this is very nice..thanks man.. |
| ||
Ah yes, thanks! |
| ||
It looks like MagicaVoxel's got a big update and now supports animations, built-in palettes and different material types per voxel. I'll try to add support for all of this but it might take some time as I'll have to redo a big part of the module. You can still use the module of course, it still loads the newest VOX format, it just doesn't use the new data. |
| ||
I would absolutely LOVE to see a Blitz3D version of this! Thanks alot bro! ~GF |
| ||
@Guy Fawkes If you just want to load the sliced PNG files (which you should anyways) it's super simple to use in any language. It's built to support the standard LoadImage function frame system. All you do is load for example "obj_tree3_10x10.png" and in this case each frame/slice is 10 width and 10 in height. Then just draw those ontop of each other. Rotate the frames/slices from the middle and voila, it's 3D! Though with Blitz3D I would probably recommend loading the voxels and generating a proper 3D mesh. I only did this because BlitzMax has no official 3D engine. |
| ||
as Hezkore mentioned.. Though with Blitz3D I would probably recommend loading the voxels and generating a proper 3D mesh. I only did this because BlitzMax has no official 3D engine. I would absolutely LOVE to see a Blitz3D version of this! Fake 3D in Blitz3D?? yea what's the point? :-) |
| ||
The point is to have a little bit of fun! That's all! I think it would be really AWESOME to see something like this in Blitz3D! I mean. Think about it. We could make Chrono Trigger-like games with this type of function! The point that this can also save ALOT of memory by it being Fake 3D anyway! Thanks alot! ~GF |
| ||
@Guy Fawkes Just load the slices and get to it! :) But since you're not using 3D, maybe you could just use BlitzMax? Notice that animations are rather difficult when it comes to voxels. Though I now have the new animation system MagicaVoxel uses working in the module, so things are a bit easier now atleast. |
| ||
I'm not going to change my program of choice! I am a blitz3D programmer & that's that! Why can't there be like an example not necessarily THIS library, but an example showing an animated character LIKE this in Blitz3D? I think it would be fun & could bring back Super Nintendo graphics which are the BEST! Thanks alot Hekzore! ~GF |
| ||
@Guy Fakwes I haven't used Blitz3D, so I don't think I'll ever get around to porting it, sorry :/ In other news. I've gotten most of the new features working and updated the source code, so grab it while it's hot! Here's an animation example for Windows (works in Linux too): example9.exe ADDED: Support for VOX animations/frames ADDED: Support for built-in palettes FIXED: Junk pixels when saving PNG slices CHANGED: Depth and Frame count is now included in PNG name ADDED: Frame slider in VOX Viewer ADDED: TVox now uses already loaded VOX files instead of loading new ones ADDED: Example 9 showing animationsI'm not sure if I should support "materials". The only thing that would actually work in 2D would be the glass material which would make voxels transparent. |
| ||
I'm not going to change my program of choice! I am a blitz3D programmer & that's that! GF, we're not questioning your loyalty with B3D lol, but it helps if you have a little bit of knowledge in BlitzMax because they are related language, in fact a successor of B3D w/o the 3D. It's really not that hard (I have converted some small snippets myself) esp if the code base is not that big. So now is your chance.. ;-) @Hezkore COOL ANIMATION DEMO! :D I'm not sure if I should support "materials" yea why not? at least make it experimental or optional? |
| ||
@RustyKristi Thanks! :) I've added some very basic glass material support. It's hard to do transparency when the slices add to the overall alpha. But things do become a bit see-through at least heh. Not sure if I should add an example for it or just include it as sort of an unsupported feature. I'm going to port the Slice loading part over to Monkey X (And Monkey 2 eventually) if anyone's interested. |
| ||
@Hezakore Been tinkering with the core drawing routines to optimize a bit and improve the shadows. The shadows now take an alpha from 0 to 1 and fade them out as they are drawn so you get nicer effects. the drawing code has been tidied up and precalcs added and if's removed giving a speed boost Here's the augmented code: Method DrawShadow(dX:Float, dY:Float, angleX:Float, angleY:Float, Frame:Int = 0) If Not _Shadow Then Return GetScale(_LastScale[0], _LastScale[1]) dX = ScreenX(dX, GetRotation()) dY = ScreenY(dY, GetRotation()) brl.max2d.SetRotation(_Rotation + _LastRotation) Frame:Mod _ModelCount If Frame < 0 Then Frame:+_ModelCount local inAlpha:float = GetAlpha() local alpha:float = inAlpha local alphadiff:float = (1.0 / _MaxZ) * .5 setcolor 0,0,0 For Local i:Int = 0 Until _MaxZ setalpha alpha alpha :- alphadiff dX:+_LastScale[0] * angleX * i dY:+_LastScale[1] * angleY * i 'Don't draw things we don't see! If dX < - _MaxX * _LastScale[0] Then dX:-_LastScale[0] * angleX * i;dY:-_LastScale[1] * angleY * i;Continue If dX > GraphicsWidth() + _MaxX * _LastScale[0] Then dX:-_LastScale[0] * angleX * i;dY:-_LastScale[1] * angleY * i;Continue If dY < - _MaxY * _LastScale[1] Then dX:-_LastScale[0] * angleX * i;dY:-_LastScale[1] * angleY * i;Continue If dY > GraphicsHeight() + _MaxY * _LastScale[1] Then dX:-_LastScale[0] * angleX * i;dY:-_LastScale[1] * angleY * i;Continue DrawImage(_Shadow, dX, dY, i + (_MaxZ * Frame)) DrawImage(_Shadow, dX, dY - _LastScale[1] * 0.5, i + (_MaxZ * Frame)) If i + 1 < _MaxZ Then DrawImage(_Shadow, dX, dY - _LastScale[1] * 1, i + (_MaxZ * Frame)) dX:-_LastScale[0] * angleX * i dY:-_LastScale[1] * angleY * i Next SetAlpha inAlpha brl.max2d.SetRotation(_LastRotation) EndMethod Method Draw(dX:Float, dY:Float, Frame:Int = 0) If Not _Slices Then Return GetScale(_LastScale[0], _LastScale[1]) dX = ScreenX(dX, GetRotation()) dY = ScreenY(dY, GetRotation()) brl.max2d.SetRotation(_Rotation + _LastRotation) Frame:Mod _ModelCount If Frame < 0 Then Frame:+_ModelCount local dxi:float local itilt:float local tiltdiff:float = _Tilt * .25 local tilt:float = 0 local dyi:float local iangle:float = 0 local anglediff:float = _Angle * .25 local angle:float = 0 local _mframe:int = _MaxZ * Frame SetColorBaseShade() if _Quality then For Local i:Int = 0 Until _MaxZ 'precalc dxi = dX - itilt * _LastScale[0] tilt = tiltdiff dyi = dY - iangle * _LastScale[0] angle = angleDiff 'Don't draw things we don't see! If dX < - _MaxX * _LastScale[0] Then Continue If dX > GraphicsWidth() + _MaxX * _LastScale[0] Then Continue If dyi * _Angle < - _MaxY * _LastScale[1] Then Continue If dyi * _Angle > GraphicsHeight() + _MaxY * _LastScale[1] Then Continue 'Draw the slice itself SetColorShade(i) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) tilt :+ tiltdiff angle :+ anglediff 'Draw more slices to fill in the gaps above SetColorShade(i + 0.25) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) tilt :+ tiltdiff angle :+ anglediff SetColorShade(i + 0.5) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) tilt :+ tiltdiff angle :+ anglediff SetColorShade(i + 0.75) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) _mframe :+ 1 itilt :+ _Tilt iangle :+ _Angle next else For Local i:Int = 0 Until _MaxZ 'precalc dxi = dX - itilt * _LastScale[0] tilt = tiltdiff dyi = dY - iangle * _LastScale[0] angle = angleDiff 'Don't draw things we don't see! If dX < - _MaxX * _LastScale[0] Then Continue If dX > GraphicsWidth() + _MaxX * _LastScale[0] Then Continue If dyi * _Angle < - _MaxY * _LastScale[1] Then Continue If dyi * _Angle > GraphicsHeight() + _MaxY * _LastScale[1] Then Continue 'Draw the slice itself SetColorShade(i) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) tilt :+ tiltdiff + tiltdiff angle :+ anglediff + anglediff 'Draw more slices to fill in the gaps above SetColorShade(i + 0.5) DrawImage(_Slices, dxi - _LastScale[0] * tilt, dyi - _LastScale[0] * angle, _mframe) _mframe :+ 1 itilt :+ _Tilt iangle :+ _Angle next end if If _Shade Then SetColor(_LastColor[0], _LastColor[1], _LastColor[2]) brl.max2d.SetRotation(_LastRotation) EndMethod |
| ||
@AdamStrange Cool stuff! I've been meaning to add fading shadows, though I feel like that should be optional. Have you measured the improved drawing time? I'm not sure I like having two drawing methods but if it gives a speed boost I guess it's okay haha. |
| ||
Yes, great work as always Adam...I'm sure I'm not the only one thinking, be great too see a game of his on Steam. |
| ||
@AdamStrange Alright I've done some testing and it takes about 4ms (in debug mode) for me to render a frame of the start area from demo.bmx With your code I get exactly the same 4ms render time. Got an example of where the render time would be improved? |
| ||
I've made a quick version of this for Monkey X. It only loads sliced PNGs though. You can find the module in my first post in this thread. The BMax module can now save padded PNG slices which you should use with Monkey X. I've also fixed some bugs. |
| ||
maybe do a stress test with very a large number? what's a padded slice? |
| ||
If you use SaveSlices with the Padded parameter set to true you'll get a one pixel border around each slice. |
| ||
aha - brilliant! Just been working on a magicavoxel import for monkey2. needed to go back to the magic site and get the palette reference and file format. Here's the initial result: No 3d was harmed in this process It is a full vexed editor as well working on vertical slices |
| ||
Very nice! That doesnt look like the slice hack though. How are you rendering this? |
| ||
oh my, this thread is VERY interesting, might get my blitz on tomorrow ;) |
| ||
it's a 2d voxel renderer. based on the concepts here:\ http://www.blitzbasic.com/Community/posts.php?topic=106157#1305128 import magica voxel = no problem, custom palette = no problem. Oh wait... bugger, color 0 is never changed load from 1 - fixed! |
| ||
ok, all stuff fixed and added support for up to 8 frames and improved the ui the right ghosted version is the editor working on a vertical slice I can add an exported to the png slice format next ;) |
| ||
Nice work Adam! Official editor for the module haha. Something I'd really like and is missing from MagicaVoxel is the ability to edit several models at once, sort of in layers. For example, I'm working on a game for a client and you play as a truck and the cargo you pull is a separate model, and as it is now, it's very hard to make the two models fit each other. I had to make the model as one and then delete the truck and save as cargo, reload the model and remove the cargo and save the truck. |
| ||
Great work guys. I do like the voxel look. Plenty of life left in 2d yet :) |
| ||
@hezakore it's not using anything from the module - its monkey2 code. ok. how about a feature that shows multiple frames at once? currently the size is 50x50x25 |
| ||
ok, latest added ability to view more than one frame at a time. There are 8 available frames. Here it is showing all four frames from the deer model |
| ||
I'm now working on reversing the process so I can import voxels from the saved slices |
| ||
sorted. Can now read in exported slices and construct the original voxel model with animation. If the filename does not have the correct format name_XxYxZxF.png It will load the png as an image and construct the voxel frame from that. Here you can see a simple image of a green monster thing has been imported and the voxels created. this should make editing more like a paint program. I've also allowed for painting of existing voxels. so you can take an imported model and repaint the colors without adding new voxels |
| ||
Before I get into writing the slice exporter. here's a shot of the new pop-up menu showing the color choices, and quick tool selection You can also see that new tools have appeared. - pencil is draw a voxel in the current color - brush is color any existing voxels with the current color - eyedropper will pick the color of an existing voxel the pop-up colors are darker and lighter (available) color variations of the current color |
| ||
after some weird little stuff Here's the first result loaded, exported and loaded as slices: There was some issues with the way HezKore dealt with multiple frames. so unfortunately I have had to drop slice support in his original format. The new format has the slices grouped in frames horizontally. this makes for very fast referencing with multiple frame exports. The draw routines can be written as you want. I've implemented a quality setting of 1 to 4 as shown above. quality 1 just draws the individual slices, 2 draws 2, 3, 3 and 4 draws 4 slices |
| ||
And here with some shadows and a bit of artistic licence this isn't a moving or blurred shot. it is a static image. I suppose you could use it for a depth of field effect? |
| ||
Finally a quick app to show all the frames and the slice data that created it: |
| ||
That's cool and all AdamStrange but maybe you should start your own thread for the editor. :) Especially now that the editor has nothing to do with my module or even uses the same slice format. And regarding frames and animations, my format just tries to create a square picture and fit all the layers next to each other for each frame. I've made some very detailed voxel models with a lot of frames and it's worked great. In other news! I'm using the Monkey X version of my module for 2 client projects. I'm glad people got interested in this heh. |
| ||
good point. What I will do is add support for import and export both formats. Yours will be _XxYxZxF.png mine will be _XxYxZfF.png so they don't get in the way of each other :) |