Here is the second half..
Code: Select all
int main()
{// Determine your windows screen resolution..
TheIRRNullDevice = createDevice(video::EDT_NULL); // Null Device for us to get our windows resolution..
TheDeskResolution = TheIRRNullDevice->getVideoModeList()->getDesktopResolution(); // As set by you in windows setup..
TheIRRNullDevice->drop(); // We got the desktop resolution, so drop the Null Device..
MyEventReceiver TheReceiver; // This receiver is the one assigned in the CREATE DEVICE function..
printf("Select the DriverType:\n"\
" (a) OpenGL \n (b) Direct3D \n (otherKey) exit\n\n");
char inkeypress;
std::cin >> inkeypress;
switch(inkeypress)
{case 'a': SelectedDriverType = video::EDT_OPENGL; break;
case 'b': SelectedDriverType = video::EDT_DIRECT3D9; break;
default: return 1;
}
TheDevice = createDevice(SelectedDriverType, TheDeskResolution, 32, false, false, false, &TheReceiver);
if (TheDevice == 0) return 1;
video::IVideoDriver* TheDriver = TheDevice->getVideoDriver();
scene::ISceneManager* TheSceneManager = TheDevice->getSceneManager();
video::IGPUProgrammingServices* TheGPUProgrammingServices = TheDriver->getGPUProgrammingServices(); // Whether GLSL or HLSL..
// - VARIABLE INITIALISATION -
// even coded into your shaders..
// BckGrndCol = SColor( 255 , 207 , 211 , 192 );
BckGrndCol = SColor( 255 , 0 , 0 , 0 );
TanUpdStatus = true; // Turn this off to see how skinned animation could mess with tangents and thus the final normals..
// There is a lot that can be said about this...
ActiveLightCount = 4; // NEVER to exceed "MAX_LIGHT_COUNT"..(try smaller values to see less lights)
CurrentAnimSpeed = 10.0; // Animating your objects in Max or Blender etc is a big subject not covered here.. (see "Realtime Rant" posted elsewhere) ..
ResetShaderArrays (); // Here resetting means "Set Defaults"..(see the definition further down)..
// Add the camera controls.. "WASD" or "ARROW KEYS"..
SKeyMap keyMap[8];
keyMap[0].Action = EKA_MOVE_FORWARD; keyMap[0].KeyCode = KEY_UP; // Arrow Keys AND "WASD"..
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;
// Static and Realtime TEXT on screen becomes very usefull during development, so I left this here..
IGUIEnvironment* OurGUIEnvironment = TheDevice->getGUIEnvironment();
// These are the variables used to return "static" and "realtime" info via the GUI to the rendered screen..
// Some values (no change) are updated once outside of the main loop and some (changing) are updated at every Nth frame..
u16 StartGuiID = 100 ; // 100 to make space for other possible eventualities..
u16 RunningGuiID = StartGuiID; // Used in text values "assigned once" and in text "changed cyclically" during the animation..
u16 ItemYPosA = 10 ; // The Starting Y Position of our TextBoxes..
u16 ItemYPosB = 20 ; // The Starting Y Extent of our Textboxes..
u16 ItemWidth = 400 ; // This is the Width of the Textboxes..
u32 ItemCount = 4 ; // Be sure not to exceed this value as used in the array definition below..
// Note how the pointer doesnt get acessed again for the later setting of text, but rather the ID assigned to the item..
IGUIStaticText* TGuiItemArray [ 4 ]; // Should be the same as "ItemCount" above..
//=========================================================
// - Create the text areas for our GUI Items -
// This happens ONCE and not cyclically..
// The updating of the text can happen once, before or cyclically during animation..
for (u16 GuiItI = 0 ; GuiItI < ItemCount ; GuiItI++)
{TGuiItemArray [GuiItI] = OurGUIEnvironment->addStaticText(L"---",
rect<s32>(10,ItemYPosA, 600,ItemYPosB),
false,
false,
0,
RunningGuiID,
false);
RunningGuiID++;
ItemYPosA += 10; // Incrementing Textbox Coordinates..
ItemYPosB += 10;
}
// Handle our "On Screen Text"..
IGUIElement* OurStTxt;
u16 CurrentGuiUI = StartGuiID;
u32 StartLiveGuiUI ;
//===========================================
OurStTxt = TheDevice->getGUIEnvironment()->getRootGUIElement()->getElementFromId (CurrentGuiUI);
if (OurStTxt) {stringw OurStr = L" Text placed only once (not cyclic) ";
OurStTxt->setText(OurStr.c_str());}
CurrentGuiUI++;
CurrentGuiUI++;
OurStTxt = TheDevice->getGUIEnvironment()->getRootGUIElement()->getElementFromId (CurrentGuiUI);
if (OurStTxt) {stringw OurStr = L" ";
OurStTxt->setText(OurStr.c_str());}
StartLiveGuiUI = CurrentGuiUI; // This is where we start setting the In-Animation Loop Values..
//===========================================
// In Blender the most managageable size for a model is 20 X 20 so we have the
// camera position and speed match this..
scene::ICameraSceneNode* TheCamera
= TheSceneManager->addCameraSceneNodeFPS(0,
200,
0.0100,
-1,
keyMap,
8);
TheCamera->setPosition(core::vector3df(0.0,0.0,50.0));
TheCamera->setTarget(core::vector3df(0.0, 0.0, 0.0)); // This changes unseen when a FPS camera is mouse-aimed and moved around..
CameraFov = 80.0; // The bigger this angle is, the biggger the 3D objects "appear"..
TheCamera->setFOV(CameraFov * 0.017453);
// We dont need Mipmapping for our skycube..
TheDriver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
/* // Uncomment this for the "SkyBox" keeping in mind that the images "MUST" exist in our "Data/Media Directory"..
ISceneNode* TheSKYBOXIscenenode
= TheSceneManager
->addSkyBoxSceneNode(TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/Y_POSITIVE.png"), // "TOP"
TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/Y_NEGATIVE.png"), // "DOWN"
TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/X_NEGATIVE.png"), // "LEFT"
TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/X_POSITIVE.png"), // "RIGHT"
TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/Z_NEGATIVE.png"), // "FRONT"
TheDriver->getTexture("Data/0000_SKY_BOXES/0003_ENV_A/Z_POSITIVE.png")); // "BACK"
//*/
// Because we disabled mipmapping for skycube images we need to now re-enable them for our other images..
TheDriver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
TheDevice->getCursorControl()->setVisible(false);
// LIGHTS (we try to have "Irrlicht Lights" talk to our shaders)..
// Initialisation (could have been a for loop)..
IRRLightARRAY [0] = TheSceneManager->addLightSceneNode(0, vector3df( 0 , 0 , 0 ), SColorf( 1.0f , 1.0f , 1.0f ), 1.0f, 1); // We have Light 001 always at Camera Position..
IRRLightARRAY [1] = TheSceneManager->addLightSceneNode(0, vector3df( 0 , 0 , 0 ), SColorf( 1.0f , 1.0f , 1.0f ), 1.0f, 2);
IRRLightARRAY [2] = TheSceneManager->addLightSceneNode(0, vector3df( 0 , 0 , 0 ), SColorf( 1.0f , 1.0f , 1.0f ), 1.0f, 3);
IRRLightARRAY [3] = TheSceneManager->addLightSceneNode(0, vector3df( 0 , 0 , 0 ), SColorf( 1.0f , 1.0f , 1.0f ), 1.0f, 4); // Do we need to "increment" the ID?
// O.K. weve created FOUR Lights (we can do hundreds), now set their data..
// See how some items are set at NODE LEVEL where others are set via Getting the DATA..
// LIGHTS INITIALISATION (GETS SET IN RUNTIME TOO!!!)..
// We also update this in realtime in the main loop..
IRRLightARRAY [0]->setPosition(vector3df ( 0.0 , 0.0 , 300.0 )); // This gets overridden as we have it "hooked to the camera"..
IRRLightARRAY [1]->setPosition(vector3df ( 0.0 , -300.0 , 0.0 ) );
IRRLightARRAY [2]->setPosition(vector3df ( -300.0 , 0.0 , 0.0 ) );
IRRLightARRAY [3]->setPosition(vector3df ( 300.0 , 0.0 , 0.0 ) );
// We will not use these lights in the same way as it is used by irrlicht.
// We will however have these lights in their Irrlicht form i.e. "ILightSceneNode" because we'd like
// to have our shader and Irrlicht work closely together so that any possible future developemnt
// with shaders could be easier.. (some day, someone could even have these shaders hardcoded into the engine :)
// We'd also like to be able to contol these light's animation etc like we can do with other "Scenenodes"..
// In other words our shader will read the information from these Irrlicht lights and act accoringly
// using, for instance, methods like "getLightType()"..
// Irrlicht could then be seamlessly used with the Shader in, for instance, a "SpotLight" (yet to be coded)..
// For now, our shader, when it sees that this light is a "Point Light", will know what to do with it..
// We'd have "getLightData().Position"..
// Here we do the actual "Loading" of our Shader Programs depending on which Driver Type we had selected i.e. GLSL or HLSL..
// OPEN GL
if (SelectedDriverType == EDT_OPENGL)
{if (TheGPUProgrammingServices)
{OurShaderCallBackClass* OurCallback = new OurShaderCallBackClass();
ShaderLanguage = video::EGSL_DEFAULT; // Meaning HLSL or GLSL.. (cg is not supported but an engine recompile could bring it back)..
// Though not supported anymore CG is still very usefull for certain platforms..
IDSelectedShader = TheGPUProgrammingServices
->addHighLevelShaderMaterialFromFiles("Shaders/GLSL_002_VERTEX_PROGRAM.vert", // Vertex Program Filename and Directory Location..
"main", // Main Vertex Function Name
EVST_VS_2_0, // Vertex Program Profile..
"Shaders/GLSL_001_FRAGMENT_SHADER.frag", // Fragment Shader Filename.. (with GLSL it it seems mandatory to have them in separate files)..
"main", // Main Fragment function name..(with GLSL other names seems to cause problems?)
EPST_PS_2_0, // Could be 3_0 Fragment Shader Profile but NO PROBLEM WITH GLSL (in HLSL we have an issue)..
OurCallback,
EMT_TRANSPARENT_ALPHA_CHANNEL_REF, // This is what makes the "Clip Mapping" possible "Mmmmonster kill!!"..
0,
ShaderLanguage // As we had selected in the console..
);
// For some reason I couldn't get both VERTEX PROGRAM and FRAGMENT SHADER in ONE FILE..
// (something to do with a function HAVING TO BE NAMED "main")?..
OurCallback->drop();
}
}
// DIRECT 3D
if (SelectedDriverType == EDT_DIRECT3D9)
{if (TheGPUProgrammingServices)
{OurShaderCallBackClass* OurCallback = new OurShaderCallBackClass();
ShaderLanguage = video::EGSL_DEFAULT; // Meaning HLSL or GLSL..
IDSelectedShader = TheGPUProgrammingServices
->addHighLevelShaderMaterialFromFiles("Shaders/HLSL_0043_WORKING.hlsl",
"vertexMain",
EVST_VS_2_0, // Seems Fine..
"Shaders/HLSL_0043_WORKING.hlsl", // Ja.. Same name..
"pixelMain",
EPST_PS_3_0, // ARITHMETIC SLOTS ISSUE (exceeds max registers) WITH "2_0"..(see similar note at GLSL version)..
OurCallback,
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF, // CLIPMAPPING..
0,
ShaderLanguage);
OurCallback->drop();
}
}
//
TheDriver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// - FIRST OBJECT -
// AniMesh0001_Skinned = TheSceneManager ->getMesh("Data/0013_FUSION_BALL/X_001.x"); // A good test model..
AniMesh0001_Skinned = TheSceneManager ->getMesh("Data/0110_DOOM_BOSS/X_001.x");
((ISkinnedMesh*)AniMesh0001_Skinned)->convertMeshToTangents(); // Must do!..
IScANInode001_Skinned = TheSceneManager->addAnimatedMeshSceneNode(AniMesh0001_Skinned);
IScANInode001_Skinned->setAnimationSpeed (CurrentAnimSpeed) ;
// Frameloop..
// IScANInode001_Skinned->setFrameLoop ( 0.0 + 1.0 , IScANInode001_Skinned -> getEndFrame () - 0.0 ); // Character animation matters..
IScANInode001_Skinned->setFrameLoop ( 5.0 + 0.0 , IScANInode001_Skinned -> getEndFrame () - 5.0 );
IScANInode001_Skinned->setPosition(core::vector3df(0.0 ,0.0 ,0.0));
IScANInode001_Skinned->setRotation(core::vector3df( 0.0 , 180.0 , 0.0 ));
IScANInode001_Skinned->setScale(core::vector3df(1.0 , 1.0 ,1.0));
IScANInode001_Skinned->setAutomaticCulling(EAC_FRUSTUM_BOX); // If we don't do this then it would seem culling is dependent on a Single Plane which
// has its normal vector and position based on the camera direction and position..
// Now.. Would this be usefull if we divided a big level object into smaller cubes treated
// as separate objects? (not as cool as true BSP)..
// IScANInode001_Skinned->setAutomaticCulling(EAC_FRUSTUM_BOX || EAC_OCC_QUERY); // Could this combination have been possible? (does occlusion_query really work?)..
// Wouldn't it be cool if we had "EAC_FRUSTUM_BUFF_BOX"? It is becoming easier to assign faces to buffers in Creative Content Apps..
// There is a method in which this can be done using Blender, Deep Explorer and Frag Motion..
IScANInode001_Skinned->setVisible(true);
IScANInode001_Skinned->getMaterial( 0 ).setTexture(DIFFUSE_LAYER, TheDriver->getTexture("Data/0110_DOOM_BOSS/DIFFUSE_MAP_A.png")); // ACDC produced a smaller PNG than Deep Explorer..
IScANInode001_Skinned->getMaterial( 0 ).setTexture(NORMALS_LAYER, TheDriver->getTexture("Data/0110_DOOM_BOSS/NORMAL_MAP_A.png"));
IScANInode001_Skinned->getMaterial( 0 ).setTexture(SPECULAR_LAYER, TheDriver->getTexture("Data/0110_DOOM_BOSS/SPECULAR_MAP_A.png"));
IScANInode001_Skinned->getMaterial( 0 ).setTexture(GLOSS_LAYER, TheDriver->getTexture("Data/0110_DOOM_BOSS/GLOSS_MAP_A.png"));
// File Formats and which Software Titles can save to which format is quite an interesting subject..
// An example of how one would use this knowledge is a PNG file that exists in 8 bit mode (very small)..
// Photoshop does not save PNG's in 8 bit mode.. Deep Exploration doesn't either..
// But.. If you open a TGA (in 8 bit form from Photoshop) and save it as a png from ACDC then you get an 8 bit PNG
// which is much smaller than a 24 bit png of the same file (lossless)..
// Now, Irrlicht handles All these file formats beautifully!!
// Keep experimenting with different image formats and titles because there is a lot that is not common knowledge..
// The smaller the file the better and there are many undocumented methods to make an image file smaller..
// For more layers see "Engine Recompiling" matters concering Custom Irrlicht versions..
// I understand that all cool "Pipeline" and "SVN" things find their way into Irrlicht releases?
IScANInode001_Skinned->setMaterialFlag(video::EMF_LIGHTING, false);
IScANInode001_Skinned->setMaterialType((video::E_MATERIAL_TYPE) IDSelectedShader);
scene::ISceneNodeAnimator* TheAnimator = TheSceneManager->createRotationAnimator(core::vector3df(0.0 ,0.0, 0.0));
IScANInode001_Skinned->addAnimator(TheAnimator);
// Important Note:
// When your Alias Wavefront "*.obj" refers to more than one material, then
// it becomes necessary to include the Material "*.mtl" file in your DATA directory!
// Irrlicht sees that there are references to more than one material and it becomes
// necessary to have the material file available for Irrlicht to read.
// This creates an issue inside of irrlicht when it encounters references within
// the material file to "Bumpmaps" (which are actually different to Normal Maps).
// The way Irrlicht renders these bumps is NOT turned off by invoking your shaders resulting
// in strange bump effects.
// We solve this by simply "commenting out" the Bumpmap references
// like this..
// map_Kd DIFFUSE_MAP_C.tga
// # map_bump NORMAL_MAP_C.tga
// map_kS SPECULAR_MAP_C.tga
// See how "#" is the comment-line initiating character in Alias Wavefront's *.mtl" or "*.obj"..
// - SECOND (Non Skinned) OBJECT -
AniMesh002_NON_Skinned = TheSceneManager ->getMesh("Data/0109_BADGER/OBJ_001.obj"); // Quake 4 model you've never seen rendered this way!..
TanMesh002_NON_Skinned = TheSceneManager->getMeshManipulator() ->createMeshWithTangents(AniMesh002_NON_Skinned, false, false, false, true ); // RECALC NORMALS..
ISceneNode002_NON_Skinned = 0; // Like "NULL"-ing a new pointer?
ISceneNode002_NON_Skinned = TheSceneManager->addMeshSceneNode(TanMesh002_NON_Skinned);
// Material 000..
ISceneNode002_NON_Skinned->getMaterial( 0 ).setTexture(DIFFUSE_LAYER, TheDriver->getTexture("Data/0109_BADGER/DIFFUSE_MAP_A.png"));
ISceneNode002_NON_Skinned->getMaterial( 0 ).setTexture(NORMALS_LAYER, TheDriver->getTexture("Data/0109_BADGER/NORMAL_MAP_A.png"));
ISceneNode002_NON_Skinned->getMaterial( 0 ).setTexture(SPECULAR_LAYER, TheDriver->getTexture("Data/0109_BADGER/SPECULAR_MAP_A.png"));
ISceneNode002_NON_Skinned->getMaterial( 0 ).setTexture(GLOSS_LAYER, TheDriver->getTexture("Data/0109_BADGER/GLOSS_MAP_A.png"));
// Still to be properly implemented in Standard Irrlicht Releases..
// ISceneNode002_NON_Skinned->getMaterial( 0 ).setTexture(CUBE_MAP_LAYER, TheDriver->getTexture("Data/MAIN_SKY_IMAGE.tga"));
// Material 001.. With Alias's “*.obj” when using MORE THAN ONE texture, then the “*.mtl” file MUST exist along with the “*.obj” file..
// Also disable all metion of “bump” or “bump_map” in the material file as Irrlicht uses it to mess with your normal calculations..
ISceneNode002_NON_Skinned->setMaterialFlag(video::EMF_LIGHTING, false); // Once this happens then irrlicht SHOULD NOT touch the lighting-rendering process for the material..
ISceneNode002_NON_Skinned->setMaterialType(E_MATERIAL_TYPE(IDSelectedShader)); // Invoke your Shader.. All Irrlicht Rendering should IMMEDIATELY be TURNBED OFF..
ISceneNode002_NON_Skinned->setScale(vector3df( 1.0 , 1.0 , 1.0 ));
ISceneNode002_NON_Skinned->setRotation(core::vector3df( 0.0 , 0.0 , 0.0 ));
ISceneNode002_NON_Skinned->setPosition(vector3df( 40.0 , 0.0 , 0.0 ));
ISceneNode002_NON_Skinned->setAutomaticCulling(EAC_FRUSTUM_BOX); // See earlier culling note..
ISceneNode002_NON_Skinned->setVisible(true);
//------------------------------------------------------------------------------------------- ---- --- -- - -
// Variables concerned with "LIVE TANGENT UPDATES"..
// We assign this "SceneMeshCount" value during the main render loop aswell
// as things can be added or removed during run-time..
// This part of the Tangent Updater do not get called at each frame as it is an “Initialisation”..
// * * * * * * * * * * * * * * * * * * * * * *
SceneMeshCount = TheSceneManager ->getMeshCache()->getMeshCount();
irr::scene::IMeshBuffer* TempMeshBuffer;
video::S3DVertexTangents* TempVertTansPTR;
TempMeshBuffer = TheSceneManager->getMeshCache()->getMeshByIndex(0)->getMeshBuffer(0);
TempVertTansPTR = (video::S3DVertexTangents*)TempMeshBuffer->getVertices();
TheSceneMeshCache = TheSceneManager->getMeshCache();
S3DVertexTangents VertexTriA;
S3DVertexTangents VertexTriB;
S3DVertexTangents VertexTriC;
// * * * * * * * * * * * * * * * * * * * * * *
int lastFPS = -1;
// MAIN CYCLIC LOOP..
while(TheDevice->run())
{if (TheDevice->isWindowActive())
{// REALTIME LIGHTS UPDATE..
// This would happen cyclically during the game or simulation..
// Some would be Muzzle flashes and others would be explosions while yet others would be static level lights..
// We do this in here instead of the Fragment Shader because we wantto treat the Shader Lights as Irrlicht Lights....
IRRLightARRAY [0]->setPosition(vector3df(CameraPosition) ); // We update this "cyclically" in the Realtime Loop..
IRRLightARRAY [1]->setPosition(vector3df ( 0.0 , -300.0 , 0.0 ) ); // They could have been animated!..
IRRLightARRAY [2]->setPosition(vector3df ( -300.0 , 0.0 , 0.0 ) );
IRRLightARRAY [3]->setPosition(vector3df ( 300.0 , 0.0 , 0.0 ) );
IRRLightARRAY [0]->getLightData().DiffuseColor = SColorf( 0.7f , 0.7f , 0.7f ); // Our "Camera Light" Grey-ish..
// IRRLightARRAY [0]->getLightData().DiffuseColor = SColorf( 0.0f , 0.0f , 0.0f ); // If you want the Camera Light Disabled (i.e. Darkened to zero)..
IRRLightARRAY [1]->getLightData().DiffuseColor = SColorf( 0.9f , 0.4f , 0.0f ); // Red-ish..
IRRLightARRAY [2]->getLightData().DiffuseColor = SColorf( 0.3f , 0.7f , 0.7f ); // Blue-ish..
IRRLightARRAY [3]->getLightData().DiffuseColor = SColorf( 0.6f , 0.6f , 0.3f ); // Yellow-ish..
// Use grey lights to show "helio free" diffuse colour (from all lights)..
// IRRLightARRAY [1]->getLightData().DiffuseColor = SColorf( 0.3f , 0.3f , 0.3f ); // 33.3..% Grey..
// IRRLightARRAY [2]->getLightData().DiffuseColor = SColorf( 0.3f , 0.3f , 0.3f ); // 33.3..% Grey..
// IRRLightARRAY [3]->getLightData().DiffuseColor = SColorf( 0.3f , 0.3f , 0.3f ); // 33.3..% Grey..
// Darken all lights except the camera lighT....
// IRRLightARRAY [1]->getLightData().DiffuseColor = SColorf( 0.0f , 0.0f , 0.0f );
// IRRLightARRAY [2]->getLightData().DiffuseColor = SColorf( 0.0f , 0.0f , 0.0f );
// IRRLightARRAY [3]->getLightData().DiffuseColor = SColorf( 0.0f , 0.0f , 0.0f );
// Try different configurations above by un-commenting certain lines....
// We do have an option here to do this without Irrlicht lights, but this method is simply "more complete"
// and shows a reasonably seamless existence between Irrlicht Lights and Shader Lights..
// This places Irrlicht Light Attributes into an "float" array sothat we can feed the array into the shader in real-time..
// We could hypothetically have Thousands of lights fed to the shaders but even GPUs have limits..
for (int Li = 0; Li < ActiveLightCount ; Li ++) // Active Lights kept to a minimum. (Light Pool.. future stuff..)
{// Get all POSITIONS to a form we can feed the Shaders as arrays..
// This could also work but we might want to treat things as nodes at the end of the day..
// Up to you..(whichever is faster?)
LightsPosArray [Li] = IRRLightARRAY [Li]->getLightData().Position; // An "Array Load Stride" of 3 is possible here so we do X,Y and Z in one move..Cool!
// Get all Colours to a form we can feed the Shaders as arrays..
// We can't just load them in one move like above because were dealing with "Scolorf" which is FOUR Floats, and we want as a set of "Three Floats"..
// Also note that Alpha (fourth value) in my opinion has nothing to do with lights under Physically Based Shading..
LightsColArray [Li].X = IRRLightARRAY [Li]->getLightData().DiffuseColor.r;
LightsColArray [Li].Y = IRRLightARRAY [Li]->getLightData().DiffuseColor.g;
LightsColArray [Li].Z = IRRLightARRAY [Li]->getLightData().DiffuseColor.b; // We skip Light alpha or .w..
// MOre things like Type, Attenuation, and Spot Light Cone Agnles can go here later..
}
// Allready initialised, but we CAN control this in Real-Time..
// "Lerping" towards Original Mapped Colour is a cool way to emulate "Ambience"..
PassIntArray [IAI_000_ACTIVE_LIGHTCOUNT] = 4; // We use FOUR lights but we could use MANY MORE..
PassIntArray [IAI_001_PLAECF_STATUS] = 1;
PassFloatArray [FAI_000_GLOBAL_BRIGHTNESS] = 1.25; // Sent to the shader via the "float" array and multiplied to all Light Intensities..
// Try different values to see it work!
PassFloatArray [FAI_001_LERP_TO_RAW_DIFFUSE] = 0.1; // not used in the shaders, but shows that we could..
IScANInode001_Skinned->setAnimationSpeed(CurrentAnimSpeed) ; // SECOND TIME FOR REALTIME CONTROLL..
CurrentGuiUI = StartLiveGuiUI; // So that we dont render static text at each frame..
// CurrentGuiUI++;
OurStTxt = TheDevice->getGUIEnvironment()->getRootGUIElement()->getElementFromId (CurrentGuiUI);
if (OurStTxt)
{stringw OurStr = L" Text updated Cyclically: ";
OurStTxt->setText(OurStr.c_str());
}
CurrentGuiUI++;
// ready for next one..
// "GeeXLab" has a nice colour picker from which you can copy Float or Int RGB values after selecting from a pallette..
TheDriver->beginScene(true, true, BckGrndCol);
// Swap the order if you want text to be blocked by rendered things..
TheSceneManager->drawAll();
OurGUIEnvironment->drawAll(); // Render our GUI elements..
TheDriver->endScene(); // Irrlicht brilliantly does all Openg GL and D3D things depending on the selected API..
CameraPosition = TheDevice->getSceneManager()->getActiveCamera()->getAbsolutePosition();
//================================================
SceneMeshCount = TheSceneManager ->getMeshCache()->getMeshCount();
// PLEASE OPTIMIZE THIS or simply do it as irrlicht does it internally....
if (TanUpdStatus == true) // globally updated for all Animated "Skinned" meshes..(not for NON-Skinned items..)
{// SceneMeshCount..
for (u32 SMeshI = 0; SMeshI < SceneMeshCount; SMeshI++)
{// start loop "SMESHI"..
u16 TheMeshType = TheSceneMeshCache->getMeshByIndex(SMeshI)->getMeshType();
// We want to recalculate Tangents for Skinned Meshes only so we must determine which ones are "Skinned"..
if (TheMeshType == EAMT_SKINNED)
{// start procedure: Only "Skinned" meshes..
AcquiredAnimeshByIndex = TheSceneMeshCache->getMeshByIndex(SMeshI);
AcquiredImesh = AcquiredAnimeshByIndex ;
u32 TheMBufferCount = AcquiredImesh->getMeshBufferCount();
for (u32 MBuffI = 0 ; MBuffI < TheMBufferCount ; MBuffI++)
{// start Buffer Loop (some large nodels may have more than one buffer)..
// It would be cool if Buffers could also be considered for Frustum Culling..
// Not relevant here but Frustum culling currently only works with complete objects..
CurrMeshBuffPtr = AcquiredImesh->getMeshBuffer(0); // WE MUST ALSO LOOP BUFFERS..
CurrTanVertsPtr = (video::S3DVertexTangents*)CurrMeshBuffPtr->getVertices(); // Many Buffers for Many Meshes..
// O.K. to do declare these here multiple times ?
u16* TheINDEXPtr = TheSceneMeshCache->getMeshByIndex(SMeshI)->getMeshBuffer(0)->getIndices();
u32 TheIndexCount = (u32)TheSceneMeshCache->getMeshByIndex(SMeshI)->getMeshBuffer(0)->getIndexCount();
for (u32 IndexII = 0; IndexII < TheIndexCount; IndexII+=3)
{// start Index Loop.. // Get all all three of our triangle vertices..
VertexTriA = CurrTanVertsPtr[TheINDEXPtr[IndexII]];
VertexTriB = CurrTanVertsPtr[TheINDEXPtr[IndexII+1]];
VertexTriC = CurrTanVertsPtr[TheINDEXPtr[IndexII+2]];
// Here we get the THREE POINTS XYZ Positions for the TRIANGLE..
f32 TAX = VertexTriA.Pos.X; f32 TAY = VertexTriA.Pos.Y; f32 TAZ = VertexTriA.Pos.Z;
f32 TBX = VertexTriB.Pos.X; f32 TBY = VertexTriB.Pos.Y; f32 TBZ = VertexTriB.Pos.Z;
f32 TCX = VertexTriC.Pos.X; f32 TCY = VertexTriC.Pos.Y; f32 TCZ = VertexTriC.Pos.Z;
// Here we get the UV Coordinates for each of the three Points.
f32 TAU = VertexTriA.TCoords.X; f32 TAV = VertexTriA.TCoords.Y;
f32 TBU = VertexTriB.TCoords.X; f32 TBV = VertexTriB.TCoords.Y;
f32 TCU = VertexTriC.TCoords.X; f32 TCV = VertexTriC.TCoords.Y;
// We introduce THREE new "Delta Vectors" which will eventually become "Triangle Edges"..
// This is a special "recipe" using "Triangle Points" and "UV Coordinates"..
// "Subtraction" hence "Delta Vectors"..
f32 DV1X = TBX - TAX ; f32 DV1Y = TBU - TAU ; f32 DV1Z = TBV - TAV;
f32 DV2X = TCX - TAX ; f32 DV2Y = TCU - TAU ; f32 DV2Z = TCV - TAV;
f32 DV3X = TBY - TAY ; f32 DV3Y = TBU - TAU ; f32 DV3Z = TBV - TAV;
f32 DV4X = TCY - TAY ; f32 DV4Y = TCU - TAU ; f32 DV4Z = TCV - TAV;
f32 DV5X = TBZ - TAZ ; f32 DV5Y = TBU - TAU ; f32 DV5Z = TBV - TAV;
f32 DV6X = TCZ - TAZ ; f32 DV6Y = TCU - TAU ; f32 DV6Z = TCV - TAV;
// Now we introduce THREE "Cross Products". Cross Product A, Cross Product B and Cross Product C.
f32 CAX = (DV1Y * DV2Z) - (DV2Y * DV1Z); f32 CAY = (DV1Z * DV2X) - (DV2Z * DV1X);
f32 CAZ = (DV1X * DV2Y) - (DV2X * DV1Y); f32 CBX = (DV3Y * DV4Z) - (DV4Y * DV3Z);
f32 CBY = (DV3Z * DV4X) - (DV4Z * DV3X); f32 CBZ = (DV3X * DV4Y) - (DV4X * DV3Y);
f32 CCX = (DV5Y * DV6Z) - (DV6Y * DV5Z); f32 CCY = (DV5Z * DV6X) - (DV6Z * DV5X);
f32 CCZ = (DV5X * DV6Y) - (DV6X * DV5Y);
// Calculate our TANGENT..
f32 TanX = (CAY / CAX); f32 TanY = (CBY / CBX); f32 TanZ = (CCY / CCX);
// ..and our BINORMAL..
// In this implementation the act of subtraction to get the Delta Vectors effectively
// makes getting Binormals elswehere (like shaders) impossible!!
f32 BinX = (CAZ / CAX); f32 BinY = (CBZ / CBX); f32 BinZ = (CCZ / CCX);
// Now we replace the Static Tangents/Binormals with our animated ones..
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Tangent.X = -TanX; // Negatives are the result of numerous tests..
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Tangent.Y = -TanY;
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Tangent.Z = -TanZ;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Tangent.X = -TanX;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Tangent.Y = -TanY;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Tangent.Z = -TanZ;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Tangent.X = -TanX;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Tangent.Y = -TanY;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Tangent.Z = -TanZ;
// Binormals are NOT done in the shader by crossing Tangents with Normals..
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Binormal.X = BinX;
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Binormal.Y = BinY;
CurrTanVertsPtr[TheINDEXPtr[IndexII]].Binormal.Z = BinZ;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Binormal.X = BinX;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Binormal.Y = BinY;
CurrTanVertsPtr[TheINDEXPtr[IndexII+1]].Binormal.Z = BinZ;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Binormal.X = BinX;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Binormal.Y = BinY;
CurrTanVertsPtr[TheINDEXPtr[IndexII+2]].Binormal.Z = BinZ;
} // end Index Loop..
} // end Buffer Loop
} // End Only "Skinned" meshes..
} // end loop "SMESHI"
} // End user determined conditional Tangents & Binormal Update..
// END of Tangent and Binormals Updating..
int fps = TheDriver->getFPS();
if (lastFPS != fps)
{core::stringw str = L"Irrlicht 'HLSL' or 'GLSL' J.Pretorius 2015 [";
str += TheDriver->getName();
str += "] FPS:";
str += fps;
TheDevice->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
}
TheDevice->drop();
return 0;
}
void ResetShaderArrays () // Scene Specific for this demo, but availabe during runtime..
{// Integers.. (in shaders I'm not sure we can pass Booleans which is what some of these actually are)..
PassIntArray [IAI_000_ACTIVE_LIGHTCOUNT] = 4; // With this we control a "For Loop" in the shaders..
PassIntArray [IAI_001_PLAECF_STATUS] = 1; // On by default..
// Floats..
PassFloatArray [FAI_000_GLOBAL_BRIGHTNESS] = 1.0; // Just an example of how we'd send a single Floating Point Value as one element in an array..
PassFloatArray [FAI_001_LERP_TO_RAW_DIFFUSE] = 0.1;
// We need global brightness here..
}
// END...