예제 #1
0
void ScriptInstance::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
{
    if (!active_ || !scriptObject_)
        return;
    
    using namespace PhysicsPostStep;
    
    if (!fixedUpdateFps_)
    {
        VariantVector parameters;
        parameters.Push(eventData[P_TIMESTEP]);
        scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
    }
    else
    {
        float timeStep = eventData[P_TIMESTEP].GetFloat();
        fixedPostUpdateAcc_ += timeStep;
        if (fixedPostUpdateAcc_ >= fixedUpdateInterval_)
        {
            fixedPostUpdateAcc_ = fmodf(fixedPostUpdateAcc_, fixedUpdateInterval_);
            VariantVector parameters;
            parameters.Push(fixedUpdateInterval_);
            scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
        }
    }
}
예제 #2
0
VariantVector CrowdManager::GetQueryFilterTypesAttr() const
{
    VariantVector ret;
    if (crowd_)
    {
        unsigned totalNumAreas = 0;
        for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
            totalNumAreas += numAreas_[i];

        ret.Reserve(numQueryFilterTypes_ * 3 + totalNumAreas + 1);
        ret.Push(numQueryFilterTypes_);

        for (unsigned i = 0; i < numQueryFilterTypes_; ++i)
        {
            const dtQueryFilter* filter = crowd_->getFilter(i);
            assert(filter);
            ret.Push(filter->getIncludeFlags());
            ret.Push(filter->getExcludeFlags());
            ret.Push(numAreas_[i]);

            for (unsigned j = 0; j < numAreas_[i]; ++j)
                ret.Push(filter->getAreaCost(j));
        }
    }
    else
        ret.Push(0);

    return ret;
}
예제 #3
0
VariantVector Cursor::GetShapesAttr() const
{
    VariantVector ret;

    unsigned numShapes = 0;
    for (unsigned i = 0; i < CS_MAX_SHAPES; ++i)
    {
        if (shapeInfos_[i].imageRect_ != IntRect::ZERO)
            ++numShapes;
    }

    ret.Push(numShapes);
    for (unsigned i = 0; i < CS_MAX_SHAPES; ++i)
    {
        if (shapeInfos_[i].imageRect_ != IntRect::ZERO)
        {
            ret.Push(String(shapeNames[i]));
            ret.Push(GetResourceRef(shapeInfos_[i].texture_, Texture2D::GetTypeStatic()));
            ret.Push(shapeInfos_[i].imageRect_);
            ret.Push(shapeInfos_[i].hotSpot_);
        }
    }

    return ret;
}
예제 #4
0
VariantVector DecalSet::GetDecalsAttr() const
{
    VariantVector ret;
    ret.Push(skinned_);
    ret.Push(decals_.Size());
    
    for (List<Decal>::ConstIterator i = decals_.Begin(); i != decals_.End(); ++i)
    {
        ret.Push(i->timer_);
        ret.Push(i->timeToLive_);
        ret.Push(i->vertices_.Size());
        ret.Push(i->indices_.Size());
        
        VectorBuffer geometry;
        
        for (PODVector<DecalVertex>::ConstIterator j = i->vertices_.Begin(); j != i->vertices_.End(); ++j)
        {
            geometry.WriteVector3(j->position_);
            geometry.WriteVector3(j->normal_);
            geometry.WriteVector2(j->texCoord_);
            geometry.WriteVector4(j->tangent_);
            if (skinned_)
            {
                for (unsigned k = 0; k < 4; ++k)
                    geometry.WriteFloat(j->blendWeights_[k]);
                for (unsigned k = 0; k < 4; ++k)
                    geometry.WriteUByte(j->blendIndices_[k]);
            }
        }
        
        for (PODVector<unsigned short>::ConstIterator j = i->indices_.Begin(); j != i->indices_.End(); ++j)
            geometry.WriteUShort(*j);
        
        ret.Push(geometry.GetBuffer());
    }
    
    if (skinned_)
    {
        ret.Push(bones_.Size());
        
        for (Vector<Bone>::ConstIterator i = bones_.Begin(); i != bones_.End(); ++i)
        {
            ret.Push(i->name_);
            VectorBuffer boneData;
            
            boneData.WriteUByte(i->collisionMask_);
            if (i->collisionMask_ & BONECOLLISION_SPHERE)
                boneData.WriteFloat(i->radius_);
            if (i->collisionMask_ & BONECOLLISION_BOX)
                boneData.WriteBoundingBox(i->boundingBox_);
            boneData.Write(i->offsetMatrix_.Data(), sizeof(Matrix3x4));
            
            ret.Push(boneData.GetBuffer());
        }
    }
    
    return ret;
}
예제 #5
0
VariantVector CrowdManager::GetObstacleAvoidanceTypesAttr() const
{
    VariantVector ret;
    if (crowd_)
    {
        ret.Reserve(numObstacleAvoidanceTypes_ * 10 + 1);
        ret.Push(numObstacleAvoidanceTypes_);

        for (unsigned i = 0; i < numObstacleAvoidanceTypes_; ++i)
        {
            const dtObstacleAvoidanceParams* params = crowd_->getObstacleAvoidanceParams(i);
            assert(params);
            ret.Push(params->velBias);
            ret.Push(params->weightDesVel);
            ret.Push(params->weightCurVel);
            ret.Push(params->weightSide);
            ret.Push(params->weightToi);
            ret.Push(params->horizTime);
            ret.Push(params->gridSize);
            ret.Push(params->adaptiveDivs);
            ret.Push(params->adaptiveRings);
            ret.Push(params->adaptiveDepth);
        }
    }
    else
        ret.Push(0);

    return ret;
}
예제 #6
0
VariantVector ParticleEmitter::GetParticleBillboardsAttr() const
{
    VariantVector ret;
    if (!serializeParticles_)
    {
        ret.Push(billboards_.Size());
        return ret;
    }

    ret.Reserve(billboards_.Size() * 7 + 1);
    ret.Push(billboards_.Size());

    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
    {
        ret.Push(i->position_);
        ret.Push(i->size_);
        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
        ret.Push(i->color_);
        ret.Push(i->rotation_);
        ret.Push(i->direction_);
        ret.Push(i->enabled_);
    }

    return ret;
}
예제 #7
0
VariantVector ParticleEmitter::GetColorsAttr() const
{
    VariantVector ret;
    ret.Reserve(colorFrames_.Size() * 2 + 1);
    ret.Push(colorFrames_.Size());
    for (Vector<ColorFrame>::ConstIterator i = colorFrames_.Begin(); i < colorFrames_.End(); ++i)
    {
        ret.Push(i->color_);
        ret.Push(i->time_);
    }
    return ret;
}
예제 #8
0
VariantVector ParticleEmitter::GetTextureFramesAttr() const
{
    VariantVector ret;
    ret.Reserve(textureFrames_.Size() * 2 + 1);
    ret.Push(textureFrames_.Size());
    for (Vector<TextureFrame>::ConstIterator i = textureFrames_.Begin(); i < textureFrames_.End(); ++i)
    {
        ret.Push(i->uv_.ToVector4());
        ret.Push(i->time_);
    }
    return ret;
}
예제 #9
0
VariantVector AnimationController::GetAnimationsAttr() const
{
    VariantVector ret;
    ret.Reserve(animations_.Size() * 5);
    for (Vector<AnimationControl>::ConstIterator i = animations_.Begin(); i != animations_.End(); ++i)
    {
        ret.Push(i->name_);
        ret.Push(i->speed_);
        ret.Push(i->targetWeight_);
        ret.Push(i->fadeTime_);
        ret.Push(i->autoFadeTime_);
    }
    return ret;
}
예제 #10
0
VariantVector AnimationController::GetNodeAnimationStatesAttr() const
{
    VariantVector ret;
    ret.Reserve(nodeAnimationStates_.Size() * 3 + 1);
    ret.Push(nodeAnimationStates_.Size());
    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = nodeAnimationStates_.Begin(); i != nodeAnimationStates_.End(); ++i)
    {
        AnimationState* state = *i;
        Animation* animation = state->GetAnimation();
        ret.Push(GetResourceRef(animation, Animation::GetTypeStatic()));
        ret.Push(state->IsLooped());
        ret.Push(state->GetTime());
    }
    return ret;
}
예제 #11
0
void ScriptEventInvoker::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
{
    if (!file_->IsCompiled())
        return;

    asIScriptFunction* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());

    if (object_ && !IsObjectAlive())
    {
        file_->CleanupEventInvoker(object_);
        return;
    }

    VariantVector parameters;
    if (method->GetParamCount() > 0)
    {
        parameters.Push(Variant((void*)&eventType));
        parameters.Push(Variant((void*)&eventData));
    }

    if (object_)
        file_->Execute(object_, method, parameters);
    else
        file_->Execute(method, parameters);
}
예제 #12
0
VariantVector AnimatedModel::GetBonesEnabledAttr() const
{
    VariantVector ret;
    const Vector<Bone>& bones = skeleton_.GetBones();
    ret.Reserve(bones.Size());
    for (Vector<Bone>::ConstIterator i = bones.Begin(); i != bones.End(); ++i)
        ret.Push(i->animated_);
    return ret;
}
예제 #13
0
void ScriptInstance::SetScriptDataAttr(PODVector<unsigned char> data)
{
    if (scriptObject_ && methods_[METHOD_LOAD])
    {
        MemoryBuffer buf(data);
        VariantVector parameters;
        parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
        scriptFile_->Execute(scriptObject_, methods_[METHOD_LOAD], parameters);
    }
}
예제 #14
0
void ScriptInstance::SetScriptNetworkDataAttr(const PODVector<unsigned char>& data)
{
    if (scriptObject_ && methods_[METHOD_READNETWORKUPDATE])
    {
        MemoryBuffer buf(data);
        VariantVector parameters;
        parameters.Push(Variant((void*)static_cast<Deserializer*>(&buf)));
        scriptFile_->Execute(scriptObject_, methods_[METHOD_READNETWORKUPDATE], parameters);
    }
}
예제 #15
0
void AngelScriptIntegration::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 component into a child scene node. The Zone controls ambient lighting and fog settings. Like the Octree,
    // it also defines its volume with a bounding box, but can be rotated (so it does not need to be aligned to the world X, Y
    // and Z axes.) Drawable objects "pick up" the zone they belong to and use it when rendering; several zones can exist
    Node* zoneNode = scene_->CreateChild("Zone");
    Zone* zone = zoneNode->CreateComponent<Zone>();
    // Set same volume as the Octree, set a close bluish fog and some ambient light
    zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
    zone->SetAmbientColor(Color(0.05f, 0.1f, 0.15f));
    zone->SetFogColor(Color(0.1f, 0.2f, 0.3f));
    zone->SetFogStart(10.0f);
    zone->SetFogEnd(100.0f);
    
    // Create randomly positioned and oriented box StaticModels in the scene
    const unsigned NUM_OBJECTS = 2000;
    for (unsigned i = 0; i < NUM_OBJECTS; ++i)
    {
        Node* boxNode = scene_->CreateChild("Box");
        boxNode->SetPosition(Vector3(Random(200.0f) - 100.0f, Random(200.0f) - 100.0f, Random(200.0f) - 100.0f));
        // Orient using random pitch, yaw and roll Euler angles
        boxNode->SetRotation(Quaternion(Random(360.0f), Random(360.0f), Random(360.0f)));
        StaticModel* boxObject = boxNode->CreateComponent<StaticModel>();
        boxObject->SetModel(cache->GetResource<Model>("Models/Box.mdl"));
        boxObject->SetMaterial(cache->GetResource<Material>("Materials/Stone.xml"));
        
        // Add our custom Rotator script object (using the ScriptInstance C++ component to instantiate / store it) which will
        // rotate the scene node each frame, when the scene sends its update event
        ScriptInstance* instance = boxNode->CreateComponent<ScriptInstance>();
        instance->CreateObject(cache->GetResource<ScriptFile>("Scripts/Rotator.as"), "Rotator");
        // Call the script object's "SetRotationSpeed" function. Function arguments need to be passed in a VariantVector
        VariantVector parameters;
        parameters.Push(Vector3(10.0f, 20.0f, 30.0f));
        instance->Execute("void SetRotationSpeed(const Vector3&in)", parameters);
    }
    
    // Create the camera. Let the starting position be at the world origin. As the fog limits maximum visible distance, we can
    // bring the far clip plane closer for more effective culling of distant objects
    cameraNode_ = scene_->CreateChild("Camera");
    Camera* camera = cameraNode_->CreateComponent<Camera>();
    camera->SetFarClip(100.0f);
    
    // Create a point light to the camera scene node
    Light* light = cameraNode_->CreateComponent<Light>();
    light->SetLightType(LIGHT_POINT);
    light->SetRange(30.0f);
}
예제 #16
0
void ScriptInstance::HandlePhysicsPostStep(StringHash eventType, VariantMap& eventData)
{
    if (!scriptObject_)
        return;

    using namespace PhysicsPostStep;

    VariantVector parameters;
    parameters.Push(eventData[P_TIMESTEP]);
    scriptFile_->Execute(scriptObject_, methods_[METHOD_FIXEDPOSTUPDATE], parameters);
}
예제 #17
0
void ScriptInstance::HandleScenePostUpdate(StringHash eventType, VariantMap& eventData)
{
    if (!active_ || !scriptObject_)
        return;
    
    using namespace ScenePostUpdate;
    
    VariantVector parameters;
    parameters.Push(eventData[P_TIMESTEP]);
    scriptFile_->Execute(scriptObject_, methods_[METHOD_POSTUPDATE], parameters);
}
예제 #18
0
VariantVector Cursor::GetShapesAttr() const
{
    VariantVector ret;

    for (HashMap<String, CursorShapeInfo>::ConstIterator i = shapeInfos_.Begin(); i != shapeInfos_.End(); ++i)
    {
        if (i->second_.imageRect_ != IntRect::ZERO)
        {
            // Could use a map but this simplifies the UI xml.
            VariantVector shape;
            shape.Push(i->first_);
            shape.Push(GetResourceRef(i->second_.texture_, Texture2D::GetTypeStatic()));
            shape.Push(i->second_.imageRect_);
            shape.Push(i->second_.hotSpot_);
            ret.Push(shape);
        }
    }

    return ret;
}
예제 #19
0
VariantVector ParticleEmitter::GetParticlesAttr() const
{
    VariantVector ret;
    ret.Reserve(particles_.Size() * 8 + 1);
    ret.Push(particles_.Size());
    for (PODVector<Particle>::ConstIterator i = particles_.Begin(); i != particles_.End(); ++i)
    {
        ret.Push(i->velocity_);
        ret.Push(i->size_);
        ret.Push(i->timer_);
        ret.Push(i->timeToLive_);
        ret.Push(i->scale_);
        ret.Push(i->rotationSpeed_);
        ret.Push(i->colorIndex_);
        ret.Push(i->texIndex_);
    }
    return ret;
}
예제 #20
0
PODVector<unsigned char> ScriptInstance::GetScriptDataAttr() const
{
    if (!scriptObject_ || !methods_[METHOD_SAVE])
        return PODVector<unsigned char>();
    else
    {
        VectorBuffer buf;
        VariantVector parameters;
        parameters.Push(Variant((void*)static_cast<Serializer*>(&buf)));
        scriptFile_->Execute(scriptObject_, methods_[METHOD_SAVE], parameters);
        return buf.GetBuffer();
    }
}
예제 #21
0
VariantVector XMLElement::GetVariantVector() const
{
    VariantVector ret;

    XMLElement variantElem = GetChild("variant");
    while (variantElem)
    {
        ret.Push(variantElem.GetVariant());
        variantElem = variantElem.GetNext("variant");
    }

    return ret;
}
예제 #22
0
void JSONValue::GetVariantVector(VariantVector& variantVector) const
{
    if (!IsArray())
    {
        LOGERROR("JSONValue is not a array");
        return;
    }

    for (unsigned i = 0; i < Size(); ++i)
    {
        Variant variant;
        (*this)[i].GetVariant(variant);
        variantVector.Push(variant);
    }
}
예제 #23
0
void ScriptFile::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
{
    if (!compiled_)
        return;
    
    asIScriptFunction* function = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
    
    VariantVector parameters;
    if (function->GetParamCount() > 0)
    {
        parameters.Push(Variant((void*)&eventType));
        parameters.Push(Variant((void*)&eventData));
    }
    
    Execute(function, parameters);
}
예제 #24
0
void ScriptInstance::HandleScriptEvent(StringHash eventType, VariantMap& eventData)
{
    if (!active_ || !scriptFile_ || !scriptObject_)
        return;
    
    asIScriptFunction* method = static_cast<asIScriptFunction*>(GetEventHandler()->GetUserData());
    
    VariantVector parameters;
    if (method->GetParamCount() > 0)
    {
        parameters.Push(Variant((void*)&eventType));
        parameters.Push(Variant((void*)&eventData));
    }
    
    scriptFile_->Execute(scriptObject_, method, parameters);
}
예제 #25
0
void ScriptInstance::HandleSceneUpdate(StringHash eventType, VariantMap& eventData)
{
    if (!active_ || !scriptObject_)
        return;
    
    using namespace SceneUpdate;
    
    float timeStep = eventData[P_TIMESTEP].GetFloat();
    
    // Execute delayed method calls
    for (unsigned i = 0; i < delayedMethodCalls_.Size();)
    {
        DelayedMethodCall& call = delayedMethodCalls_[i];
        bool remove = false;
        
        call.delay_ -= timeStep;
        if (call.delay_ <= 0.0f)
        {
            if (!call.repeat_)
                remove = true;
            else
                call.delay_ += call.period_;
            
            Execute(call.declaration_, call.parameters_);
        }
        
        if (remove)
            delayedMethodCalls_.Erase(i);
        else
            ++i;
    }
    
    // Execute delayed start before first update
    if (methods_[METHOD_DELAYEDSTART])
    {
        scriptFile_->Execute(scriptObject_, methods_[METHOD_DELAYEDSTART]);
        methods_[METHOD_DELAYEDSTART] = 0;  // Only execute once
    }
    
    if (methods_[METHOD_UPDATE])
    {
        VariantVector parameters;
        parameters.Push(timeStep);
        scriptFile_->Execute(scriptObject_, methods_[METHOD_UPDATE], parameters);
    }
}
예제 #26
0
VariantVector BillboardSet::GetBillboardsAttr() const
{
    VariantVector ret;
    ret.Reserve(billboards_.Size() * 6 + 1);
    ret.Push(billboards_.Size());

    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
    {
        ret.Push(i->position_);
        ret.Push(i->size_);
        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
        ret.Push(i->color_);
        ret.Push(i->rotation_);
        ret.Push(i->enabled_);
    }

    return ret;
}
예제 #27
0
VariantVector AnimatedModel::GetAnimationStatesAttr() const
{
    VariantVector ret;
    ret.Reserve(animationStates_.Size() * 6 + 1);
    ret.Push(animationStates_.Size());
    for (Vector<SharedPtr<AnimationState> >::ConstIterator i = animationStates_.Begin(); i != animationStates_.End(); ++i)
    {
        AnimationState* state = *i;
        Animation* animation = state->GetAnimation();
        Bone* startBone = state->GetStartBone();
        ret.Push(GetResourceRef(animation, Animation::GetTypeStatic()));
        ret.Push(startBone ? startBone->name_ : String::EMPTY);
        ret.Push(state->IsLooped());
        ret.Push(state->GetWeight());
        ret.Push(state->GetTime());
        ret.Push((int)state->GetLayer());
    }
    return ret;
}
예제 #28
0
VariantVector RaycastVehicle::GetWheelDataAttr() const
{
    VariantVector ret;
    ret.Reserve(GetNumWheels() * 22 + 1);
    ret.Push(GetNumWheels());
    for (int i = 0; i < GetNumWheels(); i++)
    {
        Node* wNode = GetWheelNode(i);
        int node_id = wNode->GetID();
        URHO3D_LOGDEBUG("RaycastVehicle: Saving node id = " + String(node_id));
        ret.Push(node_id);
        ret.Push(GetWheelDirection(i));
        ret.Push(GetWheelAxle(i));
        ret.Push(GetWheelRestLength(i));
        ret.Push(GetWheelRadius(i));
        ret.Push(IsFrontWheel(i));
        ret.Push(GetSteeringValue(i));
        ret.Push(GetWheelConnectionPoint(i));
        ret.Push(origRotation_[i]);
        ret.Push(GetWheelSkidInfoCumulative(i));
        ret.Push(GetWheelSideSlipSpeed(i));
        ret.Push(WheelIsGrounded(i));
        ret.Push(GetContactPosition(i));
        ret.Push(GetContactNormal(i));       // 14
        ret.Push(GetWheelSuspensionStiffness(i));
        ret.Push(GetWheelDampingRelaxation(i));
        ret.Push(GetWheelDampingCompression(i));
        ret.Push(GetWheelFrictionSlip(i));
        ret.Push(GetWheelRollInfluence(i));
        ret.Push(GetEngineForce(i));
        ret.Push(GetBrake(i));
        ret.Push(GetWheelSkidInfo(i));
    }
    URHO3D_LOGDEBUG("RaycastVehicle: saved items: " + String(ret.Size()));
    URHO3D_LOGDEBUG("maxSideSlipSpeed_ value save: " + String(maxSideSlipSpeed_));
    return ret;
}
예제 #29
0
void SceneResolver::Resolve()
{
    // Nodes do not have component or node ID attributes, so only have to go through components
    HashSet<StringHash> noIDAttributes;
    for (HashMap<unsigned, WeakPtr<Component> >::ConstIterator i = components_.Begin(); i != components_.End(); ++i)
    {
        Component* component = i->second_;
        if (!component || noIDAttributes.Contains(component->GetType()))
            continue;

        bool hasIDAttributes = false;
        const Vector<AttributeInfo>* attributes = component->GetAttributes();
        if (!attributes)
        {
            noIDAttributes.Insert(component->GetType());
            continue;
        }

        for (unsigned j = 0; j < attributes->Size(); ++j)
        {
            const AttributeInfo& info = attributes->At(j);
            if (info.mode_ & AM_NODEID)
            {
                hasIDAttributes = true;
                unsigned oldNodeID = component->GetAttribute(j).GetUInt();

                if (oldNodeID)
                {
                    HashMap<unsigned, WeakPtr<Node> >::ConstIterator k = nodes_.Find(oldNodeID);

                    if (k != nodes_.End() && k->second_)
                    {
                        unsigned newNodeID = k->second_->GetID();
                        component->SetAttribute(j, Variant(newNodeID));
                    }
                    else
                        URHO3D_LOGWARNING("Could not resolve node ID " + String(oldNodeID));
                }
            }
            else if (info.mode_ & AM_COMPONENTID)
            {
                hasIDAttributes = true;
                unsigned oldComponentID = component->GetAttribute(j).GetUInt();

                if (oldComponentID)
                {
                    HashMap<unsigned, WeakPtr<Component> >::ConstIterator k = components_.Find(oldComponentID);

                    if (k != components_.End() && k->second_)
                    {
                        unsigned newComponentID = k->second_->GetID();
                        component->SetAttribute(j, Variant(newComponentID));
                    }
                    else
                        URHO3D_LOGWARNING("Could not resolve component ID " + String(oldComponentID));
                }
            }
            else if (info.mode_ & AM_NODEIDVECTOR)
            {
                hasIDAttributes = true;
                const VariantVector& oldNodeIDs = component->GetAttribute(j).GetVariantVector();

                if (oldNodeIDs.Size())
                {
                    // The first index stores the number of IDs redundantly. This is for editing
                    unsigned numIDs = oldNodeIDs[0].GetUInt();
                    VariantVector newIDs;
                    newIDs.Push(numIDs);

                    for (unsigned k = 1; k < oldNodeIDs.Size(); ++k)
                    {
                        unsigned oldNodeID = oldNodeIDs[k].GetUInt();
                        HashMap<unsigned, WeakPtr<Node> >::ConstIterator l = nodes_.Find(oldNodeID);

                        if (l != nodes_.End() && l->second_)
                            newIDs.Push(l->second_->GetID());
                        else
                        {
                            // If node was not found, retain number of elements, just store ID 0
                            newIDs.Push(0);
                            URHO3D_LOGWARNING("Could not resolve node ID " + String(oldNodeID));
                        }
                    }

                    component->SetAttribute(j, newIDs);
                }
            }
        }

        // If component type had no ID attributes, cache this fact for optimization
        if (!hasIDAttributes)
            noIDAttributes.Insert(component->GetType());
    }

    // Attributes have been resolved, so no need to remember the nodes after this
    Reset();
}