Code: Select all
// Written by Jacques Pretorius "Vectrotek" in the Republic of South Africa. 2015..
// Irrlicht 1.8.3 ; Visual C++ 7.10 (release) ; Widows XP ; DX9 ; Nvidia 9600 GT
// A perfectlry working basis for HLSL applications. All it needs is optimising and cleanup.
// It calls an HLSL Shader which renders animated and skinned models physically correct..
// (This time it comes with a guarantee!)
#include <irrlicht.h>
#include <iostream>
#include "driverChoice.h"
using namespace irr;
#ifdef _MSC_VER
#pragma comment(lib, "Irrlicht.lib")
#endif
// = = = = = = = = = = = = = = = = = = = = = =
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace gui;
// = = = = = = = = = = = = = = = = = = = = = =
IrrlichtDevice* TheDevice = 0;
IrrlichtDevice* TheIRRNullDevice = 0; // is this "= 0" stil mandatory?
ISceneManager* TheSceneManager;
core::dimension2d<u32> TheDeskResolution;
// Selected Matrices..
core::matrix4 M01World;
// core::matrix4 M02View; // NOT USED ANYMORE..
core::matrix4 M05WorldViewProjection;
core::matrix4 M17WorldInverseTranspose;
bool TanUpdStatus; // Do we update the Tangents & Binormals in realtime or not..
u32 TheTestIndex;
u32 TheTestBufferCount;
u32 SceneMeshCount; // How many meshes are there in the scene?
IMeshCache* TheSceneMeshCache; // Not to be assigned cyclically..
irr::scene::IMeshBuffer* CurrMeshBuffPtr ; // We want to assign this cyclically..
video::S3DVertexTangents* CurrTanVertsPtr ;
IAnimatedMesh* AcquiredAnimeshByIndex; // This changes in the loop!
IMesh* AcquiredImesh;
u32 GeneralCounter; // To do some timed things with..
// - ANIMATED MODELS (DirectX or BLITZ BASIC) -
scene::IAnimatedMesh* AniMesh0001;
scene::IAnimatedMeshSceneNode* AniMeshNode0001;
scene::IMesh* Mesh0002;
scene::IAnimatedMesh* AniMesh0002;
scene::ISceneNode* ISceneNode0002;
//
class OurShaderCallBackClass : public video::IShaderConstantSetCallBack
{public:
virtual void OnSetConstants(video::IMaterialRendererServices* TheServices, s32 userData)
{video::IVideoDriver* TheDriver = TheServices->getVideoDriver();
// BUILD CUSTOM MATRICES..
M01World = TheDriver->getTransform(video::ETS_WORLD);
M05WorldViewProjection = TheDriver->getTransform(video::ETS_PROJECTION);
M05WorldViewProjection *= TheDriver->getTransform(video::ETS_VIEW); // Used indirectly..
M05WorldViewProjection *= TheDriver->getTransform(video::ETS_WORLD);
M17WorldInverseTranspose = M01World.getTransposed(); // For Irrlicht this replaces the commonly used "wrold matrix!"
M17WorldInverseTranspose.makeInverse();
// You may have to use.. MyMatrix = services->getVertexShaderConstantID("my_matrix"); etc..
TheServices->setVertexShaderConstant("M01World", M01World.pointer(), 16);
TheServices->setVertexShaderConstant("M05WorldViewProjection", M05WorldViewProjection.pointer(), 16);
TheServices->setVertexShaderConstant("M17WorldInverseTranspose", M17WorldInverseTranspose.pointer(), 16);
// TheServices->setVertexShaderConstant("M13ViewInverse", M13ViewInverse.pointer(), 16);
core::vector3df L001Pos ( 0.0 , 1000.0 , 0.0 ); // hooked to the camera....
core::vector3df L002Pos ( -1000.0 , 0.0 , 0.0 ); // Far Away..
core::vector3df L003Pos ( 1000.0 , 0.0 , 0.0 );
core::vector3df CameraPosition = TheDevice->getSceneManager()->getActiveCamera()->getAbsolutePosition();
// Most examples simple use matrices beacuse those programs dont really consider the "Eye Position".
// This is definitely needed where we have a camera.
TheServices->setVertexShaderConstant("CameraPos", reinterpret_cast<f32*>(&CameraPosition), 3); // NEW!
TheServices->setVertexShaderConstant("L001Pos", reinterpret_cast<f32*>(&CameraPosition), 3); // Camera Light..
TheServices->setVertexShaderConstant("L002Pos", reinterpret_cast<f32*>(&L002Pos), 3);
TheServices->setVertexShaderConstant("L003Pos", reinterpret_cast<f32*>(&L003Pos), 3);
video::SColorf L001Col( 1.0f , 1.0f , 1.0f ); // Lights all set to white as we controll them in the shader for now..
video::SColorf L002Col( 1.0f , 1.0f , 1.0f );
video::SColorf L003Col( 1.0f , 1.0f , 1.0f );
int PixelShaderCommand; // Not used from here yet but you can change commands in hte shader..
if ( GeneralCounter > 500) {PixelShaderCommand = 1;} // Automatically change commands, but not used now..
if ( GeneralCounter < 500) {PixelShaderCommand = 0;}
// TheServices->setPixelShaderConstant("ShaderCommand", reinterpret_cast<int*>(&PixelShaderCommand), 1); // NOT YET..
TheServices->setPixelShaderConstant("L001Col", reinterpret_cast<f32*>(&L001Col), 3);
TheServices->setPixelShaderConstant("L002Col", reinterpret_cast<f32*>(&L002Col), 3);
TheServices->setPixelShaderConstant("L003Col", reinterpret_cast<f32*>(&L003Col), 3);
f32 TextureLayerID0 = 0;
f32 TextureLayerID1 = 1;
f32 TextureLayerID2 = 2;
TheServices->setPixelShaderConstant("ColorSampler", &TextureLayerID0, 1); // Same as GLSL..
TheServices->setPixelShaderConstant("NormalSampler", &TextureLayerID1, 1);
TheServices->setPixelShaderConstant("SpecularSampler", &TextureLayerID2, 1);
float AppShaderCommand = 0;
}
};
//----------------------------------------------------------- ------ ----- ---- --- -- -
int main()
{TheIRRNullDevice = createDevice(video::EDT_NULL); // Null Device for us to get our windows resolution..
TheDeskResolution = TheIRRNullDevice->getVideoModeList()->getDesktopResolution(); // As set by you in windows..
TheIRRNullDevice->drop(); // We got the desktop resolution, so drop the Null Device..
TheDevice = createDevice(video::EDT_DIRECT3D9,TheDeskResolution,32);
if (TheDevice == 0) return 1;
video::IVideoDriver* TheDriver = TheDevice->getVideoDriver();
scene::ISceneManager* TheSceneManager = TheDevice->getSceneManager();
video::IGPUProgrammingServices* TheGPUProgrammingServices = TheDriver->getGPUProgrammingServices();
GeneralCounter = 0;
s32 OurShaderMaterial = 0;
if (TheGPUProgrammingServices)
{OurShaderCallBackClass* OurCallback = new OurShaderCallBackClass();
const video::E_GPU_SHADING_LANGUAGE shadingLanguage = video::EGSL_CG; // Means HLSL too..
TanUpdStatus = true; //.....
OurShaderMaterial
= 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 "2_0"....
OurCallback,
video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF, // CLIPMAPPING..
0,
shadingLanguage);
OurCallback->drop();
}
TheDriver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, true);
// - FIRST OBJECT -
// AniMesh0001 = TheSceneManager ->getMesh("Data/0013_FUSION_BALL/X_001.x");
AniMesh0001 = TheSceneManager ->getMesh("Data/0070_HALO_GUY/B3d_001.b3d");
((ISkinnedMesh*)AniMesh0001)->convertMeshToTangents(); // Right Place?
AniMeshNode0001 = TheSceneManager->addAnimatedMeshSceneNode(AniMesh0001);
float AniPlaySpeed = 40.0 ; // Default..
AniMeshNode0001->setAnimationSpeed (AniPlaySpeed) ; // Get this into command..
AniMeshNode0001->setFrameLoop ( 0.0 + 1.0 , AniMeshNode0001 -> getEndFrame () - 0.0 ); // Automate this..
AniMeshNode0001->setPosition(core::vector3df(0.0 ,0.0 ,0.0));
AniMeshNode0001->setScale(core::vector3df(1.0 ,1.0 ,1.0));
AniMeshNode0001->getMaterial( 0 ).setTexture(0, TheDriver->getTexture("Data/0070_HALO_GUY/DIFFUSE_MAP.png"));
AniMeshNode0001->getMaterial( 0 ).setTexture(1, TheDriver->getTexture("Data/0070_HALO_GUY/NORMAL_MAP.png"));
AniMeshNode0001->getMaterial( 0 ).setTexture(2, TheDriver->getTexture("Data/0070_HALO_GUY/SPECULAR_MAP.png"));
AniMeshNode0001->setMaterialFlag(video::EMF_LIGHTING, false);
AniMeshNode0001->setMaterialType((video::E_MATERIAL_TYPE) OurShaderMaterial);
scene::ISceneNodeAnimator* TheAnimator = TheSceneManager->createRotationAnimator(core::vector3df(0.0 ,0.1, 0.0));
AniMeshNode0001->addAnimator(TheAnimator);
//TheAnimator->drop(); // No!
// - SECOND OBJECT -
AniMesh0002 = TheSceneManager ->getMesh("Data/002_OROLOC/OBJ_001.obj");
Mesh0002 = TheSceneManager->getMeshManipulator() ->createMeshWithTangents(AniMesh0002 ,false,false,false,true ); // RECALC NORMALS..
ISceneNode0002 = 0; // Like "NULL"-ing a new pointer?
ISceneNode0002 = TheSceneManager->addMeshSceneNode(Mesh0002);
ISceneNode0002->getMaterial( 0 ).setTexture(0, TheDriver->getTexture("Data/002_OROLOC/DIFFUSE_MAP.png"));
ISceneNode0002->getMaterial( 0 ).setTexture(1, TheDriver->getTexture("Data/002_OROLOC/NORMAL_MAP.png"));
ISceneNode0002->getMaterial( 0 ).setTexture(2, TheDriver->getTexture("Data/002_OROLOC/SPECULAR_MAP.png"));
ISceneNode0002->setMaterialFlag(video::EMF_LIGHTING, false);
ISceneNode0002->setMaterialType(E_MATERIAL_TYPE(OurShaderMaterial)); // One material type per object..
ISceneNode0002->setScale(vector3df( 1.0 , 1.0 , 1.0 ));
ISceneNode0002->setRotation(core::vector3df( 0.0 , 0.0 , 0.0 ));
ISceneNode0002->setPosition(vector3df( -15.0 , 0.0 , 0.0 ));
//------------------------------------------------------------------------------------------- ---- --- -- - -
// Add a camera, its controls and disable the mouse cursor..
SKeyMap keyMap[8];
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;
// 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));
TheDevice->getCursorControl()->setVisible(false);
// TANGENT UPDATE VARS..
// We assign this value during the main rendeerloop aswell!!
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();
TheTestIndex = TheSceneMeshCache->getMeshIndex(AniMesh0001);
S3DVertexTangents VertexTriA;
S3DVertexTangents VertexTriB;
S3DVertexTangents VertexTriC;
int lastFPS = -1;
while(TheDevice->run())
{if (TheDevice->isWindowActive())
{// GeneralCounter ++;
if ( GeneralCounter > 1000) {GeneralCounter = 0;}
// TheDriver->beginScene(true, true, video::SColor( 255 , 207 , 211 , 192 ));
TheDriver->beginScene(true, true, video::SColor( 255 , 0 , 0 , 0 ));
TheSceneManager->drawAll();
TheDriver->endScene();
//================================================
// We want to change tangents for skinned meshes only so we must determine which ones are "Skinned"..
SceneMeshCount = TheSceneManager ->getMeshCache()->getMeshCount();
if (TanUpdStatus == true) // globally updated for all Animated "Skinned" meshes..
{// SceneMeshCount
for (u32 SMeshI = 0; SMeshI < SceneMeshCount; SMeshI++) // Nice "+3" concept in For loop..
{// start loop "SMESHI"
u16 TheMeshType = TheSceneMeshCache->getMeshByIndex(SMeshI)->getMeshType();
if ( TheMeshType == EAMT_SKINNED) // EAMT_SKINNED
{// start Only "Skinned" meshes..
AcquiredAnimeshByIndex = TheSceneMeshCache->getMeshByIndex(SMeshI);
AcquiredImesh = AcquiredAnimeshByIndex ;
u32 TheMBufferCount = AcquiredImesh->getMeshBufferCount();
for (u32 MBuffI = 0 ; MBuffI < TheMBufferCount ; MBuffI++)
{// start Buffer Loop
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) // Nice "+3" concept in For loop..
{// 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 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;
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 conditional Tangents & Binormal Update..
//================================================
int fps = TheDriver->getFPS();
if (lastFPS != fps)
{core::stringw str = L"Irrlicht 'HLSL' J.Pretorius 2015 [";
str += TheDriver->getName();
str += "] FPS:";
str += fps;
TheDevice->setWindowCaption(str.c_str());
lastFPS = fps;
}
}
}
TheDevice->drop();
return 0;
}