// // Urho3D Engine // Copyright (c) 2008-2011 Lasse Öörni // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. // #include "Precompiled.h" #include "AnimatedModel.h" #include "Animation.h" #include "AnimationState.h" #include "Audio.h" #include "Application.h" #include "BillboardSet.h" #include "Button.h" #include "CollisionShape.h" #include "Cursor.h" #include "CustomObject.h" #include "DebugHud.h" #include "DebugRenderer.h" #include "Engine.h" #include "EngineEvents.h" #include "Exception.h" #include "File.h" #include "Font.h" #include "Geometry.h" #include "IndexBuffer.h" #include "Input.h" #include "InstancedModel.h" #include "Joint.h" #include "Light.h" #include "Log.h" #include "Material.h" #include "Mod.h" #include "OcclusionBuffer.h" #include "Octree.h" #include "OctreeQuery.h" #include "PhysicsEvents.h" #include "PhysicsWorld.h" #include "Pipeline.h" #include "ParticleEmitter.h" #include "Profiler.h" #include "Renderer.h" #include "RendererImpl.h" #include "ResourceCache.h" #include "RigidBody.h" #include "Scene.h" #include "Skybox.h" #include "StaticModel.h" #include "Text.h" #include "StringUtils.h" #include "Texture2D.h" #include "UI.h" #include "UIEvents.h" #include "VertexBuffer.h" #include "Window.h" #include "XM.h" #include "XMLFile.h" #include "Zone.h" #include "DebugNew.h" void calculateTangents(float* vertexData, unsigned vertexCount, const unsigned short* indexData, unsigned indexCount); float vertexData[] = { // Side 1 -1, 1, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, 1, -1, 0, 0, -1, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 0, -1, 1, 1, 0, 0, 0, 0, -1, -1, -1, 0, 0, -1, 0, 1, 0, 0, 0, 0, // Side 2 1, 1, -1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 1, -1, -1, 1, 0, 0, 0, 1, 0, 0, 0, 0, // Side 3 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, -1, -1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, -1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, // Side 4 -1, 1, 1, -1, 0, 0, 0, 0, 0, 0, 0, 0, -1, 1, -1, -1, 0, 0, 1, 0, 0, 0, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1, 0, 0, 0, 0, -1, -1, 1, -1, 0, 0, 0, 1, 0, 0, 0, 0, // Side 5 -1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, -1, 0, 1, 0, 1, 1, 0, 0, 0, 0, -1, 1, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, // Side 6 -1, -1, -1, 0, -1, 0, 0, 0, 0, 0, 0, 0, 1, -1, -1, 0, -1, 0, 1, 0, 0, 0, 0, 0, 1, -1, 1, 0, -1, 0, 1, 1, 0, 0, 0, 0, -1, -1, 1, 0, -1, 0, 0, 1, 0, 0, 0, 0 }; const unsigned short indexData[] = { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9, 10, 10, 11, 8, 12, 13, 14, 14, 15, 12, 16, 17, 18, 18, 19, 16, 20, 21, 22, 22, 23, 20 }; float objectangle = 0; float yaw = 0; float pitch = 0; bool paused = true; int texturequality = 2; int materialquality = 2; int fallbacklevel = 0; int shadowmapsize = 1024; bool hiresshadowmap = false; int texturefilter = 2; bool usespecular = true; int drawdebug = 0; bool drawshadows = true; bool attach = true; bool useocclusion = true; bool shadowfocus = true; static const int NUM_OBJECTS = 250; static const int NUM_LIGHTS = 20; static const int NUM_INSTANCENODES = 20; static const int NUM_INSTANCES = 50; static const int NUM_BILLBOARDNODES = 20; static const int NUM_BILLBOARDS = 15; Application::Application(const std::vector& arguments) : mArguments(arguments) { subscribeToEvent(EVENT_UPDATE, EVENT_HANDLER(Application, handleUpdate)); subscribeToEvent(EVENT_POSTRENDERUPDATE, EVENT_HANDLER(Application, handlePostRenderUpdate)); subscribeToEvent(EVENT_MOUSEMOVE, EVENT_HANDLER(Application, handleMouseMove)); subscribeToEvent(EVENT_MOUSEBUTTONDOWN, EVENT_HANDLER(Application, handleMouseButtonDown)); subscribeToEvent(EVENT_MOUSEBUTTONUP, EVENT_HANDLER(Application, handleMouseButtonUp)); } Application::~Application() { if (mEngine) { mEngine->dumpResources(); mEngine->dumpProfilingData(); } } void Application::run() { init(); while (!mEngine->isExiting()) mEngine->runFrame(mScene, mCameraEntity->getComponent()); } void Application::init() { mEngine = new Engine("Test.log"); mEngine->init("Urho3D Test", mArguments); PROFILE(App_Init); mCache = mEngine->getResourceCache(); mCache->addResourcePath(getSystemFontDirectory()); Renderer* renderer = mEngine->getRenderer(); UI* ui = mEngine->getUI(); UIElement* uiRoot = ui->getRootElement(); XMLFile* uiSetup = mCache->getResource("UI/UI.xml"); Cursor* cursor = new Cursor("Cursor"); cursor->loadParameters(uiSetup, "Cursor", mCache); cursor->setPosition(renderer->getWidth() / 2, renderer->getHeight() / 2); ui->setCursor(cursor); mScene = mEngine->createScene(); PhysicsWorld* world = mScene->getExtension(); world->setGravity(Vector3(0.0f, -9.81f, 0.0f)); world->setFps(100); world->setLinearRestThreshold(0.1f); world->setAngularRestThreshold(0.1f); world->setContactSurfaceLayer(0.001f); DebugHud* debugHud = mEngine->createDebugHud(); debugHud->setFont(mCache->getResource("cour.ttf"), 12); debugHud->setMode(DEBUGHUD_SHOW_STATS | DEBUGHUD_SHOW_MODE); mCameraEntity = mScene->createEntity(); Camera* camera = mCameraEntity->createComponent(); camera->setPosition(Vector3(-50.0f, 2.0f, -50.0f)); camera->setAspectRatio((float)renderer->getWidth() / (float)renderer->getHeight()); calculateTangents(vertexData, 24, indexData, 36); SharedPtr vb(new VertexBuffer(renderer, false)); vb->setSize(24, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT); vb->setData(vertexData); SharedPtr ib(new IndexBuffer(renderer, false)); ib->setSize(36, false); ib->setData(indexData); SharedPtr g(new Geometry()); g->setVertexBuffer(0, vb); g->setIndexBuffer(ib); g->setDrawRange(TRIANGLE_LIST, 0, ib->getIndexCount()); SharedPtr box(new Model(renderer, "Models/Box.mdl")); box->setNumGeometries(1); box->setNumGeometryLodLevels(0, 1); box->setGeometry(0, 0, g); box->setBoundingBox(BoundingBox(-1.0f, 1.0f)); mCache->addManualResource(box); createScene(); Entity* sun = mScene->createEntity(); Light* sunLight = sun->createComponent(); sunLight->setLightType(LIGHT_DIRECTIONAL); sunLight->setDirection(Vector3(0.5f, -1.0f, 0.5f)); sunLight->setColor(Color(0.2f, 0.2f, 0.2f)); sunLight->setSpecularIntensity(1.0); //sunLight->setCastShadows(true); //sunLight->setShadowCascade(CascadeParameters(3, 0.95f, 0.2f, 500.0f)); Light* cameraLight = mCameraEntity->createComponent(); cameraLight->setLightType(LIGHT_SPOT); cameraLight->setDirection(Vector3(0.0f, 0.0f, 1.0f)); cameraLight->setRange(50.0f); cameraLight->setColor(Color(2.0f, 2.0f, 2.0f)); cameraLight->setSpecularIntensity(2.0f); cameraLight->setCastShadows(true); cameraLight->setShadowResolution(0.5f); cameraLight->setShadowFocus(FocusParameters(false, false, false, 0.5f, 3.0f)); cameraLight->setRampTexture(mCache->getResource("Textures/RampWide.png")); cameraLight->setSpotTexture(mCache->getResource("Textures/SpotWide.png")); camera->addChild(cameraLight); } void Application::createScene() { PROFILE(App_CreateScene); Octree* octree = mScene->getExtension(); PhysicsWorld* world = mScene->getExtension(); // Create a zone to control the ambient lighting Zone* zone = new Zone(octree); zone->setBoundingBox(BoundingBox(-1000.0f, 1000.0f)); zone->setAmbientColor(Color(0.1f, 0.1f, 0.1f)); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(zone); // Create the "floor" for (int y = -5; y <= 5; y++) { for (int x = -5; x <= 5; x++) { RigidBody* body = new RigidBody(world); body->setPosition(Vector3(x * 20.5f, -0.5f, y * 20.5f)); body->setScale(Vector3(10.0f, 0.5f, 10.0f)); body->setCollisionShape(mCache->getResource("Physics/Box.xml")); body->setCollisionGroup(2); body->setCollisionMask(1); StaticModel* object = new StaticModel(octree); object->setModel(mCache->getResource("Models/Box.mdl")); object->setMaterial(mCache->getResource("Materials/Test.xml")); body->addChild(object); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(body); newEntity->addComponent(object); } } // Create 2 occluder walls for (int x = 0; x < 2; x++) { RigidBody* body = new RigidBody(world); body->setPosition(Vector3(0.0f, 5.0f, 0.0f)); body->setRotation(Quaternion(x * 90.0f, Vector3::sUp)); body->setScale(Vector3(112.0f, 5.0f, 0.5f)); body->setCollisionShape(mCache->getResource("Physics/Box.xml")); body->setCollisionGroup(2); body->setCollisionMask(1); StaticModel* object = new StaticModel(octree); object->setModel(mCache->getResource("Models/Box.mdl")); object->setMaterial(mCache->getResource("Materials/Test.xml")); object->setCastShadows(true); object->setOccluder(true); body->addChild(object); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(body); newEntity->addComponent(object); } // Create static mushroom with physics { RigidBody* body = new RigidBody(world); body->setPosition(Vector3(50.0f, 0.0f, 50.0f)); body->setScale(10.0f); body->setCollisionShape(mCache->getResource("Physics/Mushroom.xml")); body->setCollisionGroup(2); body->setCollisionMask(1); StaticModel* object = new StaticModel(octree); object->setModel(mCache->getResource("Models/Mushroom.mdl")); object->setMaterial(mCache->getResource("Materials/Mushroom.xml")); object->setCastShadows(true); object->setOccluder(true); body->addChild(object); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(body); newEntity->addComponent(object); } // Create instanced mushrooms for (unsigned j = 0; j < NUM_INSTANCENODES; ++j) { InstancedModel* instanced = new InstancedModel(octree); instanced->setModel(mCache->getResource("Models/Mushroom.mdl")); instanced->setMaterial(mCache->getResource("Materials/Mushroom.xml")); instanced->setPosition(Vector3(random() * 160.0f - 80.0f, 0.0f, random() * 160.0f - 80.0f)); instanced->setCastShadows(true); instanced->setNumInstances(50); std::vector& instances = instanced->getInstances(); for (unsigned i = 0; i < NUM_INSTANCES; ++i) { Vector3 position(random() * 20.0f - 10.0f, 0.0f, random() * 20.0f - 10.0f); float angle = random() * 360.0f; float size = 1.0f + random() * 2.0f; instances[i].mPosition = position; instances[i].mRotation = Quaternion(angle, Vector3::sUp); instances[i].mScale = Vector3(size, size, size); } instanced->updated(); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(instanced); } // Create animated models for (unsigned i = 0; i < NUM_OBJECTS; ++i) { AnimatedModel* object = new AnimatedModel(octree); Vector3 position(random() * 180.0f - 90.0f, 0.0f, random() * 180.0f - 90.0f); float angle = random() * 360.0f; object->setPosition(position); object->setRotation(Quaternion(angle, Vector3::sUp)); object->setCastShadows(true); object->setScale(1.0f + random() * 0.25f); object->setModel(mCache->getResource("Models/Jack.mdl")); object->setMaterial(mCache->getResource("Materials/Jack.xml")); object->setShadowDistance(200.0f); object->setDrawDistance(300.0f); AnimationState* anim = object->addAnimationState(mCache->getResource("Models/Jack_Walk.ani")); if (anim) { anim->setUseNlerp(true); anim->setLooped(true); anim->setWeight(1.0f); } Entity* newEntity = mScene->createEntity(); newEntity->addComponent(object); mAnimatingObjects.push_back(newEntity); } // Create floating smoke clouds for (unsigned b = 0; b < NUM_BILLBOARDNODES; ++b) { BillboardSet* billboard = new BillboardSet(octree); billboard->setNumBillboards(NUM_BILLBOARDS); billboard->setPosition(Vector3(random() * 200.0f - 100.0f, random() * 15.0f + 5.0f, random() * 200.0f - 100.0f)); billboard->setMaterial(mCache->getResource("Materials/LitSmoke.xml")); billboard->setBillboardsSorted(true); std::vector& bb = billboard->getBillboards(); for (unsigned i = 0; i < NUM_BILLBOARDS; ++i) { bb[i].mPosition = Vector3(random() * 15.0f - 7.5f, random() * 8.0f - 4.0f, random() * 15.0f - 7.5f); bb[i].mSize = Vector2(random() * 2.0f + 3.0f, random() * 2.0f + 3.0f); bb[i].mRotation = random() * 360.0f; bb[i].mEnabled = true; } billboard->updated(); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(billboard); mBillboards.push_back(newEntity); } // Create lights for (unsigned i = 0; i < NUM_LIGHTS; ++i) { Light* light = new Light(octree); Vector3 position( random() * 150.0f - 75.0f, random() * 30.0f + 30.0f, random() * 150.0f - 75.0f ); Color color( (rand() & 1) * 0.5f + 0.5f, (rand() & 1) * 0.5f + 0.5f, (rand() & 1) * 0.5f + 0.5f ); if ((color.mR == 0.5f) && (color.mG == 0.5f) && (color.mB == 0.5f)) color = Color(1.0f, 1.0f, 1.0f); float angle = random() * 360.0f; light->setPosition(position); light->setDirection(Vector3(sinf(angle * M_DEGTORAD), -1.0f, cosf(angle * M_DEGTORAD))); light->setLightType(LIGHT_SPOT); light->setRange(75.0f); light->setRampTexture(mCache->getResource("Textures/RampExtreme.png")); light->setFov(15.0f); light->setColor(color); light->setSpecularIntensity(1.0f); light->setCastShadows(true); light->setShadowBias(BiasParameters(0.00002f, 0.0f)); light->setShadowResolution(0.5f); // The spot lights will not have anything near them, so move the near plane of the shadow camera farther // for better shadow depth resolution light->setShadowNearFarRatio(0.01f); Entity* newEntity = mScene->createEntity(); newEntity->addComponent(light); mLights.push_back(newEntity); } } void Application::handleUpdate(StringHash eventType, VariantMap& eventData) { using namespace Update; float timeStep = eventData[P_TIMESTEP].getFloat(); if (!paused) { objectangle += 10.0f * timeStep; for (unsigned i = 0; i < mLights.size(); ++i) { Light* light = mLights[i]->getComponent(); light->setRotation(Quaternion(0, objectangle * 2, 0)); } for (unsigned i = 0; i < mAnimatingObjects.size(); ++i) { AnimatedModel* model = mAnimatingObjects[i]->getComponent(); const std::vector& anims = model->getAnimationStates(); for (unsigned j = 0; j < anims.size(); ++j) anims[j]->addTime(timeStep); } for (unsigned i = 0; i < mBillboards.size(); ++i) { BillboardSet* billboard = mBillboards[i]->getComponent(); std::vector& bb = billboard->getBillboards(); for (unsigned j = 0; j < bb.size(); ++j) bb[j].mRotation += 50.0f * timeStep; billboard->updated(); } } Input* input = mEngine->getInput(); float speedMultiplier = 1.0f; if (input->getKeyDown(KEY_SHIFT)) speedMultiplier = 5.0f; if (input->getKeyDown(KEY_CONTROL)) speedMultiplier = 0.1f; if (input->getKeyDown('W')) mCameraEntity->getComponent()->translateRelative(Vector3(0.0f,0.0f,10.0f) * timeStep * speedMultiplier); if (input->getKeyDown('S')) mCameraEntity->getComponent()->translateRelative(Vector3(0.0f,0.0f,-10.0f) * timeStep * speedMultiplier); if (input->getKeyDown('A')) mCameraEntity->getComponent()->translateRelative(Vector3(-10.0f,0.0f,0.0f) * timeStep * speedMultiplier); if (input->getKeyDown('D')) mCameraEntity->getComponent()->translateRelative(Vector3(10.0f,0.0f,0.0f) * timeStep * speedMultiplier); if (input->getKeyPress('1')) { Renderer* renderer = mEngine->getRenderer(); int nextRenderMode = renderer->getRenderMode(); if (input->getKeyDown(KEY_SHIFT)) { --nextRenderMode; if (nextRenderMode < 0) nextRenderMode = 2; } else { ++nextRenderMode; if (nextRenderMode > 2) nextRenderMode = 0; } renderer->setMode((RenderMode)nextRenderMode, renderer->getWidth(), renderer->getHeight(), renderer->getFullscreen(), renderer->getVsync(), renderer->getMultiSample()); } if (input->getKeyPress('2')) { texturequality++; if (texturequality > 2) texturequality = 0; mEngine->getPipeline()->setTextureQuality(texturequality); } if (input->getKeyPress('3')) { materialquality++; if (materialquality > 2) materialquality = 0; mEngine->getPipeline()->setMaterialQuality(materialquality); } if (input->getKeyPress('4')) { usespecular = !usespecular; mEngine->getPipeline()->setSpecularLighting(usespecular); } if (input->getKeyPress('5')) { drawshadows = !drawshadows; mEngine->getPipeline()->setDrawShadows(drawshadows); } if (input->getKeyPress('6')) { shadowmapsize *= 2; if (shadowmapsize > 2048) shadowmapsize = 512; mEngine->getPipeline()->setShadowMapSize(shadowmapsize); } if (input->getKeyPress('7')) { hiresshadowmap = !hiresshadowmap; mEngine->getPipeline()->setShadowMapHiresDepth(hiresshadowmap); } if (input->getKeyPress('8')) { useocclusion = !useocclusion; mEngine->getPipeline()->setMaxOccluderTriangles(useocclusion ? 5000 : 0); } if (input->getKeyPress('L')) { Light* cameraLight = mCameraEntity->getComponent(); attach = !attach; if (attach) { cameraLight->setPosition(Vector3::sZero); cameraLight->setRotation(Quaternion::sIdentity); mCameraEntity->getComponent()->addChild(cameraLight); } else { // Detach child and set world transform to match what it was before detach mCameraEntity->getComponent()->removeChild(cameraLight, true); } } if (input->getKeyPress(' ')) { drawdebug++; if (drawdebug > 2) drawdebug = 0; mEngine->setDebugDrawMode(drawdebug); } if (input->getKeyPress('P')) { paused = !paused; } if (input->getKeyPress('C')) { Camera* camera = mCameraEntity->getComponent(); camera->setOrthographic(!camera->isOrthographic()); } if (input->getKeyPress('O')) { if (!mOcclusionDebugImage) { try { Renderer* renderer = mEngine->getRenderer(); UIElement* uiRoot = mEngine->getUIRoot(); mOcclusionDebugTexture = new Texture2D(renderer, TEXTURE_DYNAMIC); mOcclusionDebugTexture->setNumLevels(1); mOcclusionDebugTexture->setSize(256, 256, D3DFMT_R32F); mOcclusionDebugImage = new BorderImage(); mOcclusionDebugImage->setSize(256, 256); mOcclusionDebugImage->setTexture(mOcclusionDebugTexture); mOcclusionDebugImage->setAlignment(HA_RIGHT, VA_BOTTOM); uiRoot->addChild(mOcclusionDebugImage); } catch (...) { } } else { mOcclusionDebugImage->setVisible(!mOcclusionDebugImage->isVisible()); } } if (input->getKeyPress('T')) mEngine->getDebugHud()->toggle(DEBUGHUD_SHOW_PROFILER); if (input->getKeyPress('F')) { Pipeline* pipeline = mEngine->getPipeline(); EdgeFilterParameters params = pipeline->getEdgeFilter(); if (params.mMaxFilter > 0.0f) params.mMaxFilter = 0.0f; else params.mMaxFilter = 1.0f; pipeline->setEdgeFilter(params); } if (input->getKeyPress(KEY_ESCAPE)) mEngine->exit(); } void Application::handlePostRenderUpdate(StringHash eventType, VariantMap& eventData) { using namespace PostRenderUpdate; float timeStep = eventData[P_TIMESTEP].getFloat(); // Test world raycast Camera* camera = mCameraEntity->getComponent(); UI* ui = mEngine->getUI(); Renderer* renderer = mEngine->getRenderer(); DebugRenderer* debug = mEngine->getDebugRenderer(); if (camera) { IntVector2 pos = ui->getCursorPosition(); if (!ui->getElementAt(pos)) { Ray cameraRay = camera->getScreenRay(((float)pos.mX) / renderer->getWidth(), ((float)pos.mY) / renderer->getHeight()); std::vector result; RayOctreeQuery query(cameraRay, result, NODE_GEOMETRY, NODE_BILLBOARDSET, false, false, 250.0f, RAY_TRIANGLE); mScene->getExtension()->getNodes(query); if (result.size()) { VolumeNode* node = result[0].mNode; Vector3 rayHitPos = cameraRay.mOrigin + query.mResult[0].mDistance * cameraRay.mDirection; debug->addBoundingBox(BoundingBox(-0.01f, 0.01f), Matrix4x3(rayHitPos, Quaternion::sIdentity, Vector3::sUnity), Color(1.0f, 1.0f, 1.0f)); // Check for sub-object results (node-specific) if (result[0].mSubObject < M_MAX_UNSIGNED) { // Bone if (node->getNodeFlags() & NODE_ANIMATEDMODEL) { AnimatedModel* anim = static_cast(node); Bone* bone = anim->getSkeleton().getBone(result[0].mSubObject); debug->addBoundingBox(bone->getBoundingBox(), bone->getWorldTransform(), Color(1.0f, 1.0f, 1.0f)); } // Instance else if (node->getNodeFlags() & NODE_INSTANCEDMODEL) { InstancedModel* instanced = static_cast(node); Instance* instance = instanced->getInstance(result[0].mSubObject); Matrix4x3 transform(instance->mPosition, instance->mRotation, instance->mScale); if (instanced->getInstancesRelative()) transform = instanced->getWorldTransform() * transform; debug->addBoundingBox(instanced->getBoundingBox().getTransformed(transform), Color(1.0f, 1.0f, 1.0f)); } // Custom object subgeometry else if (node->getNodeFlags() & NODE_CUSTOMOBJECT) { CustomObject* custom = static_cast(node); const CustomGeometry* geom = custom->getGeometry(result[0].mSubObject); debug->addBoundingBox(geom->mBoundingBox.getTransformed(custom->getWorldTransform()), Color(1.0f, 1.0f, 1.0f)); } } else debug->addBoundingBox(node->getWorldBoundingBox(), Color(1.0f, 1.0f, 1.0f)); } } } // Update occlusion debug texture if visible if ((mOcclusionDebugImage) && (mOcclusionDebugImage->isVisible())) { PROFILE(App_UpdateOcclusionDebugTextures); static const float INV_Z_SCALE = 1.0f / (OCCLUSION_Z_SCALE); // Dump occlusion depth buffer to debug texture // Get an occlusion buffer matching the aspect ratio, it should be the main view occlusion buffer const OcclusionBuffer* buffer = mEngine->getPipeline()->getOcclusionBuffer(camera->getAspectRatio()); if (buffer) { int width = buffer->getWidth(); int height = buffer->getHeight(); if (buffer->getBuffer()) { mOcclusionDebugTexture->setSize(width, height, D3DFMT_R32F); mOcclusionDebugImage->setSize(width, height); mOcclusionDebugImage->setFullImageRect(); D3DLOCKED_RECT hwRect; mOcclusionDebugTexture->lock(0, 0, &hwRect); for (int y = 0; y < height; ++y) { float* dest = (float*)(((unsigned char*)hwRect.pBits) + y * hwRect.Pitch); int* src = buffer->getBuffer() + y * width; for (int x = 0; x < width; ++x) { float depth = src[x] * INV_Z_SCALE; dest[x] = depth * 0.5f; } } mOcclusionDebugTexture->unlock(); } } } } void Application::handleMouseButtonDown(StringHash eventType, VariantMap& eventData) { using namespace MouseButtonDown; int button = eventData[P_BUTTON].getInt(); if (button == MOUSEB_RIGHT) { UIElement* cursor = mEngine->getUICursor(); if (cursor) cursor->setVisible(false); } if (button == MOUSEB_LEFT) { // Test creating a new physics object if (mCameraEntity) { Camera* camera = mCameraEntity->getComponent(); Octree* octree = mScene->getExtension(); PhysicsWorld* world = mScene->getExtension(); Entity* newEntity = mScene->createEntity(); RigidBody* body = new RigidBody(world); body->setMode(PHYS_DYNAMIC); body->setPosition(camera->getPosition()); body->setRotation(camera->getRotation()); body->setScale(0.1f); body->setFriction(1.0f); body->setAngularMaxVelocity(500.0f); body->setCollisionShape(mCache->getResource("Physics/Box.xml")); body->setCollisionGroup(1); body->setCollisionMask(3); body->setLinearVelocity(camera->getUpVector() + camera->getForwardVector() * 10.0f); StaticModel* object = new StaticModel(octree); object->setModel(mCache->getResource("Models/Box.mdl")); object->setMaterial(mCache->getResource("Materials/Test.xml")); object->setCastShadows(true); object->setShadowDistance(75.0f); object->setDrawDistance(100.0f); body->addChild(object); newEntity->addComponent(body); newEntity->addComponent(object); } } } void Application::handleMouseButtonUp(StringHash eventType, VariantMap& eventData) { using namespace MouseButtonDown; if (eventData[P_BUTTON].getInt() == MOUSEB_RIGHT) { UIElement* cursor = mEngine->getUICursor(); if (cursor) cursor->setVisible(true); } } void Application::handleMouseMove(StringHash eventType, VariantMap& eventData) { using namespace MouseMove; if (eventData[P_BUTTONS].getInt() & MOUSEB_RIGHT) { int mousedx = eventData[P_X].getInt(); int mousedy = eventData[P_Y].getInt(); yaw += mousedx / 10.0f; pitch += mousedy / 10.0f; if (pitch < -90.0f) pitch = -90.0f; if (pitch > 90.0f) pitch = 90.0f; if (mCameraEntity) mCameraEntity->getComponent()->setRotation(Quaternion(yaw, Vector3::sUp) * Quaternion(pitch, Vector3::sRight)); } } void calculateTangents(float* vertexData, unsigned vertexCount, const unsigned short* indexData, unsigned indexCount) { // Tangent generation from // http://www.terathon.com/code/tangent.html static const int V_OFS = 0; static const int N_OFS = 3; static const int UV_OFS = 6; static const int T_OFS = 8; static const int V_SIZE = 12; Vector3 *tan1 = new Vector3[vertexCount * 2]; Vector3 *tan2 = tan1 + vertexCount; memset(tan1, 0, sizeof(Vector3) * vertexCount * 2); for (unsigned a = 0; a < indexCount; a += 3) { unsigned short i1 = indexData[a+0]; unsigned short i2 = indexData[a+1]; unsigned short i3 = indexData[a+2]; const Vector3 v1 = Vector3(&vertexData[i1 * V_SIZE + V_OFS]); const Vector3 v2 = Vector3(&vertexData[i2 * V_SIZE + V_OFS]); const Vector3 v3 = Vector3 (&vertexData[i3 * V_SIZE + V_OFS]); const Vector2 w1 = Vector2(&vertexData[i1 * V_SIZE + UV_OFS]); const Vector2 w2 = Vector2(&vertexData[i2 * V_SIZE + UV_OFS]); const Vector2 w3 = Vector2(&vertexData[i3 * V_SIZE + UV_OFS]); float x1 = v2.mX - v1.mX; float x2 = v3.mX - v1.mX; float y1 = v2.mY - v1.mY; float y2 = v3.mY - v1.mY; float z1 = v2.mZ - v1.mZ; float z2 = v3.mZ - v1.mZ; float s1 = w2.mX - w1.mX; float s2 = w3.mX - w1.mX; float t1 = w2.mY - w1.mY; float t2 = w3.mY - w1.mY; float r = 1.0f / (s1 * t2 - s2 * t1); Vector3 sdir((t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r); Vector3 tdir((s1 * x2 - s2 * x1) * r, (s1 * y2 - s2 * y1) * r, (s1 * z2 - s2 * z1) * r); tan1[i1] += sdir; tan1[i2] += sdir; tan1[i3] += sdir; tan2[i1] += tdir; tan2[i2] += tdir; tan2[i3] += tdir; } for (unsigned a = 0; a < vertexCount; a++) { const Vector3 n = Vector3(&vertexData[a * V_SIZE + N_OFS]); const Vector3 t = Vector3(tan1[a]); Vector3 xyz; float w; // Gram-Schmidt orthogonalize xyz = (t - n * n.dotProduct(t)).getNormalized(); // Calculate handedness w = n.crossProduct(t).dotProduct(tan2[a]) < 0.0f ? -1.0f : 1.0f; vertexData[a * V_SIZE + T_OFS] = xyz.mX; vertexData[a * V_SIZE + T_OFS + 1] = xyz.mY; vertexData[a * V_SIZE + T_OFS + 2] = xyz.mZ; vertexData[a * V_SIZE + T_OFS + 3] = w; } delete[] tan1; }