Ok where to start, first, let me say I've been working with Irrlicht a number of years now and I'm familiar with a lot of it's drawbacks and such AND that I've been trying desperately NOT to come asking questions here about anything if possible and ONLY after having searched for information FIRST. That being said I have finally gotten truely stuck.
Using ALL of the information that is still available since most of the examples for polyvox existed on thermite3D site that no longer exists (or at least I imagine). eh hem, anyways getting as far as I have so far outside of the obviously copied code wasn't easy and is of my own amaturistic design.
Now I've confused myself...
So here's the Relavent information and code:
1. I've built my own game engine. (if it can be called that) (not used here)
2. I'm attempting to create a sort of demo for my engine in the flesh of a simple minecraft-clone of sorts
3. I'm using Irrlicht. (obvious at this point)
4. I've used code off these and other related forums for my test-case basic framework yada yada.
5. I'm using the current develop branch on whatever git site you host it now (I forget) (bitbucket?)
6. I use bad grammar and parenthesis too much (sorry nazi's)
7. in my test I have an irrlicht FPS style camera, picking from dev branch PV, a very slow system of redrawing the entire mesh atm. (and other stuff I think, shrug)
The CODE: (forgive me it's ugly, I'm leaving in the out commented code to illistrate how difficult this has been... it's been nearly a week now.
(((((((((( )
Code:
#include "main.h"
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(SAppContext & context) : Context(context) { }
virtual bool OnEvent(const SEvent& event)
{
// Remember the mouse state
if (event.EventType == irr::EET_MOUSE_INPUT_EVENT)
{
switch(event.MouseInput.Event)
{
case EMIE_LMOUSE_PRESSED_DOWN:
MouseState.LeftButtonDown = true;
break;
case EMIE_LMOUSE_LEFT_UP:
{
pickBlock(volData,camera->getAbsolutePosition());
recalcMesh(volData);
//MouseState.LeftButtonDown = false;
break;
}
case EMIE_RMOUSE_PRESSED_DOWN:
// MouseState.RightButtonDown = true;
break;
case EMIE_RMOUSE_LEFT_UP:
{
//pickBlock(volData,camera);
//recalcMesh(volData);
camera->setPosition(core::vector3df(0,68,0));
//MouseState.RightButtonDown = false;
break;
}
case EMIE_MOUSE_MOVED:
MouseState.Position.X = event.MouseInput.X;
MouseState.Position.Y = event.MouseInput.Y;
break;
default:
// We won't use the wheel
break;
}
}
if (event.EventType == EET_GUI_EVENT)
{
s32 id = event.GUIEvent.Caller->getID();
//gui::IGUIEnvironment* env =
Context.device->getGUIEnvironment();
switch(event.GUIEvent.EventType)
{
/*
If a button was clicked, it could be one of 'our'
three buttons. If it is the first, we shut down the engine.
If it is the second, we create a little window with some
text on it. We also add a string to the list box to log
what happened. And if it is the third button, we create
a file open dialog, and add also this as string to the list box.
That's all for the event receiver.
*/
case gui::EGET_BUTTON_CLICKED:
switch(id)
{
case GUI_ID_QUIT_BUTTON:
Context.device->closeDevice();
return true;
default:
return false;
}
break;
default:
break;
}
}
return false;
}
private:
SAppContext & Context;
};
irr::scene::SMesh* convertPolyMesh(const PolyVox::SurfaceMesh<PolyVox::PositionMaterialNormal>& meshPoly) {
//irr::scene::SMeshBuffer* buffer =new irr::scene::SMeshBuffer();
irr::scene::IDynamicMeshBuffer* buffer =new irr::scene::CDynamicMeshBuffer(irr::video::EVT_STANDARD, irr::video::EIT_32BIT);
//irr::video::SColor clr(255,111,255,111);
const std::vector<uint32_t>& indices = meshPoly.getIndices();
const std::vector<PolyVox::PositionMaterialNormal>& vertices = meshPoly.getVertices();
buffer->getVertexBuffer().set_used(vertices.size());
for (size_t i = 0; i < vertices.size(); ++i) {
const PolyVox::Vector3DFloat& position = vertices[i].getPosition();
const PolyVox::Vector3DFloat& normal = vertices[i].getNormal();
buffer->getVertexBuffer()[i].Pos.X = position.getX();
buffer->getVertexBuffer()[i].Pos.Y = position.getY();
buffer->getVertexBuffer()[i].Pos.Z = position.getZ();
buffer->getVertexBuffer()[i].Normal.X = normal.getX();
buffer->getVertexBuffer()[i].Normal.Y = normal.getY();
buffer->getVertexBuffer()[i].Normal.Z = normal.getZ();
buffer->getVertexBuffer()[i].Color = irr::video::SColor(100,0,0,255);
}
buffer->getIndexBuffer().set_used(indices.size());
for (size_t i = 0; i < indices.size(); ++i) {
//if(!volData.getVoxelAt())
buffer->getIndexBuffer().setValue(i, indices[i]);
}
//Create mesh
irr::scene::SMesh* mesh =new irr::scene::SMesh;
//irr::video::SMaterial mat;
//buffer->Material=mat;
mesh->addMeshBuffer(buffer);
buffer->drop();
mesh->recalculateBoundingBox();
return mesh;
}
/*
irr::scene::IMeshBuffer* ConvertMesh(const PolyVox::SurfaceMesh<PolyVox::PositionMaterialNormal>& mesh) {
const std::vector<uint32_t>& indices = mesh.getIndices();
const std::vector<PolyVox::PositionMaterialNormal>& vertices = mesh.getVertices();
irr::scene::IDynamicMeshBuffer *mb = new irr::scene::CDynamicMeshBuffer(irr::video::EVT_STANDARD, irr::video::EIT_32BIT);
mb->getVertexBuffer().set_used(vertices.size());
for (size_t i = 0; i < vertices.size(); ++i) {
const PolyVox::Vector3DFloat& position = vertices[i].getPosition();
const PolyVox::Vector3DFloat& normal = vertices[i].getNormal();
mb->getVertexBuffer()[i].Pos.X = position.getX();
mb->getVertexBuffer()[i].Pos.Y = position.getY();
mb->getVertexBuffer()[i].Pos.Z = position.getZ();
mb->getVertexBuffer()[i].Normal.X = normal.getX();
mb->getVertexBuffer()[i].Normal.Y = normal.getY();
mb->getVertexBuffer()[i].Normal.Z = normal.getZ();
mb->getVertexBuffer()[i].Color = irr::video::SColor(255,0,200,200);
//for textures
if(fabsf(normal.getX())>fabsf(normal.getY()) && fabsf(normal.getX())>fabsf(normal.getZ())) {
mb->getVertexBuffer()[i].TCoords.X = position.getY();
mb->getVertexBuffer()[i].TCoords.Y = position.getZ();
}
else if(fabsf(normal.getY())>fabsf(normal.getX()) && fabsf(normal.getY())>fabsf(normal.getZ())) {
mb->getVertexBuffer()[i].TCoords.X = position.getX();
mb->getVertexBuffer()[i].TCoords.Y = position.getZ();
}
else {
mb->getVertexBuffer()[i].TCoords.X = position.getX();
mb->getVertexBuffer()[i].TCoords.Y = position.getY();
}
}
mb->getIndexBuffer().set_used(indices.size());
for (size_t i = 0; i < indices.size(); ++i) {
mb->getIndexBuffer().setValue(i, indices[i]);
}
mb->recalculateBoundingBox();
return mb;
}
*/
void createChunk(SimpleVolume<uint8_t>& volData)//, vector<>& chunk)
{
// uint8_t uDensity = std::numeric_limits<uint8_t>::max();
//This vector hold the position of the center of the volume
//Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
Vector3DInt32 v3dVolEnd(volData.getWidth(), volData.getHeight(), volData.getDepth());
int maxWidth = volData.getWidth()-1;
int maxHeight = volData.getHeight()-1;
int maxDepth = volData.getDepth()-1;
//int x = 0;
//int y = 0;
//This three-level for loop iterates over every voxel in the volume
for (int z = 1; z < maxDepth; z++)
{
for (int y = 1; y < maxHeight; y++)
{
for (int x = 1; x < maxWidth; x++)
{
/*
for (int z = 0; z < volData.getWidth(); z++)
{
for (int y = 0; y < volData.getHeight(); y++)
{
for (int x = 0; x < volData.getDepth(); x++)
{*/
uint8_t uDensity = std::numeric_limits<uint8_t>::max();
//Store our current position as a vector...
//Vector3DFloat v3dCurrentPos(x,y,z);
//And compute how far the current position is from the center of the volume
// float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
// if(v3dCurrentPos < volData.getShortestSideLength())
//if(fDistToCenter <= fRadius)
// {
// uDensity = 0;
//volData.setVoxelAt(x, y, z, uDensity);
// return;}
//else{
//volData.setVoxelAt(x, y, z, uDensity);
if(y <= 60){
volData.setVoxelAt(x, y, z, uDensity);//uDensity);
}
else{
volData.setVoxelAt(x, y, z, 0);//uDensity);
}
//volData.setVoxelAt(v3dVolCenter, uDensity);
// }
}
}
}
/*
uint8_t uDensity = std::numeric_limits<uint8_t>::max();
volData.setVoxelAt(0,0,0, uDensity);
v3dVolCenter.setX(v3dVolCenter.getX()+1);
v3dVolCenter.setY(v3dVolCenter.getY()+1);
v3dVolCenter.setZ(v3dVolCenter.getZ()+1);
volData.setVoxelAt(v3dVolCenter, uDensity);*/
}
/*
void createSphereInVolume(SimpleVolume<uint8_t>& volData, float fRadius)
{
//This vector hold the position of the center of the volume
Vector3DFloat v3dVolCenter(volData.getWidth() / 2, volData.getHeight() / 2, volData.getDepth() / 2);
//This three-level for loop iterates over every voxel in the volume
for (int z = 0; z < volData.getDepth(); z++)
{
for (int y = 0; y < volData.getHeight(); y++)
{
for (int x = 0; x < volData.getWidth(); x++)
{
//Store our current position as a vector...
Vector3DFloat v3dCurrentPos(x,y,z);
//And compute how far the current position is from the center of the volume
float fDistToCenter = (v3dCurrentPos - v3dVolCenter).length();
if(fDistToCenter <= fRadius)
{
//Our new density value
//uint8_t uDensity = Density8::getmaxDensity()();
uint8_t uDensity = std::numeric_limits<uint8_t>::max();
//Get the old voxel
//uint8_t voxel = volData.getVoxelAt(x,y,z);
//Modify the density
//voxel.setDensity(uDensity);
//Wrte the voxel value into the volume
volData.setVoxelAt(x, y, z, uDensity);
}
//144 in the middle, (144 - 32) at the edges. Threshold of 128 is between these
//volData.setVoxelAt(x, y, z, 144 - fDistToCenter);
}
}
}
}
*/
void pickBlock(SimpleVolume<uint8_t>& volData,core::vector3df v3d)
{
//camera->updateAbsolutePosition();
Vector3DFloat rayOrigin;
rayOrigin.setX(v3d.X);
rayOrigin.setY(v3d.Y);
rayOrigin.setZ(v3d.Z);
core::vector3df target = (camera->getTarget() - camera->getPosition());
Vector3DFloat rayDir;
rayDir.setX(target.X);
rayDir.setY(target.Y);
rayDir.setZ(target.Z);
Vector3DFloat start(rayOrigin.getX(), rayOrigin.getY(), rayOrigin.getZ());
Vector3DFloat direction(rayDir.getX(), rayDir.getY(), rayDir.getZ());
//start.normalise();
direction.normalise();
direction *= 9999.0f; //Casts ray of length 1000
std::cout << start << direction << std::endl;
// const int32_t uVolumeSideLength = 32;
const int8_t emptyVoxelExample = 0; //A voxel value of zero will represent empty space.
//PolyVox::PickResult resultHit = pickVoxel(&volData, Vector3DFloat(0, uVolumeSideLength / 2, uVolumeSideLength / 2), Vector3DFloat(uVolumeSideLength, 0, 0), emptyVoxelExample);
PolyVox::PickResult resultHit = pickVoxel(&volData, start, direction, emptyVoxelExample);
if(resultHit.didHit)
{
//resultHit.hitVoxel.normalise();
volData.setVoxelAt(resultHit.hitVoxel,emptyVoxelExample);
recalcMesh(volData);
}
}
void calcMesh(SimpleVolume<uint8_t>& volData)
{
//Transform voxel into mesh
PolyVox::CubicSurfaceExtractorWithNormals< PolyVox::SimpleVolume<uint8_t> >
surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
//PolyVox::MarchingCubesSurfaceExtractor< PolyVox::SimpleVolume<uint8_t> >
surfaceExtractor(&volData, volData.getEnclosingRegion(), &mesh);
surfaceExtractor.execute();
//Extract the surface
//SurfaceMesh<PositionMaterialNormal> meshLowLOD;
//MarchingCubesSurfaceExtractor< RawVolume<uint8_t> > surfaceExtractor(&volDataLowLOD, volDataLowLOD.getEnclosingRegion(), &meshLowLOD);
//surfaceExtractor.execute();
//meshLowLOD.scaleVertices(/*2.0f*/63.0f / 31.0f);
//Extract the surface
//SurfaceMesh<PositionMaterialNormal> meshHighLOD;
//MarchingCubesSurfaceExtractor< SimpleVolume<uint8_t> > surfaceExtractorHigh(&volData, PolyVox::Region(Vector3DInt32(30,0,0), Vector3DInt32(63, 63, 63)), &meshHighLOD);
//surfaceExtractorHigh.execute();
//meshHighLOD.translateVertices(Vector3DFloat(30, 0, 0));
//Transform mesh into irrlicht mesh
//irr::scene::IMeshSceneNode* levelMesh =
device->getSceneManager()->addMeshSceneNode(convertPolyMesh(mesh),0,26);
}
void recalcMesh(SimpleVolume<uint8_t>& volData)
{
scene::ISceneNode* SNP = device->getSceneManager()->getSceneNodeFromId(26);
SNP->removeAll();
// PolyVox::VolumeResampler<> vlrs = resampleSameSize()
calcMesh(volData);
//createChunk(volData);
}
int main(int argc, char**argv)
{
// ask user for driver
//video::E_DRIVER_TYPE driverType = video::EDT_OPENGL; // video::driverChoiceConsole(); if (driverType==video::EDT_COUNT)
//return 1;
// create device and exit if creation failed
device =
//createDevice(driverType, core::dimension2d<u32>(640, 480));
createDevice(video::EDT_OPENGL, core::dimension2d<u32>(800, 600));
if (device == 0)
return 1; // could not create selected driver.
/*
Set the caption of the window to some nice text. Note that there is an
'L' in front of the string. The Irrlicht Engine uses wide character
strings when displaying text.
*/
device->setWindowCaption(L"Hello World! - Irrlicht Engine Demo");
/*
Get a pointer to the VideoDriver, the SceneManager and the graphical
user interface environment, so that we do not always have to write
device->getVideoDriver(), device->getSceneManager(), or
device->getGUIEnvironment().
*/
irr::video::IVideoDriver* driver = device->getVideoDriver();
irr::scene::ISceneManager* smgr = device->getSceneManager();
irr::gui::IGUIEnvironment* guienv = device->getGUIEnvironment();
SKeyMap keyMap[9];
keyMap[0].Action = EKA_MOVE_FORWARD;
keyMap[0].KeyCode = KEY_UP;
keyMap[1].Action = EKA_MOVE_FORWARD;
keyMap[1].KeyCode = KEY_KEY_W;
keyMap[2].Action = EKA_MOVE_BACKWARD;
keyMap[2].KeyCode = KEY_DOWN;
keyMap[3].Action = EKA_MOVE_BACKWARD;
keyMap[3].KeyCode = KEY_KEY_S;
keyMap[4].Action = EKA_STRAFE_LEFT;
keyMap[4].KeyCode = KEY_LEFT;
keyMap[5].Action = EKA_STRAFE_LEFT;
keyMap[5].KeyCode = KEY_KEY_A;
keyMap[6].Action = EKA_STRAFE_RIGHT;
keyMap[6].KeyCode = KEY_RIGHT;
keyMap[7].Action = EKA_STRAFE_RIGHT;
keyMap[7].KeyCode = KEY_KEY_D;
keyMap[8].Action = EKA_JUMP_UP;
keyMap[8].KeyCode = KEY_KEY_J;
camera = smgr->addCameraSceneNodeFPS(0, 100.0f, 0.01f, -1, keyMap, 9, false, 3.f);
//camera = smgr->addCameraSceneNodeFPS();
smgr->setAmbientLight(video::SColorf(1.0f,1.0f,1.0f));
/*
We add a hello world label to the window, using the GUI environment.
The text is placed at the position (10,10) as top left corner and
(260,22) as lower right corner.
*/
guienv->addStaticText(L"Hello World! This is the Irrlicht Software renderer!",
irr::core::rect<s32>(10,10,260,22), true);
//Create and show the Qt OpenGL window
//QApplication app(argc, argv);
//OpenGLWidget openGLWidget(0);
//openGLWidget.show();
// region-rings-factor formula
// an = 1-9-16-24-32-40-48
// sf = 1-3-5-7-9-11-13-15-17-19-21
// an = (sf x 4) + 4 // +4 corners ;)
//createVol();
camera->setPosition(core::vector3df(0,70,0));
camera->updateAbsolutePosition();
//createSphereInVolume(volData, 28);
// place a chunk in it
createChunk(volData);
calcMesh(volData);
//createChunk(createVol());
//camera->setTarget(levelMesh->getAbsolutePosition());
/*
PolyVox::RaycastResults::RaycastResult rs;
PolyVox::raycastWithDirection(volData,camera->getAbsolutePosition(),camera->getTarget());
if(rs->Interupted())
{
volData->getVoxelAt(camera->getTarget());
}
*/
//QCOMPARE(resultHit.previousVoxel, Vector3DInt32((uVolumeSideLength / 2), uVolumeSideLength / 2, uVolumeSideLength / 2));
//PickResult resultMiss = pickVoxel(&volData, Vector3DFloat(0, uVolumeSideLength / 2, uVolumeSideLength / 2), Vector3DFloat(uVolumeSideLength / 2, uVolumeSideLength, uVolumeSideLength), emptyVoxelExample);
//QCOMPARE(resultMiss.didHit, false);
//RaycastResult raycastTestFunctor;
/*class raycastTestFunctor
{
public:
void operator()()
{
// true, 1 if a < b, false otherwise
return;
}
};*/
//RaycastResult rcResult = PolyVox::raycastWithDirection(&volData, start, direction, raycastTestFunctor);
//RaycastResult result = raycastWithDirection(
//&volData, start, direction, raycastTestFunctor);
/*
if(rcResult == RaycastResults::Interupted)
{
//iend
//SceneMgr->getSceneNode("mEditNode")->setPosition(raycastResult.intersectionVoxel.getX(), raycastResult.intersectionVoxel.getY(), raycastResult.intersectionVoxel.getZ());
// volData.getVoxelAt();
}
*/
//if(rcResult == RaycastResult::Interupted)
//{
//}
//Smooth the data - should reimplement this using LowPassFilter
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
//smoothRegion<SimpleVolume, Density8>(volData, volData.getEnclosingRegion());
//RawVolume<uint8_t> volDataLowLOD(PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(15, 31, 31)));
//VolumeResampler<SimpleVolume<uint8_t>, RawVolume<uint8_t> > volumeResampler(&volData, PolyVox::Region(Vector3DInt32(0,0,0), Vector3DInt32(31, 63, 63)), &volDataLowLOD, volDataLowLOD.getEnclosingRegion());
//volumeResampler.execute();
// Store the appropriate data in a context structure.
SAppContext context;
context.device = device;
//context.counter = 0;
//context.listbox = listbox;
// Then create the event receiver, giving it that context structure.
MyEventReceiver receiver(context);
// And tell the device to use our custom event receiver.
device->setEventReceiver(&receiver);
if(MouseState.LeftButtonDown)
{
}
while(device->run() && driver)
{
// Catch focus changes (workaround until Irrlicht has events for this)
// bool focused = device->isWindowFocused();
//if ( hasFocus && !focused )
// onKillFocus();
//hasFocus = focused;
if (device->isWindowActive())
{
driver->beginScene(true, true, video::SColor(150,50,50,50));
smgr->drawAll();
guienv->drawAll();
driver->endScene();
}
else
device->yield();
}
//Pass the surface to the OpenGL window
//openGLWidget.setSurfaceMeshToRender(meshHighLOD);
//openGLWidget.setSurfaceMeshToRenderLowLOD(meshLowLOD);
//Run the message pump.
//return app.exec();
device->drop();
return 0;
}
And now for the actually issue. Everything "sort of" works, the picking is currently working in odd ways but it works. but the problem is the surface extraction. If I pick and extract and redraw the mesh the original giant cube of voxel cubes doesn't change at all, as though surface extraction is giving me bad index references.
Perhaps this is all the information you need to explain your voxel engine. So for now I'll just post this bad thread on your wonderful forum and pray for a fix. If you have trouble understanding my problem I can post video or screenshots if you like rather than anyone having to build my example.
Thanks in advance. Any constructive criticism, help, ideas, or code would be greatly appreciated as well.
[update] little edit, I've fixed up the code a little bit to decrease the page resizing so large and removed some of the more completely irrelavent code for easier reading and comprehension.
Edited this again, this time I've added an image to help explain the problem