示例#1
0
void ListView::ChangeSelection(int delta, bool additive)
{
    unsigned numItems = GetNumItems();
    if (selections_.Empty())
    {
        // Select first item if there is no selection yet
        if (numItems > 0)
            SetSelection(0);
        if (abs(delta) == 1)
            return;
    }
    if (!multiselect_)
        additive = false;

    // If going downwards, use the last selection as a base. Otherwise use first
    unsigned selection = delta > 0 ? selections_.Back() : selections_.Front();
    int direction = delta > 0 ? 1 : -1;
    unsigned newSelection = selection;
    unsigned okSelection = selection;
    PODVector<unsigned> indices = selections_;

    while (delta != 0)
    {
        newSelection += direction;
        if (newSelection >= numItems)
            break;

        UIElement* item = GetItem(newSelection);
        if (item->IsVisible())
        {
            indices.Push(okSelection = newSelection);
            delta -= direction;
        }
    }

    if (!additive)
        SetSelection(okSelection);
    else
        SetSelections(indices);
}
示例#2
0
void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask)
{
    PROFILE(PhysicsRaycast);

    btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ +
        maxDistance * ray.direction_));
    rayCallback.m_collisionFilterGroup = (short)0xffff;
    rayCallback.m_collisionFilterMask = collisionMask;

    world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback);

    for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i)
    {
        PhysicsRaycastResult newResult;
        newResult.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObjects[i]->getUserPointer());
        newResult.position_ = ToVector3(rayCallback.m_hitPointWorld[i]);
        newResult.normal_ = ToVector3(rayCallback.m_hitNormalWorld[i]);
        newResult.distance_ = (newResult.position_ - ray.origin_).Length();
        result.Push(newResult);
    }

    Sort(result.Begin(), result.End(), CompareRaycastResults);
}
VertexDeclaration::VertexDeclaration(Graphics* graphics, unsigned elementMask) :
    declaration_(0)
{
    PODVector<VertexDeclarationElement> elements;
    unsigned offset = 0;
    
    for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i)
    {
        VertexElement element = (VertexElement)i;
        
        if (elementMask & (1 << i))
        {
            VertexDeclarationElement newElement;
            newElement.stream_ = 0;
            newElement.element_ = element;
            newElement.offset_ = offset;
            offset += VertexBuffer::elementSize[i];
            
            elements.Push(newElement);
        }
    }
    
    Create(graphics, elements);
}
void DynamicGeometry::CreateScene()
{
    ResourceCache* cache = GetSubsystem<ResourceCache>();

    scene_ = new Scene(context_);

    // Create the Octree component to the scene so that drawable objects can be rendered. Use default volume
    // (-1000, -1000, -1000) to (1000, 1000, 1000)
    scene_->CreateComponent<Octree>();

    // Create a Zone for ambient light & fog control
    Node* zoneNode = scene_->CreateChild("Zone");
    Zone* zone = zoneNode->CreateComponent<Zone>();
    zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
    zone->SetFogColor(Color(0.2f, 0.2f, 0.2f));
    zone->SetFogStart(200.0f);
    zone->SetFogEnd(300.0f);

    // Create a directional light
    Node* lightNode = scene_->CreateChild("DirectionalLight");
    lightNode->SetDirection(Vector3(-0.6f, -1.0f, -0.8f)); // The direction vector does not need to be normalized
    Light* light = lightNode->CreateComponent<Light>();
    light->SetLightType(LIGHT_DIRECTIONAL);
    light->SetColor(Color(0.4f, 1.0f, 0.4f));
    light->SetSpecularIntensity(1.5f);

    // Get the original model and its unmodified vertices, which are used as source data for the animation
    Model* originalModel = cache->GetResource<Model>("Models/Box.mdl");
    if (!originalModel)
    {
        ATOMIC_LOGERROR("Model not found, cannot initialize example scene");
        return;
    }
    // Get the vertex buffer from the first geometry's first LOD level
    VertexBuffer* buffer = originalModel->GetGeometry(0, 0)->GetVertexBuffer(0);
    const unsigned char* vertexData = (const unsigned char*)buffer->Lock(0, buffer->GetVertexCount());
    if (vertexData)
    {
        unsigned numVertices = buffer->GetVertexCount();
        unsigned vertexSize = buffer->GetVertexSize();
        // Copy the original vertex positions
        for (unsigned i = 0; i < numVertices; ++i)
        {
            const Vector3& src = *reinterpret_cast<const Vector3*>(vertexData + i * vertexSize);
            originalVertices_.Push(src);
        }
        buffer->Unlock();

        // Detect duplicate vertices to allow seamless animation
        vertexDuplicates_.Resize(originalVertices_.Size());
        for (unsigned i = 0; i < originalVertices_.Size(); ++i)
        {
            vertexDuplicates_[i] = i; // Assume not a duplicate
            for (unsigned j = 0; j < i; ++j)
            {
                if (originalVertices_[i].Equals(originalVertices_[j]))
                {
                    vertexDuplicates_[i] = j;
                    break;
                }
            }
        }
    }
    else
    {
        ATOMIC_LOGERROR("Failed to lock the model vertex buffer to get original vertices");
        return;
    }

    // Create StaticModels in the scene. Clone the model for each so that we can modify the vertex data individually
    for (int y = -1; y <= 1; ++y)
    {
        for (int x = -1; x <= 1; ++x)
        {
            Node* node = scene_->CreateChild("Object");
            node->SetPosition(Vector3(x * 2.0f, 0.0f, y * 2.0f));
            StaticModel* object = node->CreateComponent<StaticModel>();
            SharedPtr<Model> cloneModel = originalModel->Clone();
            object->SetModel(cloneModel);
            // Store the cloned vertex buffer that we will modify when animating
            animatingBuffers_.Push(SharedPtr<VertexBuffer>(cloneModel->GetGeometry(0, 0)->GetVertexBuffer(0)));
        }
    }

    // Finally create one model (pyramid shape) and a StaticModel to display it from scratch
    // Note: there are duplicated vertices to enable face normals. We will calculate normals programmatically
    {
        const unsigned numVertices = 18;

        float vertexData[] = {
            // Position             Normal
            0.0f, 0.5f, 0.0f,       0.0f, 0.0f, 0.0f,
            0.5f, -0.5f, 0.5f,      0.0f, 0.0f, 0.0f,
            0.5f, -0.5f, -0.5f,     0.0f, 0.0f, 0.0f,

            0.0f, 0.5f, 0.0f,       0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.5f,     0.0f, 0.0f, 0.0f,
            0.5f, -0.5f, 0.5f,      0.0f, 0.0f, 0.0f,

            0.0f, 0.5f, 0.0f,       0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,    0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.5f,     0.0f, 0.0f, 0.0f,

            0.0f, 0.5f, 0.0f,       0.0f, 0.0f, 0.0f,
            0.5f, -0.5f, -0.5f,     0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,    0.0f, 0.0f, 0.0f,

            0.5f, -0.5f, -0.5f,     0.0f, 0.0f, 0.0f,
            0.5f, -0.5f, 0.5f,      0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.5f,     0.0f, 0.0f, 0.0f,

            0.5f, -0.5f, -0.5f,     0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, 0.5f,     0.0f, 0.0f, 0.0f,
            -0.5f, -0.5f, -0.5f,    0.0f, 0.0f, 0.0f
        };

        const unsigned short indexData[] = {
            0, 1, 2,
            3, 4, 5,
            6, 7, 8,
            9, 10, 11,
            12, 13, 14,
            15, 16, 17
        };

        // Calculate face normals now
        for (unsigned i = 0; i < numVertices; i += 3)
        {
            Vector3& v1 = *(reinterpret_cast<Vector3*>(&vertexData[6 * i]));
            Vector3& v2 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 1)]));
            Vector3& v3 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 2)]));
            Vector3& n1 = *(reinterpret_cast<Vector3*>(&vertexData[6 * i + 3]));
            Vector3& n2 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 1) + 3]));
            Vector3& n3 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 2) + 3]));

            Vector3 edge1 = v1 - v2;
            Vector3 edge2 = v1 - v3;
            n1 = n2 = n3 = edge1.CrossProduct(edge2).Normalized();
        }

        SharedPtr<Model> fromScratchModel(new Model(context_));
        SharedPtr<VertexBuffer> vb(new VertexBuffer(context_));
        SharedPtr<IndexBuffer> ib(new IndexBuffer(context_));
        SharedPtr<Geometry> geom(new Geometry(context_));

        // Shadowed buffer needed for raycasts to work, and so that data can be automatically restored on device loss
        vb->SetShadowed(true);
        // We could use the "legacy" element bitmask to define elements for more compact code, but let's demonstrate
        // defining the vertex elements explicitly to allow any element types and order
        PODVector<VertexElement> elements;
        elements.Push(VertexElement(TYPE_VECTOR3, SEM_POSITION));
        elements.Push(VertexElement(TYPE_VECTOR3, SEM_NORMAL));
        vb->SetSize(numVertices, elements);
        vb->SetData(vertexData);

        ib->SetShadowed(true);
        ib->SetSize(numVertices, false);
        ib->SetData(indexData);

        geom->SetVertexBuffer(0, vb);
        geom->SetIndexBuffer(ib);
        geom->SetDrawRange(TRIANGLE_LIST, 0, numVertices);

        fromScratchModel->SetNumGeometries(1);
        fromScratchModel->SetGeometry(0, 0, geom);
        fromScratchModel->SetBoundingBox(BoundingBox(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f)));

        // Though not necessary to render, the vertex & index buffers must be listed in the model so that it can be saved properly
        Vector<SharedPtr<VertexBuffer> > vertexBuffers;
        Vector<SharedPtr<IndexBuffer> > indexBuffers;
        vertexBuffers.Push(vb);
        indexBuffers.Push(ib);
        // Morph ranges could also be not defined. Here we simply define a zero range (no morphing) for the vertex buffer
        PODVector<unsigned> morphRangeStarts;
        PODVector<unsigned> morphRangeCounts;
        morphRangeStarts.Push(0);
        morphRangeCounts.Push(0);
        fromScratchModel->SetVertexBuffers(vertexBuffers, morphRangeStarts, morphRangeCounts);
        fromScratchModel->SetIndexBuffers(indexBuffers);

        Node* node = scene_->CreateChild("FromScratchObject");
        node->SetPosition(Vector3(0.0f, 3.0f, 0.0f));
        StaticModel* object = node->CreateComponent<StaticModel>();
        object->SetModel(fromScratchModel);
    }

    // Create the camera
    cameraNode_ = new Node(context_);
    cameraNode_->SetPosition(Vector3(0.0f, 2.0f, -20.0f));
    Camera* camera = cameraNode_->CreateComponent<Camera>();
    camera->SetFarClip(300.0f);
}
示例#5
0
void AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
{
    // If no bones or no bone-level testing, use the StaticModel test
    RayQueryLevel level = query.level_;
    if (level < RAY_TRIANGLE || !skeleton_.GetNumBones())
    {
        StaticModel::ProcessRayQuery(query, results);
        return;
    }

    // Check ray hit distance to AABB before proceeding with bone-level tests
    if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
        return;

    const Vector<Bone>& bones = skeleton_.GetBones();
    Sphere boneSphere;

    for (unsigned i = 0; i < bones.Size(); ++i)
    {
        const Bone& bone = bones[i];
        if (!bone.node_)
            continue;

        float distance;

        // Use hitbox if available
        if (bone.collisionMask_ & BONECOLLISION_BOX)
        {
            // Do an initial crude test using the bone's AABB
            const BoundingBox& box = bone.boundingBox_;
            const Matrix3x4& transform = bone.node_->GetWorldTransform();
            distance = query.ray_.HitDistance(box.Transformed(transform));
            if (distance >= query.maxDistance_)
                continue;
            if (level != RAY_AABB)
            {
                // Follow with an OBB test if required
                Matrix3x4 inverse = transform.Inverse();
                Ray localRay = query.ray_.Transformed(inverse);
                distance = localRay.HitDistance(box);
                if (distance >= query.maxDistance_)
                    continue;
            }
        }
        else if (bone.collisionMask_ & BONECOLLISION_SPHERE)
        {
            boneSphere.center_ = bone.node_->GetWorldPosition();
            boneSphere.radius_ = bone.radius_;
            distance = query.ray_.HitDistance(boneSphere);
            if (distance >= query.maxDistance_)
                continue;
        }
        else
            continue;

        // If the code reaches here then we have a hit
        RayQueryResult result;
        result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
        result.normal_ = -query.ray_.direction_;
        result.distance_ = distance;
        result.drawable_ = this;
        result.node_ = node_;
        result.subObject_ = i;
        results.Push(result);
    }
}
bool ShaderVariation::Compile()
{
    const String& sourceCode = owner_->GetSourceCode(type_);
    Vector<String> defines = defines_.Split(' ');

    // Set the entrypoint, profile and flags according to the shader being compiled
    const char* entryPoint = 0;
    const char* profile = 0;
    unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;

    if (type_ == VS)
    {
        entryPoint = "VS";
        defines.Push("COMPILEVS");
        profile = "vs_3_0";
    }
    else
    {
        entryPoint = "PS";
        defines.Push("COMPILEPS");
        profile = "ps_3_0";
        flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
    }

    defines.Push("MAXBONES=" + String(Graphics::GetMaxBones()));

    // Collect defines into macros
    Vector<String> defineValues;
    PODVector<D3D_SHADER_MACRO> macros;

    for (unsigned i = 0; i < defines.Size(); ++i)
    {
        unsigned equalsPos = defines[i].Find('=');
        if (equalsPos != String::NPOS)
        {
            defineValues.Push(defines[i].Substring(equalsPos + 1));
            defines[i].Resize(equalsPos);
        }
        else
            defineValues.Push("1");
    }
    for (unsigned i = 0; i < defines.Size(); ++i)
    {
        D3D_SHADER_MACRO macro;
        macro.Name = defines[i].CString();
        macro.Definition = defineValues[i].CString();
        macros.Push(macro);

        // In debug mode, check that all defines are referenced by the shader code
#ifdef _DEBUG
        if (sourceCode.Find(defines[i]) == String::NPOS)
            ATOMIC_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]);
#endif
    }

    D3D_SHADER_MACRO endMacro;
    endMacro.Name = 0;
    endMacro.Definition = 0;
    macros.Push(endMacro);

    // Compile using D3DCompile
    ID3DBlob* shaderCode = 0;
    ID3DBlob* errorMsgs = 0;

    HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), &macros.Front(), 0,
        entryPoint, profile, flags, 0, &shaderCode, &errorMsgs);
    if (FAILED(hr))
    {
        // Do not include end zero unnecessarily
        compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1);
    }
    else
    {
        if (type_ == VS)
            ATOMIC_LOGDEBUG("Compiled vertex shader " + GetFullName());
        else
            ATOMIC_LOGDEBUG("Compiled pixel shader " + GetFullName());

        // Inspect the produced bytecode using MojoShader, then strip and store it
        unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer();
        unsigned bufSize = (unsigned)shaderCode->GetBufferSize();
        ParseParameters(bufData, bufSize);
        CopyStrippedCode(byteCode_, bufData, bufSize);
    }

    ATOMIC_SAFE_RELEASE(shaderCode);
    ATOMIC_SAFE_RELEASE(errorMsgs);

    return !byteCode_.Empty();
}
VertexDeclaration::VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** vertexBuffers) :
    inputLayout_(0)
{
    PODVector<D3D11_INPUT_ELEMENT_DESC> elementDescs;
    unsigned prevBufferDescs = 0;

    for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i)
    {
        if (!vertexBuffers[i])
            continue;

        const PODVector<VertexElement>& srcElements = vertexBuffers[i]->GetElements();
        bool isExisting = false;

        for (unsigned j = 0; j < srcElements.Size(); ++j)
        {
            const VertexElement& srcElement = srcElements[j];
            const char* semanticName = ShaderVariation::elementSemanticNames[srcElement.semantic_];

            // Override existing element if necessary
            for (unsigned k = 0; k < prevBufferDescs; ++k)
            {
                if (elementDescs[k].SemanticName == semanticName && elementDescs[k].SemanticIndex == srcElement.index_)
                {
                    isExisting = true;
                    elementDescs[k].InputSlot = i;
                    elementDescs[k].AlignedByteOffset = srcElement.offset_;
                    elementDescs[k].InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
                    elementDescs[k].InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0;
                    break;
                }
            }

            if (isExisting)
                continue;

            D3D11_INPUT_ELEMENT_DESC newDesc;
            newDesc.SemanticName = semanticName;
            newDesc.SemanticIndex = srcElement.index_;
            newDesc.Format = d3dElementFormats[srcElement.type_];
            newDesc.InputSlot = (UINT)i;
            newDesc.AlignedByteOffset = srcElement.offset_;
            newDesc.InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA;
            newDesc.InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0;
            elementDescs.Push(newDesc);
        }

        prevBufferDescs = elementDescs.Size();
    }

    if (elementDescs.Empty())
        return;

    const PODVector<unsigned char>& byteCode = vertexShader->GetByteCode();

    HRESULT hr = graphics->GetImpl()->GetDevice()->CreateInputLayout(&elementDescs[0], (UINT)elementDescs.Size(), &byteCode[0],
        byteCode.Size(), (ID3D11InputLayout**)&inputLayout_);
    if (FAILED(hr))
    {
        ATOMIC_SAFE_RELEASE(inputLayout_);
        ATOMIC_LOGERRORF("Failed to create input layout for shader %s due to missing vertex element(s) (HRESULT %x)",
            vertexShader->GetFullName().CString(), (unsigned)hr);
    }
}
示例#8
0
void OptimizeIndices(ModelSubGeometryLodLevel* subGeom, ModelVertexBuffer* vb, ModelIndexBuffer* ib)
{
    PODVector<Triangle> oldTriangles;
    PODVector<Triangle> newTriangles;

    if (subGeom->indexCount_ % 3)
    {
        PrintLine("Index count is not divisible by 3, skipping index optimization");
        return;
    }

    for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
    {
        vb->vertices_[i].useCount_ = 0;
        vb->vertices_[i].cachePosition_ = -1;
    }

    for (unsigned i = subGeom->indexStart_; i < subGeom->indexStart_ + subGeom->indexCount_; i += 3)
    {
        Triangle triangle;
        triangle.v0_ = ib->indices_[i];
        triangle.v1_ = ib->indices_[i + 1];
        triangle.v2_ = ib->indices_[i + 2];
        vb->vertices_[triangle.v0_].useCount_++;
        vb->vertices_[triangle.v1_].useCount_++;
        vb->vertices_[triangle.v2_].useCount_++;
        oldTriangles.Push(triangle);
    }

    for (unsigned i = 0; i < vb->vertices_.Size(); ++i)
        CalculateScore(vb->vertices_[i]);

    PODVector<unsigned> vertexCache;

    while (oldTriangles.Size())
    {
        unsigned bestTriangle = M_MAX_UNSIGNED;
        float bestTriangleScore = -1.0f;

        // Find the best triangle at this point
        for (unsigned i = 0; i < oldTriangles.Size(); ++i)
        {
            Triangle& triangle = oldTriangles[i];
            float triangleScore =
                vb->vertices_[triangle.v0_].score_ +
                vb->vertices_[triangle.v1_].score_ +
                vb->vertices_[triangle.v2_].score_;

            if (triangleScore > bestTriangleScore)
            {
                bestTriangle = i;
                bestTriangleScore = triangleScore;
            }
        }

        if (bestTriangle == M_MAX_UNSIGNED)
        {
            PrintLine("Could not find next triangle, aborting index optimization");
            return;
        }

        // Add the best triangle
        Triangle triangleCopy = oldTriangles[bestTriangle];
        newTriangles.Push(triangleCopy);
        oldTriangles.Erase(oldTriangles.Begin() + bestTriangle);

        // Reduce the use count
        vb->vertices_[triangleCopy.v0_].useCount_--;
        vb->vertices_[triangleCopy.v1_].useCount_--;
        vb->vertices_[triangleCopy.v2_].useCount_--;

        // Model the LRU cache behaviour
        // Erase the triangle vertices from the middle of the cache, if they were there
        for (unsigned i = 0; i < vertexCache.Size(); ++i)
        {
            if ((vertexCache[i] == triangleCopy.v0_) ||
                    (vertexCache[i] == triangleCopy.v1_) ||
                    (vertexCache[i] == triangleCopy.v2_))
            {
                vertexCache.Erase(vertexCache.Begin() + i);
                --i;
            }
        }

        // Then push them to the front
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v0_);
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v1_);
        vertexCache.Insert(vertexCache.Begin(), triangleCopy.v2_);

        // Update positions & scores of all vertices in the cache
        // Give position -1 if vertex is going to be erased
        for (unsigned i = 0; i < vertexCache.Size(); ++i)
        {
            ModelVertex& vertex = vb->vertices_[vertexCache[i]];
            if (i >= VERTEX_CACHE_SIZE)
                vertex.cachePosition_ = -1;
            else
                vertex.cachePosition_ = i;
            CalculateScore(vertex);
        }

        // Finally erase the extra vertices
        if (vertexCache.Size() > VERTEX_CACHE_SIZE)
            vertexCache.Resize(VERTEX_CACHE_SIZE);
    }

    // Rewrite the index data now
    unsigned i = subGeom->indexStart_;
    for (unsigned j = 0; j < newTriangles.Size(); ++j)
    {
        ib->indices_[i++] = newTriangles[j].v0_;
        ib->indices_[i++] = newTriangles[j].v1_;
        ib->indices_[i++] = newTriangles[j].v2_;
    }
}
示例#9
0
void Terrain::CreateIndexData()
{
    URHO3D_PROFILE(CreateIndexData);

    PODVector<unsigned short> indices;
    drawRanges_.Clear();
    unsigned row = (unsigned)(patchSize_ + 1);

    /* Build index data for each LOD level. Each LOD level except the lowest can stitch to the next lower LOD from the edges:
       north, south, west, east, or any combination of them, requiring 16 different versions of each LOD level's index data

       Normal edge:     Stitched edge:
       +----+----+      +---------+
       |\   |\   |      |\       /|
       | \  | \  |      | \     / |
       |  \ |  \ |      |  \   /  |
       |   \|   \|      |   \ /   |
       +----+----+      +----+----+
    */
    for (unsigned i = 0; i < numLodLevels_; ++i)
    {
        unsigned combinations = (i < numLodLevels_ - 1) ? 16 : 1;
        int skip = 1 << i;

        for (unsigned j = 0; j < combinations; ++j)
        {
            unsigned indexStart = indices.Size();

            int zStart = 0;
            int xStart = 0;
            int zEnd = patchSize_;
            int xEnd = patchSize_;

            if (j & STITCH_NORTH)
                zEnd -= skip;
            if (j & STITCH_SOUTH)
                zStart += skip;
            if (j & STITCH_WEST)
                xStart += skip;
            if (j & STITCH_EAST)
                xEnd -= skip;

            // Build the main grid
            for (int z = zStart; z < zEnd; z += skip)
            {
                for (int x = xStart; x < xEnd; x += skip)
                {
                    indices.Push((unsigned short)((z + skip) * row + x));
                    indices.Push((unsigned short)(z * row + x + skip));
                    indices.Push((unsigned short)(z * row + x));
                    indices.Push((unsigned short)((z + skip) * row + x));
                    indices.Push((unsigned short)((z + skip) * row + x + skip));
                    indices.Push((unsigned short)(z * row + x + skip));
                }
            }

            // Build the north edge
            if (j & STITCH_NORTH)
            {
                int z = patchSize_ - skip;
                for (int x = 0; x < patchSize_; x += skip * 2)
                {
                    if (x > 0 || (j & STITCH_WEST) == 0)
                    {
                        indices.Push((unsigned short)((z + skip) * row + x));
                        indices.Push((unsigned short)(z * row + x + skip));
                        indices.Push((unsigned short)(z * row + x));
                    }
                    indices.Push((unsigned short)((z + skip) * row + x));
                    indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
                    indices.Push((unsigned short)(z * row + x + skip));
                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
                    {
                        indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
                        indices.Push((unsigned short)(z * row + x + 2 * skip));
                        indices.Push((unsigned short)(z * row + x + skip));
                    }
                }
            }

            // Build the south edge
            if (j & STITCH_SOUTH)
            {
                int z = 0;
                for (int x = 0; x < patchSize_; x += skip * 2)
                {
                    if (x > 0 || (j & STITCH_WEST) == 0)
                    {
                        indices.Push((unsigned short)((z + skip) * row + x));
                        indices.Push((unsigned short)((z + skip) * row + x + skip));
                        indices.Push((unsigned short)(z * row + x));
                    }
                    indices.Push((unsigned short)(z * row + x));
                    indices.Push((unsigned short)((z + skip) * row + x + skip));
                    indices.Push((unsigned short)(z * row + x + 2 * skip));
                    if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0)
                    {
                        indices.Push((unsigned short)((z + skip) * row + x + skip));
                        indices.Push((unsigned short)((z + skip) * row + x + 2 * skip));
                        indices.Push((unsigned short)(z * row + x + 2 * skip));
                    }
                }
            }

            // Build the west edge
            if (j & STITCH_WEST)
            {
                int x = 0;
                for (int z = 0; z < patchSize_; z += skip * 2)
                {
                    if (z > 0 || (j & STITCH_SOUTH) == 0)
                    {
                        indices.Push((unsigned short)(z * row + x));
                        indices.Push((unsigned short)((z + skip) * row + x + skip));
                        indices.Push((unsigned short)(z * row + x + skip));
                    }
                    indices.Push((unsigned short)((z + 2 * skip) * row + x));
                    indices.Push((unsigned short)((z + skip) * row + x + skip));
                    indices.Push((unsigned short)(z * row + x));
                    if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
                    {
                        indices.Push((unsigned short)((z + 2 * skip) * row + x));
                        indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
                        indices.Push((unsigned short)((z + skip) * row + x + skip));
                    }
                }
            }

            // Build the east edge
            if (j & STITCH_EAST)
            {
                int x = patchSize_ - skip;
                for (int z = 0; z < patchSize_; z += skip * 2)
                {
                    if (z > 0 || (j & STITCH_SOUTH) == 0)
                    {
                        indices.Push((unsigned short)(z * row + x));
                        indices.Push((unsigned short)((z + skip) * row + x));
                        indices.Push((unsigned short)(z * row + x + skip));
                    }
                    indices.Push((unsigned short)((z + skip) * row + x));
                    indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
                    indices.Push((unsigned short)(z * row + x + skip));
                    if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0)
                    {
                        indices.Push((unsigned short)((z + skip) * row + x));
                        indices.Push((unsigned short)((z + 2 * skip) * row + x));
                        indices.Push((unsigned short)((z + 2 * skip) * row + x + skip));
                    }
                }
            }

            drawRanges_.Push(MakePair(indexStart, indices.Size() - indexStart));
        }
    }

    indexBuffer_->SetSize(indices.Size(), false);
    indexBuffer_->SetData(&indices[0]);
}
示例#10
0
void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
{
    // Disregard the click end if a drag is going on
    if (selectOnClickEnd_ && GetSubsystem<UI>()->IsDragging())
        return;

    int button = eventData[UIMouseClick::P_BUTTON].GetInt();
    int buttons = eventData[UIMouseClick::P_BUTTONS].GetInt();
    int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();

    UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());

    // Check if the clicked element belongs to the list
    unsigned i = FindItem(element);
    if (i >= GetNumItems())
        return;

    // If not editable, repeat the previous selection. This will send an event and allow eg. a dropdownlist to close
    if (!editable_)
    {
        SetSelections(selections_);
        return;
    }

    if (button == MOUSEB_LEFT)
    {
        // Single selection
        if (!multiselect_ || !qualifiers)
            SetSelection(i);

        // Check multiselect with shift & ctrl
        if (multiselect_)
        {
            if (qualifiers & QUAL_SHIFT)
            {
                if (selections_.Empty())
                    SetSelection(i);
                else
                {
                    unsigned first = selections_.Front();
                    unsigned last = selections_.Back();
                    PODVector<unsigned> newSelections = selections_;
                    if (i == first || i == last)
                    {
                        for (unsigned j = first; j <= last; ++j)
                            newSelections.Push(j);
                    }
                    else if (i < first)
                    {
                        for (unsigned j = i; j <= first; ++j)
                            newSelections.Push(j);
                    }
                    else if (i < last)
                    {
                        if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last)))
                        {
                            for (unsigned j = first; j <= i; ++j)
                                newSelections.Push(j);
                        }
                        else
                        {
                            for (unsigned j = i; j <= last; ++j)
                                newSelections.Push(j);
                        }
                    }
                    else if (i > last)
                    {
                        for (unsigned j = last; j <= i; ++j)
                            newSelections.Push(j);
                    }
                    SetSelections(newSelections);
                }
            }
            else if (qualifiers & QUAL_CTRL)
                ToggleSelection(i);
        }
    }

    // Propagate the click as an event. Also include right-clicks
    VariantMap& clickEventData = GetEventDataMap();
    clickEventData[ItemClicked::P_ELEMENT] = this;
    clickEventData[ItemClicked::P_ITEM] = element;
    clickEventData[ItemClicked::P_SELECTION] = i;
    clickEventData[ItemClicked::P_BUTTON] = button;
    clickEventData[ItemClicked::P_BUTTONS] = buttons;
    clickEventData[ItemClicked::P_QUALIFIERS] = qualifiers;
    SendEvent(E_ITEMCLICKED, clickEventData);
}
示例#11
0
void CompileVariation(CompiledVariation* variation)
{
    IncludeHandler includeHandler;
    PODVector<D3D_SHADER_MACRO> macros;
    
    // Insert variation-specific and global defines
    for (unsigned i = 0; i < variation->defines_.Size(); ++i)
    {
        D3D_SHADER_MACRO macro;
        macro.Name = variation->defines_[i].CString();
        macro.Definition = variation->defineValues_[i].CString();
        macros.Push(macro);
    }
    for (unsigned i = 0; i < defines_.Size(); ++i)
    {
        D3D_SHADER_MACRO macro;
        macro.Name = defines_[i].CString();
        macro.Definition = defineValues_[i].CString();
        macros.Push(macro);
    }
    
    D3D_SHADER_MACRO endMacro;
    endMacro.Name = 0;
    endMacro.Definition = 0;
    macros.Push(endMacro);
    
    LPD3DBLOB shaderCode = NULL;
    LPD3DBLOB errorMsgs = NULL;
    
    // Set the profile, entrypoint and flags according to the shader being compiled
    String profile;
    String entryPoint;
    unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3;
    
    if (variation->type_ == VS)
    {
        entryPoint = "VS";
        if (!useSM3_)
            profile = "vs_2_0";
        else
            profile = "vs_3_0";
    }
    else
    {
        entryPoint = "PS";
        if (!useSM3_)
            profile = "ps_2_0";
        else
        {
            profile = "ps_3_0";
            flags |= D3DCOMPILE_PREFER_FLOW_CONTROL;
        }
    }
    
    // Compile using D3DCompiler
    HRESULT hr = D3DCompile(hlslCode_.CString(), hlslCode_.Length(), NULL, &macros.Front(), &includeHandler,
        entryPoint.CString(), profile.CString(), flags, 0, &shaderCode, &errorMsgs);

    if (FAILED(hr))
    {
        variation->errorMsg_ = String((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
        compileFailed_ = true;
    }
    else
    {
        BYTE const *const bufData = static_cast<BYTE *>(shaderCode->GetBufferPointer());
        SIZE_T const bufSize = shaderCode->GetBufferSize();
        MOJOSHADER_parseData const *parseData = MOJOSHADER_parse("bytecode", bufData, bufSize, NULL, 0, NULL, 0, NULL, NULL, NULL);

        CopyStrippedCode(variation->byteCode_, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize());
        
        if (!variation->name_.Empty())
            PrintLine("Compiled shader variation " + variation->name_ + ", code size " + String(variation->byteCode_.Size()));
        else
            PrintLine("Compiled base shader variation, code size " + String(variation->byteCode_.Size()));
            
        // Print warnings if any
        if (errorMsgs && errorMsgs->GetBufferSize())
        {
            String warning((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize());
            PrintLine("WARNING: " + warning);
        }

        for(int i = 0; i < parseData->symbol_count; i++)
        {
            MOJOSHADER_symbol const& symbol = parseData->symbols[i];

            String name(symbol.name);
            unsigned const reg = symbol.register_index;
            unsigned const regCount = symbol.register_count;

            // Check if the parameter is a constant or a texture sampler
            bool const isSampler = (name[0] == 's');
            name = name.Substring(1);
            
            if (isSampler)
            {
                // Skip if it's a G-buffer sampler, which are aliases for the standard texture units
                if (name != "AlbedoBuffer" && name != "NormalBuffer" && name != "DepthBuffer" && name != "LightBuffer")
                {
                    Parameter newTextureUnit(name, reg, 1);
                    variation->textureUnits_.Push(newTextureUnit);
                }
            }
            else
            {
                Parameter newParam(name, reg, regCount);
                variation->constants_.Push(newParam);
            }
        }

        MOJOSHADER_freeParseData(parseData);

        // Create the last part of the output path (SM2/SM3) if it does not exist
        String outPath = GetPath(variation->outFileName_);
        if (!fileSystem_->DirExists(outPath))
            fileSystem_->CreateDir(outPath);

        File outFile(context_);
        if (!outFile.Open(variation->outFileName_, FILE_WRITE))
        {
            variation->errorMsg_ = "Could not open output file " + variation->outFileName_;
            compileFailed_ = true;
        }
        else
        {
            outFile.WriteFileID("USHD");
            outFile.WriteShort((unsigned short)variation->type_);
            outFile.WriteShort(useSM3_ ? 3 : 2);
            
            outFile.WriteUInt(variation->constants_.Size());
            for (unsigned i = 0; i < variation->constants_.Size(); ++i)
            {
                outFile.WriteString(variation->constants_[i].name_);
                outFile.WriteUByte(variation->constants_[i].register_);
                outFile.WriteUByte(variation->constants_[i].regCount_);
            }
            
            outFile.WriteUInt(variation->textureUnits_.Size());
            for (unsigned i = 0; i < variation->textureUnits_.Size(); ++i)
            {
                outFile.WriteString(variation->textureUnits_[i].name_);
                outFile.WriteUByte(variation->textureUnits_[i].register_);
            }
            
            unsigned dataSize = variation->byteCode_.Size();
            outFile.WriteUInt(dataSize);
            if (dataSize)
                outFile.Write(&variation->byteCode_[0], dataSize);
        }
    }
    
    if (shaderCode)
        shaderCode->Release();
    if (errorMsgs)
        errorMsgs->Release();
}
示例#12
0
文件: Text.cpp 项目: jjiezheng/urho3d
void Text::UpdateText()
{
    int width = 0;
    int height = 0;

    rowWidths_.Clear();
    printText_.Clear();

    PODVector<unsigned> printToText;

    if (font_)
    {
        const FontFace* face = font_->GetFace(fontSize_);
        if (!face)
            return;

        rowHeight_ = face->rowHeight_;
        int rowWidth = 0;
        int rowHeight = (int)(rowSpacing_ * rowHeight_);

        // First see if the text must be split up
        if (!wordWrap_)
        {
            printText_ = unicodeText_;
            printToText.Resize(printText_.Size());
            for (unsigned i = 0; i < printText_.Size(); ++i)
                printToText[i] = i;
        }
        else
        {
            int maxWidth = GetWidth();
            unsigned nextBreak = 0;
            unsigned lineStart = 0;
            for (unsigned i = 0; i < unicodeText_.Size(); ++i)
            {
                unsigned j;
                unsigned c = unicodeText_[i];

                if (c != '\n')
                {
                    bool ok = true;

                    if (nextBreak <= i)
                    {
                        int futureRowWidth = rowWidth;
                        for (j = i; j < unicodeText_.Size(); ++j)
                        {
                            unsigned d = unicodeText_[j];
                            if (d == ' ' || d == '\n')
                            {
                                nextBreak = j;
                                break;
                            }
                            const FontGlyph* glyph = face->GetGlyph(d);
                            if (glyph)
                            {
                                futureRowWidth += glyph->advanceX_;
                                if (j < unicodeText_.Size() - 1)
                                    futureRowWidth += face->GetKerning(d, unicodeText_[j + 1]);
                            }
                            if (d == '-' && futureRowWidth <= maxWidth)
                            {
                                nextBreak = j + 1;
                                break;
                            }
                            if (futureRowWidth > maxWidth)
                            {
                                ok = false;
                                break;
                            }
                        }
                    }

                    if (!ok)
                    {
                        // If did not find any breaks on the line, copy until j, or at least 1 char, to prevent infinite loop
                        if (nextBreak == lineStart)
                        {
                            while (i < j)
                            {
                                printText_.Push(unicodeText_[i]);
                                printToText.Push(i);
                                ++i;
                            }
                        }
                        printText_.Push('\n');
                        printToText.Push(Min((int)i, (int)unicodeText_.Size() - 1));
                        rowWidth = 0;
                        nextBreak = lineStart = i;
                    }

                    if (i < unicodeText_.Size())
                    {
                        // When copying a space, position is allowed to be over row width
                        c = unicodeText_[i];
                        const FontGlyph* glyph = face->GetGlyph(c);
                        if (glyph)
                        {
                            rowWidth += glyph->advanceX_;
                            if (i < text_.Length() - 1)
                                rowWidth += face->GetKerning(c, unicodeText_[i + 1]);
                        }
                        if (rowWidth <= maxWidth)
                        {
                            printText_.Push(c);
                            printToText.Push(i);
                        }
                    }
                }
                else
                {
                    printText_.Push('\n');
                    printToText.Push(Min((int)i, (int)unicodeText_.Size() - 1));
                    rowWidth = 0;
                    nextBreak = lineStart = i;
                }
            }
        }

        rowWidth = 0;

        for (unsigned i = 0; i < printText_.Size(); ++i)
        {
            unsigned c = printText_[i];

            if (c != '\n')
            {
                const FontGlyph* glyph = face->GetGlyph(c);
                if (glyph)
                {
                    rowWidth += glyph->advanceX_;
                    if (i < printText_.Size() - 1)
                        rowWidth += face->GetKerning(c, printText_[i + 1]);
                }
            }
            else
            {
                width = Max(width, rowWidth);
                height += rowHeight;
                rowWidths_.Push(rowWidth);
                rowWidth = 0;
            }
        }

        if (rowWidth)
        {
            width = Max(width, rowWidth);
            height += rowHeight;
            rowWidths_.Push(rowWidth);
        }

        // Set row height even if text is empty
        if (!height)
            height = rowHeight;

        // Store position & size of each character
        charPositions_.Resize(unicodeText_.Size() + 1);
        charSizes_.Resize(unicodeText_.Size());

        unsigned rowIndex = 0;
        int x = GetRowStartPosition(rowIndex);
        int y = 0;
        for (unsigned i = 0; i < printText_.Size(); ++i)
        {
            charPositions_[printToText[i]] = IntVector2(x, y);
            unsigned c = printText_[i];
            if (c != '\n')
            {
                const FontGlyph* glyph = face->GetGlyph(c);
                charSizes_[printToText[i]] = IntVector2(glyph ? glyph->advanceX_ : 0, rowHeight_);
                if (glyph)
                {
                    x += glyph->advanceX_;
                    if (i < printText_.Size() - 1)
                        x += face->GetKerning(c, printText_[i + 1]);
                }
            }
            else
            {
                charSizes_[printToText[i]] = IntVector2::ZERO;
                x = GetRowStartPosition(++rowIndex);
                y += rowHeight;
            }
        }
        // Store the ending position
        charPositions_[unicodeText_.Size()] = IntVector2(x, y);
    }

    // Set minimum and current size according to the text size, but respect fixed width if set
    if (!IsFixedWidth())
    {
        SetMinWidth(wordWrap_ ? 0 : width);
        SetWidth(width);
    }
    SetFixedHeight(height);
}
示例#13
0
文件: Text.cpp 项目: jjiezheng/urho3d
void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor)
{
    // Hovering and/or whole selection batch
    if ((hovering_ && hoverColor_.a_ > 0.0) || (selected_ && selectionColor_.a_ > 0.0f))
    {
        bool both = hovering_ && selected_ && hoverColor_.a_ > 0.0 && selectionColor_.a_ > 0.0f;
        UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData);
        batch.AddQuad(0, 0, GetWidth(), GetHeight(), 0, 0, 0, 0, both ? selectionColor_.Lerp(hoverColor_, 0.5f) :
                      (selected_ && selectionColor_.a_ > 0.0f ? selectionColor_ : hoverColor_));
        UIBatch::AddOrMerge(batch, batches);
    }

    // Partial selection batch
    if (!selected_ && selectionLength_ && charSizes_.Size() >= selectionStart_ + selectionLength_ && selectionColor_.a_ > 0.0f)
    {
        UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData);

        IntVector2 currentStart = charPositions_[selectionStart_];
        IntVector2 currentEnd = currentStart;
        for (unsigned i = selectionStart_; i < selectionStart_ + selectionLength_; ++i)
        {
            // Check if row changes, and start a new quad in that case
            if (charSizes_[i].x_ && charSizes_[i].y_)
            {
                if (charPositions_[i].y_ != currentStart.y_)
                {
                    batch.AddQuad(currentStart.x_, currentStart.y_, currentEnd.x_ - currentStart.x_, currentEnd.y_ - currentStart.y_,
                                  0, 0, 0, 0, selectionColor_);
                    currentStart = charPositions_[i];
                    currentEnd = currentStart + charSizes_[i];
                }
                else
                {
                    currentEnd.x_ += charSizes_[i].x_;
                    currentEnd.y_ = Max(currentStart.y_ + charSizes_[i].y_, currentEnd.y_);
                }
            }
        }
        if (currentEnd != currentStart)
        {
            batch.AddQuad(currentStart.x_, currentStart.y_, currentEnd.x_ - currentStart.x_, currentEnd.y_ - currentStart.y_,
                          0, 0, 0, 0, selectionColor_);
        }

        UIBatch::AddOrMerge(batch, batches);
    }

    // Text batch
    if (font_)
    {
        const FontFace* face = font_->GetFace(fontSize_);
        if (!face)
            return;

        if (face->textures_.Size() > 1)
        {
            // Only traversing thru the printText once regardless of number of textures/pages in the font
            Vector<PODVector<GlyphLocation> > pageGlyphLocations(face->textures_.Size());

            unsigned rowIndex = 0;
            int x = GetRowStartPosition(rowIndex);
            int y = 0;

            for (unsigned i = 0; i < printText_.Size(); ++i)
            {
                unsigned c = printText_[i];

                if (c != '\n')
                {
                    const FontGlyph* p = face->GetGlyph(c);
                    if (!p)
                        continue;

                    pageGlyphLocations[p->page_].Push(GlyphLocation(x, y, p));

                    x += p->advanceX_;
                    if (i < printText_.Size() - 1)
                        x += face->GetKerning(c, printText_[i + 1]);
                }
                else
                {
                    x = GetRowStartPosition(++rowIndex);
                    y += rowHeight_;
                }
            }

            for (unsigned n = 0; n < face->textures_.Size(); ++n)
            {
                // One batch per texture/page
                UIBatch pageBatch(this, BLEND_ALPHA, currentScissor, face->textures_[n], &vertexData);

                const PODVector<GlyphLocation>& pageGlyphLocation = pageGlyphLocations[n];
                for (unsigned i = 0; i < pageGlyphLocation.Size(); ++i)
                {
                    const GlyphLocation& glyphLocation = pageGlyphLocation[i];
                    const FontGlyph& glyph = *glyphLocation.glyph_;
                    pageBatch.AddQuad(glyphLocation.x_ + glyph.offsetX_, glyphLocation.y_ + glyph.offsetY_, glyph.width_, glyph.height_, glyph.x_, glyph.y_);
                }

                batches.Push(pageBatch);
            }
        }
        else
        {
            // If only one texture page, construct the UI batch directly
            unsigned rowIndex = 0;
            int x = GetRowStartPosition(rowIndex);
            int y = 0;

            UIBatch batch(this, BLEND_ALPHA, currentScissor, face->textures_[0], &vertexData);

            for (unsigned i = 0; i < printText_.Size(); ++i)
            {
                unsigned c = printText_[i];

                if (c != '\n')
                {
                    const FontGlyph* p = face->GetGlyph(c);
                    if (!p)
                        continue;

                    batch.AddQuad(x + p->offsetX_, y + p->offsetY_, p->width_, p->height_, p->x_, p->y_);

                    x += p->advanceX_;
                    if (i < printText_.Size() - 1)
                        x += face->GetKerning(c, printText_[i + 1]);
                }
                else
                {
                    x = GetRowStartPosition(++rowIndex);
                    y += rowHeight_;
                }
            }

            UIBatch::AddOrMerge(batch, batches);
        }
    }

    // Reset hovering for next frame
    hovering_ = false;
}
示例#14
0
void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData)
{
    if (eventData[UIMouseClick::P_BUTTON].GetInt() != MOUSEB_LEFT)
        return;
    int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt();

    UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr());

    // If not editable, repeat the previous selection. This will send an event and allow eg. a dropdownlist to close
    if (!editable_)
    {
        SetSelections(selections_);
        return;
    }

    unsigned numItems = GetNumItems();
    for (unsigned i = 0; i < numItems; ++i)
    {
        if (element == GetItem(i))
        {
            // Single selection
            if (!multiselect_ || !qualifiers)
                SetSelection(i);

            // Check multiselect with shift & ctrl
            if (multiselect_)
            {
                if (qualifiers & QUAL_SHIFT)
                {
                    if (selections_.Empty())
                        SetSelection(i);
                    else
                    {
                        unsigned first = selections_.Front();
                        unsigned last = selections_.Back();
                        PODVector<unsigned> newSelections = selections_;
                        if (i == first || i == last)
                        {
                            for (unsigned j = first; j <= last; ++j)
                                newSelections.Push(j);
                        }
                        else if (i < first)
                        {
                            for (unsigned j = i; j <= first; ++j)
                                newSelections.Push(j);
                        }
                        else if (i < last)
                        {
                            if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last)))
                            {
                                for (unsigned j = first; j <= i; ++j)
                                    newSelections.Push(j);
                            }
                            else
                            {
                                for (unsigned j = i; j <= last; ++j)
                                    newSelections.Push(j);
                            }
                        }
                        else if (i > last)
                        {
                            for (unsigned j = last; j <= i; ++j)
                                newSelections.Push(j);
                        }
                        SetSelections(newSelections);
                    }
                }
                else if (qualifiers & QUAL_CTRL)
                    ToggleSelection(i);
            }

            return;
        }
    }
}
void Constraint::GetDependencyNodes(PODVector<Node*>& dest)
{
    if (otherBody_ && otherBody_->GetNode())
        dest.Push(otherBody_->GetNode());
}
    bool CSComponentAssembly::ParseComponentClassJSON(const JSONValue& json)
    {
        if (!typeMap_.Size())
            InitTypeMap();

        String className = json.Get("name").GetString();

        classNames_.Push(className);

        const JSONValue& jfields = json.Get("fields");

        PODVector<StringHash> enumsAdded;

        if (jfields.IsArray())
        {
            for (unsigned i = 0; i < jfields.GetArray().Size(); i++)
            {
                const JSONValue& jfield = jfields.GetArray().At(i);

                VariantType varType = VAR_NONE;

                bool isEnum = jfield.Get("isEnum").GetBool();
                String typeName = jfield.Get("typeName").GetString();
                String fieldName = jfield.Get("name").GetString();
                String defaultValue = jfield.Get("defaultValue").GetString();

                if (!defaultValue.Length())
                {
                    JSONArray caPos = jfield.Get("caPos").GetArray();
                    if (caPos.Size())
                        defaultValue = caPos[0].GetString();
                }

                if (!defaultValue.Length())
                {
                    JSONObject caNamed = jfield.Get("caNamed").GetObject();
                    if (caNamed.Contains("DefaultValue"))
                        defaultValue = caNamed["DefaultValue"].GetString();
                }

                if (isEnum && assemblyEnums_.Contains(typeName) && !enumsAdded.Contains(fieldName))
                {
                    varType = VAR_INT;
                    enumsAdded.Push(fieldName);
                    const Vector<EnumInfo>& einfos = assemblyEnums_[typeName];
                    for (unsigned i = 0; i < einfos.Size(); i++)
                        AddEnum(/*typeName*/fieldName, einfos[i], className);
                }

                if (varType == VAR_NONE && typeMap_.Contains(typeName))
                    varType = typeMap_[typeName];

                if (varType == VAR_NONE)
                {
                    // FIXME: We need to be able to test if a type is a ResourceRef, this isn't really the way to achieve that
                    const HashMap<StringHash, SharedPtr<ObjectFactory>>& factories = context_->GetObjectFactories();
                    HashMap<StringHash, SharedPtr<ObjectFactory>>::ConstIterator itr = factories.Begin();

                    while (itr != factories.End())
                    {
                        if (itr->second_->GetTypeName() == typeName)
                        {
                            varType = VAR_RESOURCEREF;
                            break;
                        }

                        itr++;
                    }

                    if (varType == VAR_NONE)
                    {
                        ATOMIC_LOGERRORF("Component Class %s contains unmappable type %s in field %s",
                            className.CString(), typeName.CString(), fieldName.CString());

                        continue;
                    }

                }

                if (!defaultValue.Length() && varType == VAR_RESOURCEREF)
                {
                    // We still need a default value for ResourceRef's so we know the classtype
                    AddDefaultValue(fieldName, ResourceRef(typeName), className);
                }
                else
                {
                    Variant value;

                    if (varType == VAR_RESOURCEREF)
                    {
                        ResourceRef rref(typeName);
                        rref.name_ = defaultValue;
                        value = rref;
                    }
                    else
                    {
                        value.FromString(varType, defaultValue);
                    }

                    AddDefaultValue(fieldName, value, className);
                }

                AddField(fieldName, varType, className);

            }

        }

        return true;
    }
示例#17
0
void StaticModelGroup::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
{
    // If no bones or no bone-level testing, use the Drawable test
    RayQueryLevel level = query.level_;
    if (level < RAY_AABB)
    {
        Drawable::ProcessRayQuery(query, results);
        return;
    }

    // Check ray hit distance to AABB before proceeding with more accurate tests
    // GetWorldBoundingBox() updates the world transforms
    if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_)
        return;

    for (unsigned i = 0; i < numWorldTransforms_; ++i)
    {
        // Initial test using AABB
        float distance = query.ray_.HitDistance(boundingBox_.Transformed(worldTransforms_[i]));
        Vector3 normal = -query.ray_.direction_;

        // Then proceed to OBB and triangle-level tests if necessary
        if (level >= RAY_OBB && distance < query.maxDistance_)
        {
            Matrix3x4 inverse = worldTransforms_[i].Inverse();
            Ray localRay = query.ray_.Transformed(inverse);
            distance = localRay.HitDistance(boundingBox_);

            if (level == RAY_TRIANGLE && distance < query.maxDistance_)
            {
                distance = M_INFINITY;

                for (unsigned j = 0; j < batches_.Size(); ++j)
                {
                    Geometry* geometry = batches_[j].geometry_;
                    if (geometry)
                    {
                        Vector3 geometryNormal;
                        float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal);
                        if (geometryDistance < query.maxDistance_ && geometryDistance < distance)
                        {
                            distance = geometryDistance;
                            normal = (worldTransforms_[i] * Vector4(geometryNormal, 0.0f)).Normalized();
                        }
                    }
                }
            }
        }

        if (distance < query.maxDistance_)
        {
            RayQueryResult result;
            result.position_ = query.ray_.origin_ + distance * query.ray_.direction_;
            result.normal_ = normal;
            result.distance_ = distance;
            result.drawable_ = this;
            result.node_ = node_;
            result.subObject_ = i;
            results.Push(result);
        }
    }
}
void NavigationMesh::FindPath(PODVector<NavigationPathPoint>& dest, const Vector3& start, const Vector3& end,
    const Vector3& extents, const dtQueryFilter* filter)
{
    ATOMIC_PROFILE(FindPath);
    dest.Clear();

    if (!InitializeQuery())
        return;

    // Navigation data is in local space. Transform path points from world to local
    const Matrix3x4& transform = node_->GetWorldTransform();
    Matrix3x4 inverse = transform.Inverse();

    Vector3 localStart = inverse * start;
    Vector3 localEnd = inverse * end;

    const dtQueryFilter* queryFilter = filter ? filter : queryFilter_.Get();
    dtPolyRef startRef;
    dtPolyRef endRef;
    navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter, &startRef, 0);
    navMeshQuery_->findNearestPoly(&localEnd.x_, &extents.x_, queryFilter, &endRef, 0);

    if (!startRef || !endRef)
        return;

    int numPolys = 0;
    int numPathPoints = 0;

    navMeshQuery_->findPath(startRef, endRef, &localStart.x_, &localEnd.x_, queryFilter, pathData_->polys_, &numPolys,
        MAX_POLYS);
    if (!numPolys)
        return;

    Vector3 actualLocalEnd = localEnd;

    // If full path was not found, clamp end point to the end polygon
    if (pathData_->polys_[numPolys - 1] != endRef)
        navMeshQuery_->closestPointOnPoly(pathData_->polys_[numPolys - 1], &localEnd.x_, &actualLocalEnd.x_, 0);

    navMeshQuery_->findStraightPath(&localStart.x_, &actualLocalEnd.x_, pathData_->polys_, numPolys,
        &pathData_->pathPoints_[0].x_, pathData_->pathFlags_, pathData_->pathPolys_, &numPathPoints, MAX_POLYS);

    // Transform path result back to world space
    for (int i = 0; i < numPathPoints; ++i)
    {
        NavigationPathPoint pt;
        pt.position_ = transform * pathData_->pathPoints_[i];
        pt.flag_ = (NavigationPathPointFlag)pathData_->pathFlags_[i];

        // Walk through all NavAreas and find nearest
        unsigned nearestNavAreaID = 0;       // 0 is the default nav area ID
        float nearestDistance = M_LARGE_VALUE;
        for (unsigned j = 0; j < areas_.Size(); j++)
        {
            NavArea* area = areas_[j].Get();
            if (area && area->IsEnabledEffective())
            {
                BoundingBox bb = area->GetWorldBoundingBox();
                if (bb.IsInside(pt.position_) == INSIDE)
                {
                    Vector3 areaWorldCenter = area->GetNode()->GetWorldPosition();
                    float distance = (areaWorldCenter - pt.position_).LengthSquared();
                    if (distance < nearestDistance)
                    {
                        nearestDistance = distance;
                        nearestNavAreaID = area->GetAreaID();
                    }
                }
            }
        }
        pt.areaID_ = (unsigned char)nearestNavAreaID;

        dest.Push(pt);
    }
}
示例#19
0
void PrefabImporter::HandlePrefabSave(StringHash eventType, VariantMap& eventData)
{
    using namespace PrefabSave;

    PrefabComponent* component = static_cast<PrefabComponent*>(eventData[P_PREFABCOMPONENT].GetPtr());

    if (component->GetPrefabGUID() != asset_->GetGUID())
        return;

    Node* node = component->GetNode();

    if (!node)
        return;

    // flip temporary root children and components to not be temporary for save
    const Vector<SharedPtr<Component>>& rootComponents = node->GetComponents();
    const Vector<SharedPtr<Node> >& children = node->GetChildren();

    PODVector<Component*> tempComponents;
    PODVector<Node*> tempChildren;
    PODVector<Node*> filterNodes;

    for (unsigned i = 0; i < rootComponents.Size(); i++)
    {
        if (rootComponents[i]->IsTemporary())
        {
            rootComponents[i]->SetTemporary(false);
            tempComponents.Push(rootComponents[i]);

            // Animated sprites contain a temporary node we don't want to save in the prefab
            // it would be nice if this was general purpose because have to test this when
            // breaking node as well
            if (rootComponents[i]->GetType() == AnimatedSprite2D::GetTypeStatic())
            {
                AnimatedSprite2D* asprite = (AnimatedSprite2D*) rootComponents[i].Get();
                if (asprite->GetRootNode())
                    filterNodes.Push(asprite->GetRootNode());
            }

        }
    }

    for (unsigned i = 0; i < children.Size(); i++)
    {
        if (filterNodes.Contains(children[i].Get()))
            continue;

        if (children[i]->IsTemporary())
        {
            children[i]->SetTemporary(false);
            tempChildren.Push(children[i]);
        }
    }

    // store original transform
    Vector3 pos = node->GetPosition();
    Quaternion rot = node->GetRotation();
    Vector3 scale = node->GetScale();

    node->SetPosition(Vector3::ZERO);
    node->SetRotation(Quaternion::IDENTITY);
    node->SetScale(Vector3::ONE);

    component->SetTemporary(true);

    SharedPtr<File> file(new File(context_, asset_->GetPath(), FILE_WRITE));
    node->SaveXML(*file);
    file->Close();

    component->SetTemporary(false);

    // restore
    node->SetPosition(pos);
    node->SetRotation(rot);
    node->SetScale(scale);

    for (unsigned i = 0; i < tempComponents.Size(); i++)
    {
        tempComponents[i]->SetTemporary(true);
    }

    for (unsigned i = 0; i < tempChildren.Size(); i++)
    {
        tempChildren[i]->SetTemporary(true);
    }


    FileSystem* fs = GetSubsystem<FileSystem>();
    fs->Copy(asset_->GetPath(), asset_->GetCachePath());

    // reload it immediately so it is ready for use
    // TODO: The resource cache is reloading after this reload due to catching the file cache
    ResourceCache* cache = GetSubsystem<ResourceCache>();
    XMLFile* xmlfile = cache->GetResource<XMLFile>(asset_->GetGUID());
    cache->ReloadResource(xmlfile);

    VariantMap changedData;
    changedData[PrefabChanged::P_GUID] = asset_->GetGUID();
    SendEvent(E_PREFABCHANGED, changedData);

}