How save and load scene with texture
How save and load scene with texture
[img]http://s011.radikal.ru/i315/1511/e2/ff87080a553a.png
[img]http://s019.radikal.ru/i636/1511/6c/45db1eb883b3.png
Hello! I am beginner. I writing code on c++. My scene include new objects (adding same spheres) wich are creating in the scene while scene runing. Also scene including node, terrain and skydome with textures. Please help me with saving scene state (with textures and new added objects) into .irr. And further loading scene from .irr while scene run.
[img]http://s019.radikal.ru/i636/1511/6c/45db1eb883b3.png
Hello! I am beginner. I writing code on c++. My scene include new objects (adding same spheres) wich are creating in the scene while scene runing. Also scene including node, terrain and skydome with textures. Please help me with saving scene state (with textures and new added objects) into .irr. And further loading scene from .irr while scene run.
Re: How save and load scene with texture
Hi, we need a little bit more information about what you are doing and what is happening before we can help:
Did you try ISceneManager::saveScene? Which parts are not saved&loaded? If you can give us a quick example to reproduce that it would be nice. Otherwise at least the .irr file and a detailed description which parts are not working.
Did you try ISceneManager::saveScene? Which parts are not saved&loaded? If you can give us a quick example to reproduce that it would be nice. Otherwise at least the .irr file and a detailed description which parts are not working.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: How save and load scene with texture
I using Irrlicht in my study in architecture theory. I explore some questions of the perception of architectural form.
Re: How save and load scene with texture
Hehe, I didn't mean what you use it for but _how_ you use it :-) So the code you are using so far and which doesn't seem to work. As well as the other questions I asked.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: How save and load scene with texture
Thanks! I can post my code. But all working. I posted picture link with error and console log.
This is plaсe where application hanging:
scenemgr->loadScene("../../media/myscene.irr",0 ,0);
Me need halp with save scene state with texture by default way. It is possible or i must write Serializer for this?
This is plaсe where application hanging:
scenemgr->loadScene("../../media/myscene.irr",0 ,0);
Me need halp with save scene state with texture by default way. It is possible or i must write Serializer for this?
Re: How save and load scene with texture
Usually you do not have to do anything special. If you add a smgr->saveScene("somefilename.irr"); in example 15 and then load that again the textures are there.
But I can't tell what is going wrong in your case without knowing your code or at least seeing the corresponding .irr file. Right now I can't even tell if it's a problem on saving or loading as I can't see from your screenshots which information is inside the .irr file.
But I can't tell what is going wrong in your case without knowing your code or at least seeing the corresponding .irr file. Right now I can't even tell if it's a problem on saving or loading as I can't see from your screenshots which information is inside the .irr file.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: How save and load scene with texture
//Still hangs on load:((
#include <irrlicht.h>
#include <Windows.h>
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
bool tracking1 = false;
bool scenesave = false;
bool sceneload = false;
irr::core::stringw str = "Keyboard: ";
class MySerializer : public ISceneUserDataSerializer{
private:
IFileSystem* Filesys;
IVideoDriver* Driver;
public:
//! called for each node loaded from the file
void OnReadUserData(ISceneNode* forSceneNode, io::IAttributes* userData){
// here you can save additional data for each node
// maybe get the data from an array and use the node's ID as index ???
switch(forSceneNode->getType()){
case ESNT_ANIMATED_MESH:{
stringc str = userData->getAttributeAsString("TestName");
printf("name: %s\n", str.c_str());
}break;
}
}
//! called for each node saved to the file
IAttributes* createUserData(ISceneNode* forSceneNode){
// here you can load additional data for each node
// maybe store the data in an array and use the node's ID as index ???
switch(forSceneNode->getType()){
case ESNT_ANIMATED_MESH:{
IAttributes* atr = Filesys->createEmptyAttributes(Driver);
atr->addString("TestName","name of the mesh");
return atr;
}break;
}
return 0;
}
//! Constructor
MySerializer(IrrlichtDevice* dev){
if(dev){
Filesys = dev->getFileSystem();
Driver = dev->getVideoDriver();
}
}
};
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(){
}
virtual ~MyEventReceiver(){}
// you may need to change the parameter type depending on your Irrlicht version
virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
//if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
//{
// Cursor.X = event.MouseInput.X;
//Cursor.Y = event.MouseInput.Y;
//}
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
{
tracking1 = true;
}
//else{tracking1 = false;}
if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
{
tracking1 = false;
}
}
else if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) {
switch (event.KeyInput.Key) {
// switch terrain wireframe
case irr::KEY_KEY_S: {
scenesave = true;
return true;
}
case irr::KEY_KEY_L: {
sceneload = true;
return true;
}
default:
break;
}
}
return false;
}
};
int main(int argc, char** argv)
{
// start up the engine
int screenW=GetSystemMetrics(SM_CXSCREEN);//Получить ширину экрана
int screenH=GetSystemMetrics(SM_CYSCREEN);//Получить высоту экрана
IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
core::dimension2d<u32>(screenW,screenH),32,false);
device->getCursorControl()->setVisible(false);
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* scenemgr = device->getSceneManager();
// MySerializer serializer(device); // create the serializer
gui::IGUIEnvironment* env = device->getGUIEnvironment();
scene::ISceneNode* skydome=scenemgr->addSkyDomeSceneNode(driver->getTexture("../../media/sky.png"),32,16,0.95f,16.0f);
device->setWindowCaption(L"Hello World!");
// load and show quake2 .md2 model
scene::IAnimatedMeshSceneNode* node = scenemgr->addAnimatedMeshSceneNode(
scenemgr->getMesh("../../media/Clermont2.3ds"));
scene::ITerrainSceneNode* terrain = scenemgr->addTerrainSceneNode(
"../../media/terr.png",
0, // parent node
-1, // node id
core::vector3df(-50000.f, 0.f, -50000.f), // position
core::vector3df(0.f, 0.0f, 0.f), // rotation
core::vector3df(10000.f, 0.f, 10000.f), // scale
video::SColor ( 255, 200, 200, 200 ), // vertexColor
5, // maxLOD
scene::ETPS_17, // patchSize
0 // smoothFactor
);
terrain->setMaterialTexture(1, driver->getTexture("../../media/wall.jpg"));
terrain->setMaterialType(video::EMT_LIGHTMAP_LIGHTING);
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
terrain->scaleTexture(1.0f, 2000.0f);
// if everything worked, add a texture and disable lighting
if (node)
{
node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialFlag(video::EMF_LIGHTING, false);
}
// add a first person shooter style user controlled camera
irr::scene::ICameraSceneNode* camera = scenemgr->addCameraSceneNodeFPS(0,100.0f,2.0f,-1,0,0,true,4.0f,false,true);
camera->setPosition(core::vector3df(-5000.0f,1750.0f,0.0f));
camera->setFarValue(10000000);
camera->setVisible(true);
camera->render();
scene::ITriangleSelector* selector = scenemgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
scene::ISceneCollisionManager* collMan = scenemgr->getSceneCollisionManager();
video::ITexture* stexture = driver->addTexture(core::dimension2d<u32>(1,1), "texture", video::ECF_A8R8G8B8);
s32* p = (s32*)stexture->lock();
p[0] = video::SColor(255,255,0,0).color;
stexture->unlock();
MyEventReceiver* receiver = new MyEventReceiver();
device->setEventReceiver(receiver);
while(device->run())
{
if (device->isWindowActive()) {
if (sceneload==true)
{
sceneload=false;
if (argc>1)
scenemgr->loadScene(argv[1]);
else
scenemgr->loadScene("myscene.irr",0 ,0);
}
if (scenesave==true)
{
scenesave=false;
scenemgr->saveScene("myscene.irr",0,0);
}
driver->beginScene(true, true, video::SColor(255,126,126,126));
scenemgr->drawAll();
// Create decal manager
irr::core::triangle3df triangle;
irr::scene::ISceneNode* node3 = 0;
irr::core::vector3df collisionPoint;
irr::core::vector3df NullPoint = core::vector3df(0.f, 0.f, 0.f);
irr::core::line3df line = irr::core::line3df(camera->getPosition(), camera->getTarget());
scene::ISceneNode * selectedSceneNode =
collMan->getSceneNodeAndCollisionPointFromRay(
line,
collisionPoint, // This will be the position of the collision
triangle, // This will be the triangle hit in the collision
0, // This ensures that only nodes that we have
// set up to be pickable are considered
0); // Check the entire scene (this is actually the implicit default)
// Sphere
// create empty texture of 1 pixel size in 32 bit A8R8G8B8 color format
if (tracking1==true&&collisionPoint!=NullPoint)
{
scene::IMeshSceneNode* sphere = scenemgr->addSphereSceneNode(50.f, ;
sphere->setMaterialTexture(0,stexture);
sphere->setMaterialType(video::EMT_LIGHTMAP_LIGHTING);
sphere->setMaterialFlag(video::EMF_LIGHTING, false);
sphere->setPosition(collisionPoint);
}
irr::core::stringw str2 = "CP: ";
str2 += "X:";
str2 += (irr::f32) collisionPoint.X;
str2 +=" , Y" ;
str2+=(irr::f32) collisionPoint.Y;
str2+= " , Z";
str2+=(irr::f32) collisionPoint.Z;
irr::core::stringw str3 = "Tracking: ";
if (tracking1 == true){str3 += "True";}
if (tracking1 == false){str3 += "False";}
//irr::core::stringw str = "ARCHITECTONIC EPERIMENT BY G.KOZLOV";
env->getSkin()->getFont()->draw(str.c_str(), irr::core::rect<s32>(10, 10, 200, 20), video::SColor(255,255,255,255), false, true);
env->getSkin()->getFont()->draw(str2, irr::core::rect<s32>(10, 30, 200, 20), video::SColor(255,255,255,255), false, true);
env->getSkin()->getFont()->draw(str3, irr::core::rect<s32>(10, 50, 200, 20), video::SColor(255,255,255,255), false, true);
device->setWindowCaption(str.c_str());
driver->endScene();
}
}
// delete device
device->drop();
//ShowCursor(false);
return 0;
}
#include <irrlicht.h>
#include <Windows.h>
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
bool tracking1 = false;
bool scenesave = false;
bool sceneload = false;
irr::core::stringw str = "Keyboard: ";
class MySerializer : public ISceneUserDataSerializer{
private:
IFileSystem* Filesys;
IVideoDriver* Driver;
public:
//! called for each node loaded from the file
void OnReadUserData(ISceneNode* forSceneNode, io::IAttributes* userData){
// here you can save additional data for each node
// maybe get the data from an array and use the node's ID as index ???
switch(forSceneNode->getType()){
case ESNT_ANIMATED_MESH:{
stringc str = userData->getAttributeAsString("TestName");
printf("name: %s\n", str.c_str());
}break;
}
}
//! called for each node saved to the file
IAttributes* createUserData(ISceneNode* forSceneNode){
// here you can load additional data for each node
// maybe store the data in an array and use the node's ID as index ???
switch(forSceneNode->getType()){
case ESNT_ANIMATED_MESH:{
IAttributes* atr = Filesys->createEmptyAttributes(Driver);
atr->addString("TestName","name of the mesh");
return atr;
}break;
}
return 0;
}
//! Constructor
MySerializer(IrrlichtDevice* dev){
if(dev){
Filesys = dev->getFileSystem();
Driver = dev->getVideoDriver();
}
}
};
class MyEventReceiver : public IEventReceiver
{
public:
MyEventReceiver(){
}
virtual ~MyEventReceiver(){}
// you may need to change the parameter type depending on your Irrlicht version
virtual bool OnEvent(const SEvent& event)
{
if (event.EventType == EET_MOUSE_INPUT_EVENT)
{
//if(event.MouseInput.Event == EMIE_MOUSE_MOVED)
//{
// Cursor.X = event.MouseInput.X;
//Cursor.Y = event.MouseInput.Y;
//}
if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
{
tracking1 = true;
}
//else{tracking1 = false;}
if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
{
tracking1 = false;
}
}
else if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) {
switch (event.KeyInput.Key) {
// switch terrain wireframe
case irr::KEY_KEY_S: {
scenesave = true;
return true;
}
case irr::KEY_KEY_L: {
sceneload = true;
return true;
}
default:
break;
}
}
return false;
}
};
int main(int argc, char** argv)
{
// start up the engine
int screenW=GetSystemMetrics(SM_CXSCREEN);//Получить ширину экрана
int screenH=GetSystemMetrics(SM_CYSCREEN);//Получить высоту экрана
IrrlichtDevice *device = createDevice(video::EDT_OPENGL,
core::dimension2d<u32>(screenW,screenH),32,false);
device->getCursorControl()->setVisible(false);
video::IVideoDriver* driver = device->getVideoDriver();
scene::ISceneManager* scenemgr = device->getSceneManager();
// MySerializer serializer(device); // create the serializer
gui::IGUIEnvironment* env = device->getGUIEnvironment();
scene::ISceneNode* skydome=scenemgr->addSkyDomeSceneNode(driver->getTexture("../../media/sky.png"),32,16,0.95f,16.0f);
device->setWindowCaption(L"Hello World!");
// load and show quake2 .md2 model
scene::IAnimatedMeshSceneNode* node = scenemgr->addAnimatedMeshSceneNode(
scenemgr->getMesh("../../media/Clermont2.3ds"));
scene::ITerrainSceneNode* terrain = scenemgr->addTerrainSceneNode(
"../../media/terr.png",
0, // parent node
-1, // node id
core::vector3df(-50000.f, 0.f, -50000.f), // position
core::vector3df(0.f, 0.0f, 0.f), // rotation
core::vector3df(10000.f, 0.f, 10000.f), // scale
video::SColor ( 255, 200, 200, 200 ), // vertexColor
5, // maxLOD
scene::ETPS_17, // patchSize
0 // smoothFactor
);
terrain->setMaterialTexture(1, driver->getTexture("../../media/wall.jpg"));
terrain->setMaterialType(video::EMT_LIGHTMAP_LIGHTING);
terrain->setMaterialFlag(video::EMF_LIGHTING, false);
terrain->scaleTexture(1.0f, 2000.0f);
// if everything worked, add a texture and disable lighting
if (node)
{
node->setMaterialTexture(0, driver->getTexture("../../media/wall.jpg"));
node->setMaterialType(video::EMT_TRANSPARENT_ADD_COLOR);
node->setMaterialFlag(video::EMF_LIGHTING, false);
}
// add a first person shooter style user controlled camera
irr::scene::ICameraSceneNode* camera = scenemgr->addCameraSceneNodeFPS(0,100.0f,2.0f,-1,0,0,true,4.0f,false,true);
camera->setPosition(core::vector3df(-5000.0f,1750.0f,0.0f));
camera->setFarValue(10000000);
camera->setVisible(true);
camera->render();
scene::ITriangleSelector* selector = scenemgr->createTriangleSelector(node);
node->setTriangleSelector(selector);
selector->drop();
scene::ISceneCollisionManager* collMan = scenemgr->getSceneCollisionManager();
video::ITexture* stexture = driver->addTexture(core::dimension2d<u32>(1,1), "texture", video::ECF_A8R8G8B8);
s32* p = (s32*)stexture->lock();
p[0] = video::SColor(255,255,0,0).color;
stexture->unlock();
MyEventReceiver* receiver = new MyEventReceiver();
device->setEventReceiver(receiver);
while(device->run())
{
if (device->isWindowActive()) {
if (sceneload==true)
{
sceneload=false;
if (argc>1)
scenemgr->loadScene(argv[1]);
else
scenemgr->loadScene("myscene.irr",0 ,0);
}
if (scenesave==true)
{
scenesave=false;
scenemgr->saveScene("myscene.irr",0,0);
}
driver->beginScene(true, true, video::SColor(255,126,126,126));
scenemgr->drawAll();
// Create decal manager
irr::core::triangle3df triangle;
irr::scene::ISceneNode* node3 = 0;
irr::core::vector3df collisionPoint;
irr::core::vector3df NullPoint = core::vector3df(0.f, 0.f, 0.f);
irr::core::line3df line = irr::core::line3df(camera->getPosition(), camera->getTarget());
scene::ISceneNode * selectedSceneNode =
collMan->getSceneNodeAndCollisionPointFromRay(
line,
collisionPoint, // This will be the position of the collision
triangle, // This will be the triangle hit in the collision
0, // This ensures that only nodes that we have
// set up to be pickable are considered
0); // Check the entire scene (this is actually the implicit default)
// Sphere
// create empty texture of 1 pixel size in 32 bit A8R8G8B8 color format
if (tracking1==true&&collisionPoint!=NullPoint)
{
scene::IMeshSceneNode* sphere = scenemgr->addSphereSceneNode(50.f, ;
sphere->setMaterialTexture(0,stexture);
sphere->setMaterialType(video::EMT_LIGHTMAP_LIGHTING);
sphere->setMaterialFlag(video::EMF_LIGHTING, false);
sphere->setPosition(collisionPoint);
}
irr::core::stringw str2 = "CP: ";
str2 += "X:";
str2 += (irr::f32) collisionPoint.X;
str2 +=" , Y" ;
str2+=(irr::f32) collisionPoint.Y;
str2+= " , Z";
str2+=(irr::f32) collisionPoint.Z;
irr::core::stringw str3 = "Tracking: ";
if (tracking1 == true){str3 += "True";}
if (tracking1 == false){str3 += "False";}
//irr::core::stringw str = "ARCHITECTONIC EPERIMENT BY G.KOZLOV";
env->getSkin()->getFont()->draw(str.c_str(), irr::core::rect<s32>(10, 10, 200, 20), video::SColor(255,255,255,255), false, true);
env->getSkin()->getFont()->draw(str2, irr::core::rect<s32>(10, 30, 200, 20), video::SColor(255,255,255,255), false, true);
env->getSkin()->getFont()->draw(str3, irr::core::rect<s32>(10, 50, 200, 20), video::SColor(255,255,255,255), false, true);
device->setWindowCaption(str.c_str());
driver->endScene();
}
}
// delete device
device->drop();
//ShowCursor(false);
return 0;
}
Re: How save and load scene with texture
Screenshot:
[img][http://s009.radikal.ru/i307/1511/66/817849677197.png]
.irr file:
[url][http://www.fast-files.com/getfile.aspx?file=101558]
[img][http://s009.radikal.ru/i307/1511/66/817849677197.png]
.irr file:
[url][http://www.fast-files.com/getfile.aspx?file=101558]
Re: How save and load scene with texture
*sigh* one of those days the forum is down constantly and I never know if my posts go through :-/
It loads everything - so you have the whole scene twice afterwards. That it doesn't move anymore has do to with the camera. You have 2 now - as the old one is not cleared. But even clearing it the loaded cameras doesn't seem to work. I'll have to debug that.
But you can remove the camera before saving and create it new after loading as workaround for now.
I don't see any troubles with textures and model loading. But I had to make a few modifications as I don't have your textures/models so I used some from the Irrlicht folder instead (room.3ds and terrain-texture.jpg).
It loads everything - so you have the whole scene twice afterwards. That it doesn't move anymore has do to with the camera. You have 2 now - as the old one is not cleared. But even clearing it the loaded cameras doesn't seem to work. I'll have to debug that.
But you can remove the camera before saving and create it new after loading as workaround for now.
I don't see any troubles with textures and model loading. But I had to make a few modifications as I don't have your textures/models so I used some from the Irrlicht folder instead (room.3ds and terrain-texture.jpg).
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Re: How save and load scene with texture
Thank you CuteAlient! ) All working! I crete new camera after loadScene.
scenemgr->loadScene("myscene.irr",0 ,0);
irr::scene::ICameraSceneNode* camera = scenemgr->addCameraSceneNodeFPS(0,100.0f,2.0f,-1,0,0,true,4.0f,false,true);
camera->setPosition(core::vector3df(-5000.0f,1750.0f,0.0f));
camera->setFarValue(10000000);
camera->setVisible(true);
camera->render();
Further may be I must thinking more about texture and other links in CodeBlocks. I programming in CobeBlocks and in Irrlicht and on c++ only 5 days. So I'm real beginner )) And more thanks you again!!!
scenemgr->loadScene("myscene.irr",0 ,0);
irr::scene::ICameraSceneNode* camera = scenemgr->addCameraSceneNodeFPS(0,100.0f,2.0f,-1,0,0,true,4.0f,false,true);
camera->setPosition(core::vector3df(-5000.0f,1750.0f,0.0f));
camera->setFarValue(10000000);
camera->setVisible(true);
camera->render();
Further may be I must thinking more about texture and other links in CodeBlocks. I programming in CobeBlocks and in Irrlicht and on c++ only 5 days. So I'm real beginner )) And more thanks you again!!!
Re: How save and load scene with texture
Beginner and you already posted an example which showed me 2(!) different bugs in Irrlicht :-)
- First one was even caused by me years ago. Camera-serialization is not activating camera due to a copy-paste bug where it looks for the wrong name on loading.
- Also I noticed the fps-animator does not serialize it's values (like speed) at all.
Already too late for me to work on that today (first one needs to be fixed back in Irrlicht 1.7 - really old bug), but I'll fix them on the weekend. I guess not many people tried serializing camera so far... or everyone just worked around it and thought camera serialization shouldn't work.
edit: Both bugs fixed now in svn trunk.
- First one was even caused by me years ago. Camera-serialization is not activating camera due to a copy-paste bug where it looks for the wrong name on loading.
- Also I noticed the fps-animator does not serialize it's values (like speed) at all.
Already too late for me to work on that today (first one needs to be fixed back in Irrlicht 1.7 - really old bug), but I'll fix them on the weekend. I guess not many people tried serializing camera so far... or everyone just worked around it and thought camera serialization shouldn't work.
edit: Both bugs fixed now in svn trunk.
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm