Ejemplo n.º 1
0
void CommandDeleteT::Undo()
{
    wxASSERT(m_Done);
    if (!m_Done) return;

    ArrayT<MapElementT*> InsertedElems;

    for (unsigned long PrimNr=0; PrimNr<m_DeletePrims.Size(); PrimNr++)
    {
        m_MapDoc.Insert(m_DeletePrims[PrimNr], m_DeletePrimsParents[PrimNr]);
        InsertedElems.PushBack(m_DeletePrims[PrimNr]);
    }

    for (unsigned long EntNr=0; EntNr<m_DeleteEnts.Size(); EntNr++)
    {
        m_MapDoc.Insert(m_DeleteEnts[EntNr]);
        InsertedElems.PushBack(m_DeleteEnts[EntNr]);
    }

    // Update all observers.
    m_MapDoc.UpdateAllObservers_Created(InsertedElems);

    // Select the previously selected elements again.
    m_CommandSelect->Undo();

    m_Done=false;
}
Ejemplo n.º 2
0
void BspTreeBuilderT::QuickSortFacesIntoTexNameOrder()
{
    while (ToDoRanges.Size()>=2)
    {
        const unsigned long LastIndex =ToDoRanges[ToDoRanges.Size()-1]; ToDoRanges.DeleteBack();
        const unsigned long FirstIndex=ToDoRanges[ToDoRanges.Size()-1]; ToDoRanges.DeleteBack();

        if (FirstIndex<LastIndex)
        {
            const char*   x=FaceChildren[LastIndex]->Material->Name.c_str();
            unsigned long i=FirstIndex-1;

            for (unsigned long j=FirstIndex; j<=LastIndex-1; j++)
                if (_stricmp(FaceChildren[j]->Material->Name.c_str(), x)<0)
                {
                    i++;
                    std::swap(FaceChildren[i], FaceChildren[j]);
                    std::swap(FaceNrs[i], FaceNrs[j]);
                }

            std::swap(FaceChildren[i+1], FaceChildren[LastIndex]);
            std::swap(FaceNrs[i+1], FaceNrs[LastIndex]);

            i++;

            ToDoRanges.PushBack(i+1); ToDoRanges.PushBack(LastIndex);
            if (i>0) { ToDoRanges.PushBack(FirstIndex); ToDoRanges.PushBack(i-1); }
        }
    }
}
Ejemplo n.º 3
0
bool CommandDeleteT::Do()
{
    wxASSERT(!m_Done);
    if (m_Done) return false;

    // Deselect any affected elements that are selected.
    m_CommandSelect->Do();

    ArrayT<MapElementT*> DeletedElems;

    for (unsigned long EntNr=0; EntNr<m_DeleteEnts.Size(); EntNr++)
    {
        m_MapDoc.Remove(m_DeleteEnts[EntNr]);
        DeletedElems.PushBack(m_DeleteEnts[EntNr]);
    }

    for (unsigned long PrimNr=0; PrimNr<m_DeletePrims.Size(); PrimNr++)
    {
        m_MapDoc.Remove(m_DeletePrims[PrimNr]);
        DeletedElems.PushBack(m_DeletePrims[PrimNr]);
    }

    // Update all observers.
    m_MapDoc.UpdateAllObservers_Deleted(DeletedElems);

    m_Done=true;
    return true;
}
Ejemplo n.º 4
0
void CommandDeleteT::Init(const ArrayT<MapElementT*>& DeleteElems)
{
    // Split the list of elements into a list of primitives and a list of entities.
    // The lists are checked for duplicates and kept free of them as well.
    for (unsigned long ElemNr=0; ElemNr<DeleteElems.Size(); ElemNr++)
    {
        MapElementT*   Elem=DeleteElems[ElemNr];
        MapPrimitiveT* Prim=dynamic_cast<MapPrimitiveT*>(Elem);
        MapEntityT*    Ent =dynamic_cast<MapEntityT*>(Elem);

        if (Prim)
        {
            MapEntityT* Parent=dynamic_cast<MapEntityT*>(Prim->GetParent());

            if (Parent && IsEntirelyDeleted(Parent, DeleteElems))
            {
                // If the parent is a regular entity (not the world!) that is entirely deleted anyway,
                // add the parent to the records instead of the individual primitive.
                if (m_DeleteEnts.Find(Parent)==-1) m_DeleteEnts.PushBack(Parent);
            }
            else
            {
                if (m_DeletePrims.Find(Prim)==-1)
                {
                    m_DeletePrims.PushBack(Prim);
                    m_DeletePrimsParents.PushBack(Prim->GetParent());
                }
            }
            continue;
        }

        if (Ent)
        {
            if (m_DeleteEnts.Find(Ent)==-1) m_DeleteEnts.PushBack(Ent);
            continue;
        }
    }


    // Build the combined list of all deleted elements in order to unselect them.
    ArrayT<MapElementT*> Unselect;

    for (unsigned long PrimNr=0; PrimNr<m_DeletePrims.Size(); PrimNr++)
        Unselect.PushBack(m_DeletePrims[PrimNr]);

    for (unsigned long EntNr=0; EntNr<m_DeleteEnts.Size(); EntNr++)
    {
        Unselect.PushBack(m_DeleteEnts[EntNr]);

        for (unsigned long PrimNr=0; PrimNr<m_DeleteEnts[EntNr]->GetPrimitives().Size(); PrimNr++)
            Unselect.PushBack(m_DeleteEnts[EntNr]->GetPrimitives()[PrimNr]);
    }

    m_CommandSelect=CommandSelectT::Remove(&m_MapDoc, Unselect);
}
Ejemplo n.º 5
0
void InspDlgEntityPropsT::OnContextMenuItemDelete(wxCommandEvent& event)
{
    // Just in case the user pressed e.g. CTRL+Q (clear selection) in the meanwhile...
    if (LastRightClickedProperty==NULL) return;

    // Only handle deletes on properties that are not a category.
    if (LastRightClickedProperty->IsCategory()) return;

    // Key is not undefined (cannot be deleted).
    if (LastRightClickedProperty->GetParent()!=UnDefKeys)
    {
        wxMessageBox("Only undefined keys can be deleted.");
        return;
    }

    ArrayT<CommandT*> Commands;

    for (unsigned long i=0; i<SelectedEntities.Size(); i++)
        Commands.PushBack(new CommandDeletePropertyT(*MapDoc, SelectedEntities[i], LastRightClickedProperty->GetName()));

    CommandT* MacroCommand=new CommandMacroT(Commands, "Delete Property '"+LastRightClickedProperty->GetName()+"'");

    // Note that we also receive the update notification recursively (triggered by command execution in SubmitCommand()).
    // This is intentional, or else we had to manually fix the PropMan, CombinedPropInfos, etc. here, which is error-prone.
    // The complete recursive update is much less work and 100% safe, because it guarantees that no new bugs are introduced here.
    MapDoc->GetHistory().SubmitCommand(MacroCommand);
}
Ejemplo n.º 6
0
bool EditorChoiceWindowT::HandlePGChange(wxPropertyGridEvent& Event, GuiEditor::ChildFrameT* ChildFrame)
{
    if (EditorWindowT::HandlePGChange(Event, ChildFrame)) return true;

    const wxPGProperty* Prop    =Event.GetProperty();
    const wxString      PropName=Prop->GetName();

    if (PropName=="Choices")
    {
        ArrayT<std::string> NewStrings;
        wxStringTokenizer   Tokenizer(Prop->GetValueAsString(), "\r\n");

        while (Tokenizer.HasMoreTokens())
            NewStrings.PushBack(std::string(Tokenizer.GetNextToken()));

        ChildFrame->SubmitCommand(new CommandSetWinPropT< ArrayT<std::string> >(m_GuiDoc, this, PropName, m_Choice->m_Choices, NewStrings));
        return true;
    }

    if (PropName=="DefaultChoice")
    {
        wxASSERT(m_Choice->GetMemberVar("selectedChoice").Member!=NULL);

        ChildFrame->SubmitCommand(new CommandModifyWindowT(m_GuiDoc, m_Choice, PropName, m_Choice->GetMemberVar("selectedChoice"), Prop->GetValue().GetLong()));
        return true;
    }

    return false;
}
Ejemplo n.º 7
0
void SubjectT::UpdateAllObservers_Modified(IntrusivePtrT<cf::GuiSys::WindowT> Window, WindowModDetailE Detail, const wxString& PropertyName)
{
    ArrayT< IntrusivePtrT<cf::GuiSys::WindowT> > Windows;
    Windows.PushBack(Window);

    UpdateAllObservers_Modified(Windows, Detail, PropertyName);
}
Ejemplo n.º 8
0
CommandSelectT* CommandSelectT::Add(GuiDocumentT* GuiDocument, IntrusivePtrT<cf::GuiSys::WindowT> Window)
{
    ArrayT< IntrusivePtrT<cf::GuiSys::WindowT> > AddSelection;
    AddSelection.PushBack(Window);

    return CommandSelectT::Add(GuiDocument, AddSelection);
}
Ejemplo n.º 9
0
CommandSelectT* CommandSelectT::Remove(GuiDocumentT* GuiDocument, IntrusivePtrT<cf::GuiSys::WindowT> Window)
{
    ArrayT< IntrusivePtrT<cf::GuiSys::WindowT> > RemoveSelection;
    RemoveSelection.PushBack(Window);

    return CommandSelectT::Remove(GuiDocument, RemoveSelection);
}
Ejemplo n.º 10
0
void SubjectT::UpdateAllObservers_Modified(IntrusivePtrT<cf::GuiSys::WindowT> Window, WindowModDetailE Detail)
{
    ArrayT< IntrusivePtrT<cf::GuiSys::WindowT> > Windows;
    Windows.PushBack(Window);

    UpdateAllObservers_Modified(Windows, Detail);
}
Ejemplo n.º 11
0
void SubjectT::UpdateAllObservers_Deleted(IntrusivePtrT<cf::GuiSys::WindowT> Window)
{
    ArrayT< IntrusivePtrT<cf::GuiSys::WindowT> > Windows;
    Windows.PushBack(Window);

    UpdateAllObservers_Deleted(Windows);
}
Ejemplo n.º 12
0
void CommandTransformT::Undo()
{
    wxASSERT(m_Done);
    if (!m_Done) return;

    if (m_DoClone)
    {
        m_CommandSelect->Undo();

        // Remove cloned objects from world again.
        for (unsigned long CloneNr=0; CloneNr<m_ClonedElems.Size(); CloneNr++)
            m_MapDoc.Remove(m_ClonedElems[CloneNr]);

        m_MapDoc.UpdateAllObservers_Deleted(m_ClonedElems);
    }
    else
    {
        // Record the previous bounding-boxes for the observer message.
        ArrayT<BoundingBox3fT> OldBounds;

        for (unsigned long ElemNr=0; ElemNr<m_TransElems.Size(); ElemNr++)
        {
            OldBounds.PushBack(m_TransElems[ElemNr]->GetBB());
            m_TransElems[ElemNr]->Assign(m_OldStates[ElemNr]);
        }

        m_MapDoc.UpdateAllObservers_Modified(m_TransElems, MEMD_TRANSFORM, OldBounds);
    }

    m_Done=false;
}
Ejemplo n.º 13
0
bool CommandTransformT::Do()
{
    wxASSERT(!m_Done);
    if (m_Done) return false;

    if (m_DoClone)
    {
        // Insert cloned objects into the document, attaching them to the same parents as the respective source element.
        for (unsigned long CloneNr=0; CloneNr<m_ClonedElems.Size(); CloneNr++)
        {
            MapEntityT* Ent=dynamic_cast<MapEntityT*>(m_ClonedElems[CloneNr]);
            if (Ent)
            {
                m_MapDoc.Insert(Ent);
                continue;
            }

            MapPrimitiveT* ClonedPrim=dynamic_cast<MapPrimitiveT*>(m_ClonedElems[CloneNr]);
            MapPrimitiveT* OrigPrim  =dynamic_cast<MapPrimitiveT*>(m_TransElems[CloneNr]);
            wxASSERT((ClonedPrim==NULL)==(OrigPrim==NULL));
            if (ClonedPrim && OrigPrim)
            {
                m_MapDoc.Insert(ClonedPrim, OrigPrim->GetParent());
                continue;
            }

            // TODO(?): Insert m_ClonedElems[CloneNr] into the same group as m_TransElems[CloneNr]?
        }

        m_MapDoc.UpdateAllObservers_Created(m_ClonedElems);
        m_CommandSelect->Do();
    }
    else
    {
        // Record the previous bounding-boxes for the observer message.
        ArrayT<BoundingBox3fT> OldBounds;

        for (unsigned long ElemNr=0; ElemNr<m_TransElems.Size(); ElemNr++)
        {
            OldBounds.PushBack(m_TransElems[ElemNr]->GetBB());

            switch (m_Mode)
            {
                case MODE_TRANSLATE: m_TransElems[ElemNr]->TrafoMove(m_Amount);               break;
                case MODE_ROTATE:    m_TransElems[ElemNr]->TrafoRotate(m_RefPoint, m_Amount); break;
                case MODE_SCALE:     m_TransElems[ElemNr]->TrafoScale(m_RefPoint, m_Amount);  break;
                case MODE_MATRIX:    m_TransElems[ElemNr]->Transform(m_Matrix);               break;
            }
        }

        m_MapDoc.UpdateAllObservers_Modified(m_TransElems, MEMD_TRANSFORM, OldBounds);
    }

    m_Done=true;
    return true;
}
Ejemplo n.º 14
0
template<class T> void Polygon3T<T>::GetChoppedUpAlong(const Polygon3T<T>& SplittingPoly, const T EdgeThickness, ArrayT< Polygon3T<T> >& NewPolys) const
{
    Polygon3T<T> FragmentPoly=*this;

    NewPolys.Clear();

    for (unsigned long VertexNr=0; VertexNr<SplittingPoly.Vertices.Size(); VertexNr++)
    {
        const Plane3T<T> SplitPlane=SplittingPoly.GetEdgePlane(VertexNr, EdgeThickness);

        if (FragmentPoly.WhatSideSimple(SplitPlane, EdgeThickness)!=Both) continue;

        ArrayT< Polygon3T<T> > SplitResult=FragmentPoly.GetSplits(SplitPlane, EdgeThickness);

        FragmentPoly=SplitResult[0];
        NewPolys.PushBack(SplitResult[1]);
    }

    NewPolys.PushBack(FragmentPoly);
}
Ejemplo n.º 15
0
CommandDeleteT::CommandDeleteT(MapDocumentT& MapDoc, MapElementT* DeleteElem)
    : m_MapDoc(MapDoc),
      m_DeleteEnts(),
      m_DeletePrims(),
      m_DeletePrimsParents(),
      m_CommandSelect(NULL)
{
    ArrayT<MapElementT*> DeleteElems;

    DeleteElems.PushBack(DeleteElem);
    Init(DeleteElems);
}
Ejemplo n.º 16
0
void SubmodelsListT::UnloadSelectedSubmodels()
{
    // We have to make the "detour" via the DelSM array, because unloading any submodel potentially modifies the indices of the rest.
    ArrayT<ModelDocumentT::SubmodelT*> DelSM;

    for (long SelNr=GetFirstSelected(); SelNr!=-1; SelNr=GetNextSelected(SelNr))
        DelSM.PushBack(m_ModelDoc->GetSubmodels()[SelNr]);

    for (unsigned long SMNr=0; SMNr<DelSM.Size(); SMNr++)
        m_ModelDoc->UnloadSubmodel(m_ModelDoc->GetSubmodels().Find(DelSM[SMNr]));

    m_ModelDoc->UpdateAllObservers_SubmodelsChanged();
}
Ejemplo n.º 17
0
void CommandDeletePropertyT::Undo()
{
    wxASSERT(m_Done);
    if (!m_Done) return;

    m_Entity->GetProperties().InsertAt(m_Index, m_PropBackup);

    ArrayT<MapElementT*> MapElements;
    MapElements.PushBack(m_Entity);

    m_MapDoc.UpdateAllObservers_Modified(MapElements, MEMD_ENTITY_PROPERTY_CREATED, m_PropBackup.Key);

    m_Done=false;
}
Ejemplo n.º 18
0
// Diese Funktion sortiert die Faces anhand ihres Texture-Namens in aufsteigender Reihenfolge.
// Dank Z-Buffering kann die Engine damit die Faces in dieser Reihenfolge mit einem Minimum von State-Changes rendern.
// Da außerdem die LightMaps der Faces in dieser Reihenfolge in die größeren LightMaps einsortiert werden
// (CreateFullBrightLightMaps()), erhalten wir den selben positiven Effekt auch für die LightMaps!
void BspTreeBuilderT::SortFacesIntoTexNameOrder()
{
    if (FaceChildren.Size()==0) return;

    Console->Print(cf::va("\n%-50s %s\n", "*** Sort Faces ***", GetTimeSinceProgramStart()));

    FaceNrs.Clear();
    for (unsigned long FaceNr=0; FaceNr<FaceChildren.Size(); FaceNr++) FaceNrs.PushBack(FaceNr);

    // QuickSort Faces according to their texture name.
    ToDoRanges.Clear();
    ToDoRanges.PushBack(0);
    ToDoRanges.PushBack(FaceChildren.Size()-1);

    QuickSortFacesIntoTexNameOrder();

    // Verify sorting.
    for (unsigned long FaceNr=0; FaceNr+1<FaceChildren.Size(); FaceNr++)
        if (_stricmp(FaceChildren[FaceNr]->Material->Name.c_str(), FaceChildren[FaceNr+1]->Material->Name.c_str())>0) Error("Bad sorting!");

    // Wir wissen nun, daß an Stelle der Face i nun die Face FaceNrs[i] steht, wollen aber wissen, an welcher
    // Stelle nun die i-te Face steht. Führe dazu das RevFaceNrs-Array ein und fülle es entsprechend aus.
    ArrayT<unsigned long> RevFaceNrs;

    RevFaceNrs.PushBackEmpty(FaceChildren.Size());
    for (unsigned long FaceNr=0; FaceNr<FaceChildren.Size(); FaceNr++) RevFaceNrs[FaceNrs[FaceNr]]=FaceNr;

    // Korrigiere damit die FaceSets der Leaves.
    ArrayT<cf::SceneGraph::BspTreeNodeT::LeafT>& Leaves=BspTree->Leaves;

    for (unsigned long LeafNr=0; LeafNr<Leaves.Size(); LeafNr++)
        for (unsigned long FaceNr=0; FaceNr<Leaves[LeafNr].FaceChildrenSet.Size(); FaceNr++)
            Leaves[LeafNr].FaceChildrenSet[FaceNr]=RevFaceNrs[Leaves[LeafNr].FaceChildrenSet[FaceNr]];

    Console->Print("done\n");
}
Ejemplo n.º 19
0
void EngineEntityT::WriteNewBaseLine(unsigned long SentClientBaseLineFrameNr, ArrayT< ArrayT<char> >& OutDatas) const
{
    // Nur dann etwas tun, wenn unsere 'BaseLineFrameNr' größer (d.h. jünger) als 'SentClientBaseLineFrameNr' ist,
    // d.h. unsere 'BaseLineFrameNr' noch nie / noch nicht an den Client gesendet wurde.
    if (SentClientBaseLineFrameNr>=BaseLineFrameNr) return;

    NetDataT NewBaseLineMsg;

    NewBaseLineMsg.WriteByte(SC1_EntityBaseLine);
    NewBaseLineMsg.WriteLong(Entity->GetID());
    NewBaseLineMsg.WriteLong(Entity->GetType()->TypeNr);
    NewBaseLineMsg.WriteLong(Entity->GetWorldFileIndex());
    NewBaseLineMsg.WriteDMsg(m_BaseLine.GetDeltaMessage(cf::Network::StateT() /*::ALL_ZEROS*/));

    OutDatas.PushBack(NewBaseLineMsg.Data);
}
Ejemplo n.º 20
0
void BspTreeBuilderT::BuildBSPPortals(unsigned long NodeNr, ArrayT< Plane3T<double> >& NodeList)
{
    const ArrayT<cf::SceneGraph::BspTreeNodeT::NodeT>& Nodes=BspTree->Nodes;

    NodeList.PushBack(Nodes[NodeNr].Plane.GetMirror());

    if (Nodes[NodeNr].FrontIsLeaf) CreateLeafPortals(Nodes[NodeNr].FrontChild, NodeList);
                              else BuildBSPPortals(Nodes[NodeNr].FrontChild, NodeList);

    NodeList[NodeList.Size()-1]=Nodes[NodeNr].Plane;

    if (Nodes[NodeNr].BackIsLeaf) CreateLeafPortals(Nodes[NodeNr].BackChild, NodeList);
                             else BuildBSPPortals(Nodes[NodeNr].BackChild, NodeList);

    NodeList.DeleteBack();
}
Ejemplo n.º 21
0
ArrayT<const SoundShaderT*> SoundShaderManagerImplT::RegisterSoundShaderScript(const std::string& ScriptFile, const std::string& ModDir)
{
    ArrayT<const SoundShaderT*> NewSoundShaders;

    // Check if script has already been loaded and return empty sound shader array if this is the case.
    for (unsigned long Nr=0; Nr<m_SoundShaderScriptFiles.Size(); Nr++)
        if (m_SoundShaderScriptFiles[Nr]==ScriptFile) return NewSoundShaders;

    m_SoundShaderScriptFiles.PushBack(ScriptFile);

    // Get sound shaders from the script.
    TextParserT TextParser(ScriptFile.c_str(), "({[]}),");

    try
    {
        while (!TextParser.IsAtEOF())
        {
            const std::string Token=TextParser.GetNextToken();

            // If the sound shader cannot be parsed (e.g. due to a syntax error or unknown token),
            // the parsing of the entire file is aborted - the file might be something else than a sound shader script.
            // Even if it was, we cannot easily continue anyway.

            SoundShaderT*  NewSoundShader=new SoundShaderT(Token, TextParser, ModDir);
            SoundShaderT*& TestShader    =m_SoundShaders[NewSoundShader->Name];

            // Check if sound shader with this name already exists.
            if (TestShader==NULL)
            {
                TestShader=NewSoundShader;
                NewSoundShaders.PushBack(NewSoundShader);
            }
            else
            {
                std::cout << "File '"<< ScriptFile << "' sound shader '" << NewSoundShader->Name << "' duplicate definition (ignored).\n";
                delete NewSoundShader;
            }
        }
    }
    catch (const TextParserT::ParseError&)
    {
        std::cout << "Error parsing '" << ScriptFile << "' at input byte " << TextParser.GetReadPosByte() << "\n";
    }

    return NewSoundShaders;
}
Ejemplo n.º 22
0
bool CommandDeletePropertyT::Do()
{
    wxASSERT(!m_Done);
    if (m_Done) return false;
    if (m_Index<0) return false;

    // Note that only non-class keys can be deleted here, so we don't need to call anything MapEntityT specific here - children don't need to be notified.
    m_Entity->GetProperties().RemoveAtAndKeepOrder(m_Index);

    // FIXME Note that when a property of multiple entities is deleted, this observer notification is created
    // for EACH of these entities. We should change the command to accept an array of entities...
    ArrayT<MapElementT*> MapElements;
    MapElements.PushBack(m_Entity);

    m_MapDoc.UpdateAllObservers_Modified(MapElements, MEMD_ENTITY_PROPERTY_DELETED, m_PropBackup.Key);

    m_Done=true;
    return true;
}
Ejemplo n.º 23
0
void InspDlgEntityPropsT::OnContextMenuItemAdd(wxCommandEvent& event)
{
    const wxString NewKey=wxGetTextFromUser("Please enter the name of the new key.", "New Key", "");
    if (NewKey=="") return;

    // Check if a property with this key already exists.
    if (PropMan->GetPropertyByName(NewKey)!=NULL)
    {
        wxMessageBox("A key with the same name already exists.", "Couldn't create property", wxOK | wxICON_ERROR);
        return;
    }

    ArrayT<CommandT*> Commands;

    for (unsigned long i=0; i<SelectedEntities.Size(); i++)
        Commands.PushBack(new CommandSetPropertyT(*MapDoc, SelectedEntities[i], NewKey, ""));

    CommandT* MacroCommand=new CommandMacroT(Commands, "Add Property '"+NewKey+"'");

    // Note that we also receive the update notification recursively (triggered by command execution in SubmitCommand()).
    // This is intentional, or else we had to manually fix the PropMan, CombinedPropInfos, etc. here, which is error-prone.
    // The complete recursive update is much less work and 100% safe, because it guarantees that no new bugs are introduced here.
    MapDoc->GetHistory().SubmitCommand(MacroCommand);
}
Ejemplo n.º 24
0
// Note that the world clip model is *not* in the list of returned clip models!
void ClipWorldT::GetClipModelsFromBB(ArrayT<ClipModelT*>& ClipModels, unsigned long ContentMask, const BoundingBox3dT& BB) const
{
    BoundingBox3dT ExpandedBB=BB.GetEpsilonBox(1.0);
    unsigned long  GridRect[4];

    GetGridRectFromBB(GridRect, BB);

#ifdef DEBUG
    for (unsigned long ModelNr=0; ModelNr<ClipModels.Size(); ModelNr++)
        assert(!ClipModels[ModelNr]->AlreadyChecked);
#endif

    for (unsigned long x=GridRect[0]; x<GridRect[2]; x++)
        for (unsigned long y=GridRect[1]; y<GridRect[3]; y++)
        {
            ClipSectorT& Sector=Sectors[y*SectorSubdivs + x];   // The sector at (x, y).

            if ((Sector.ModelContents & ContentMask)==0) continue;

            for (ClipLinkT* Link=Sector.ListOfModels; Link!=NULL; Link=Link->NextModelInSector)
            {
                ClipModelT* ClipModel=Link->ClipModel;

                if (ClipModel->AlreadyChecked) continue;
                if (!ClipModel->IsEnabled) continue;
                if ((ClipModel->GetContents() & ContentMask)==0) continue;
                if (!ClipModel->GetAbsoluteBB().Intersects(ExpandedBB)) continue;

                ClipModel->AlreadyChecked=true;
                ClipModels.PushBack(ClipModel);
            }
        }

    for (unsigned long ModelNr=0; ModelNr<ClipModels.Size(); ModelNr++)
        ClipModels[ModelNr]->AlreadyChecked=false;
}
Ejemplo n.º 25
0
void InspDlgEntityPropsT::OnPropertyGridChanged(wxPropertyGridEvent& event)
{
    // Load property info from property to get all entities that belong to this property.
    PropInfoT* PropInfo=(PropInfoT*)event.GetProperty()->GetClientData();

    wxASSERT(PropInfo!=NULL);               // Because the property grid items were created from a list of PropInfoTs.
    wxASSERT(PropInfo->KeyState!=MIXED);    // Because keys with such state have been disabled from editing.

    // Only write value change to entities if the property got its property info and the key isn't a MIXED key
    // (as they are not editable and thus have been disabled for editing above).
    if (PropInfo==NULL || PropInfo->KeyState==MIXED) return;


    wxString NewValue=event.GetProperty()->GetValueAsString();

    // We need to translate the value here according to property type.
    if (wxString(event.GetProperty()->GetClassInfo()->GetClassName())=="wxFlagsProperty" ||
        wxString(event.GetProperty()->GetClassInfo()->GetClassName())=="wxBoolProperty")
    {
        NewValue=wxString::Format("%li", event.GetProperty()->GetValue().GetLong());
    }
    else if (event.GetProperty()->GetValueType()=="wxColour")
    {
        wxColour NewColor; NewColor << event.GetProperty()->GetValue();

        NewValue=wxString::Format("%i %i %i", NewColor.Red(), NewColor.Green(), NewColor.Blue());
    }


    // Have the selected entities available as a set of MapElementTs as well.
    ArrayT<MapElementT*> MapElements;

    for (unsigned long i=0; i<SelectedEntities.Size(); i++)
        MapElements.PushBack(SelectedEntities[i]);

    CommandT* MacroCommand=NULL;

    // Changing the class of an entity.
    if (event.GetProperty()->GetName()=="classname")
    {
        // Only allow a class change if all entities share the same class.
        for (unsigned long i=1; i<SelectedEntities.Size(); i++)
        {
            if (SelectedEntities[i]->GetClass()!=SelectedEntities[0]->GetClass())
            {
                wxMessageBox("Entities have different entity classes.", "Error: Couldn't change entity class.", wxOK | wxICON_ERROR);

                // Intentionally update also this dialog to restore previous property value. TODO: Can the event be vetoed instead?
                NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, "");
                return;
            }
        }

        const EntityClassT* NewEntityClass=MapDoc->GetGameConfig()->FindClass(NewValue);

        if (NewEntityClass==NULL)
        {
            wxMessageBox("Could not find entity class \""+NewValue+"\" in the game config.", "Error: Couldn't change entity class.", wxOK | wxICON_ERROR);

            // Intentionally update also this dialog to restore previous property value. TODO: Can the event be vetoed instead?
            NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, "");
            return;
        }

        ArrayT<CommandT*> Commands;

        for (unsigned long i=0; i<SelectedEntities.Size(); i++)
        {
            MapEntityT* Ent=dynamic_cast<MapEntityT*>(SelectedEntities[i]);

            if (Ent)
                Commands.PushBack(new CommandChangeClassT(*MapDoc, Ent, NewEntityClass));
        }

        MacroCommand=new CommandMacroT(Commands, "Change entity class");
    }
    else
    {
        wxASSERT(SelectedEntities[0]!=NULL);

        const wxString Key=event.GetProperty()->GetName();

        // Check if key is found in all entities, in the same category (defined or undefined) and has the same type for all objects.
        // First entity as reference.
        bool Undefined=(SelectedEntities[0]->GetClass() && SelectedEntities[0]->GetClass()->FindVar(Key)==NULL);

        for (unsigned long i=1; i<SelectedEntities.Size(); i++)
        {
            if (Undefined) // Just check if key is also undefined (type is always string).
            {
                if (  (SelectedEntities[i]->GetClass() && SelectedEntities[i]->GetClass()->FindVar(Key)!=NULL)
                    || SelectedEntities[i]->FindProperty(Key)==NULL)
                {
                    wxMessageBox("Value is not defined by all entities.", "Error: Couldn't change property value.", wxOK | wxICON_ERROR);
                    NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value.
                    return;
                }
            }
            else // Check if key is defined and has the same type.
            {
                if (   SelectedEntities[i]->GetClass()==NULL
                    || SelectedEntities[i]->GetClass()->FindVar(Key)==NULL
                    || SelectedEntities[i]->GetClass()->FindVar(Key)->GetType()!=SelectedEntities[0]->GetClass()->FindVar(Key)->GetType())
                {
                    wxMessageBox("Value is not defined by all entities or hasn't the same type.", "Error: Couldn't change property value.", wxOK | wxICON_ERROR);
                    NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value.
                    return;
                }
            }

        }

        // Check Lua compatibility for entity names.
        if (Key=="name" && !IsLuaIdentifier(NewValue))
        {
            wxMessageBox("An entity name must be a string of letters, digits, and underscores that is\n"
                "not beginning with a digit and is not a reserved Lua keyword or global variable.",
                "Entity name is not a valid script identifier.", wxOK | wxICON_ERROR);

            NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value.
            return;
        }

        // Handle unique keys here.
        if (SelectedEntities[0]->GetClass()->FindVar(Key) && SelectedEntities[0]->GetClass()->FindVar(Key)->IsUnique())
        {
            // Only change unique when only one entity is selected.
            wxASSERT(SelectedEntities.Size()==1);
            // Since unique properties are disabled, when more than one entity is selected, we should never get here.
            // This is just a security precaution for release code. We forgo writing back the original value here, since this
            // case will probably never happen.
            if (SelectedEntities.Size()>1)
            {
                wxMessageBox("Unique keys can only be changed if one entity is selected.", "Error: Couldn't change unique key.", wxOK | wxICON_ERROR);
                NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value.
                return;
            }

            // Load all map entities.
            // We cannot call PropObjects[0]->CheckUniqueValues() here, since this method checks all values of the entity
            // for uniqueness so it would fail if another unique marked value is not unique.
            for (unsigned long EntNr=1/*skip world*/; EntNr<MapDoc->GetEntities().Size(); EntNr++)
            {
                MapEntityBaseT* Entity=MapDoc->GetEntities()[EntNr];

                      EntPropertyT*     Prop=Entity->FindProperty(Key);
                const EntClassVarT* ClassVar=Entity->GetClass() ? Entity->GetClass()->FindVar(Key) : NULL;

                // Check if new value is already used by an entity.
                if ((Prop!=NULL && Prop->Value==NewValue) || (ClassVar!=NULL && ClassVar->GetDefault()==NewValue))
                {
                    // Set value in property grid to old value and return.
                    wxMessageBox("This unique value is already used by another entity.", "Error: Couldn't change unique key.", wxOK | wxICON_ERROR);
                    NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, ""); // Intentionally update also this dialog to restore previous property value.
                    return;
                }
            }
        }

        // Set the property to its new value.
        ArrayT<CommandT*> Commands;

        for (unsigned long i=0; i<SelectedEntities.Size(); i++)
            Commands.PushBack(new CommandSetPropertyT(*MapDoc, SelectedEntities[i], Key, NewValue));

        MacroCommand=new CommandMacroT(Commands, "Change property '"+Key+"'");
    }

    // Execution of command triggers recursive update of this dialog. To prevent this we set IsRecursiveSelfNotify.
    IsRecursiveSelfNotify=true;

    // Run command (in SubmitCommand()) and react if its execution fails by updating the whole dialog recursively.
    // Also update whole dialog in case classname has changed.
    if (!MapDoc->GetHistory().SubmitCommand(MacroCommand) || event.GetProperty()->GetName()=="classname")
    {
        IsRecursiveSelfNotify=false;
        NotifySubjectChanged_Modified(MapDoc, MapElements, MEMD_ENTITY_PROPERTY_MODIFIED, "");
        return;
    }

    IsRecursiveSelfNotify=false;

    PropInfo->ValueIsConsistent=true;
    PropMan ->SetPropertyBackgroundColour(event.GetProperty(), COLOR_CUSTOM);

    // Special case: Model file of the entity has changed.
    if (PropInfo->ClassVar && PropInfo->ClassVar->GetType()==EntClassVarT::TYPE_FILE_MODEL && event.GetProperty()->GetName()=="model")
    {
        wxPGProperty* Insert=PropMan->GetPropertyByName("collisionModel");

        // If a collisionModel key is found color it to notify the user that it should be changed too.
        if (Insert!=NULL) PropMan->SetPropertyBackgroundColour(Insert, COLOR_WARNING);
    }

    // Check if newly set value is a DEFAULT value and update background color.
    if ((PropInfo->KeyState & NORMAL) && NewValue==PropInfo->ClassVar->GetDefault())
    {
        PropMan->SetPropertyBackgroundColour(event.GetProperty(), COLOR_DEFAULT);
        PropInfo->ValueIsDefault=true;
    }
}
Ejemplo n.º 26
0
// Diese Funktion bestimmt die Sichtpyramide ('Frustum'), die eine Lichtquelle 'LightSource' durch ein Loch 'Hole' wirft.
//
// Dabei leuchtet die 'LightSource' in die *entgegengesetzte* Richtung ihres Normalenvektors,
// und das 'Hole' läßt auch nur Licht in der Gegenrichtung seines Normalenvektors durch.
// Beides ist sinnvoll, denn sowohl 'LightSource' als auch 'Hole' sind idR Portale von Leaves.
// Beachte, daß die Normalenvektoren von Portalen stets zur *Mitte* ihrer Leaves hin zeigen (nicht nach außen wie bei Brushes).
// Die 'LightSource' ist dann ein Portal des vorangegangenen Leafs, durch das das aktuelle Leaf betreten wird.
// Das 'Hole' ist ein Portal des aktuellen Leafs zum nächsten Leaf.
// Bemerkung: Würde man das Loch zur Lichtquelle und umgekehrt machen (PolygonMirror),
// wäre das Frustum das gleiche, dessen Ebenen wären aber gespiegelt!
//
// Es wird vorausgesetzt, daß 'LightSource' und 'Hole' gültige Polygone sind.
// Wenn daraus erfolgreich ein Frustum konstruiert werden kann, wird dieses zurückgegeben und es gilt 'Frustum.Size()>0'.
// Andernfalls scheitert die Funktion und es wird ein Frustum der Größe 0 zurückgegeben ('Frustum.Size()==0').
// Die Voraussetzung für den Erfolg dieser Funktion ist eine - in unserem Sinne - "vernünftige" Anordnung der beiden Polygone:
// a) Das 'Hole' muß komplett auf der (Licht emittierenden) Rückseite der 'LightSource'-Ebene liegen.
// b) Die 'LightSource' muß komplett auf der (lichtdurchlässigen) Vorderseite der 'Hole'-Ebene liegen.
// Beachte, daß im allgemeinen bzw. erweiterten Sinne andere Frustren durchaus auch sinnvoll sein können,
// z.B. wenn die 'LightSource' und das 'Hole' sich schneiden.
// Solche Fälle betrachten wir jedoch als ungültig und sie führen zum Scheitern der Funktion.
inline void FindFrustum(const Polygon3T<double>& LightSource, const Polygon3T<double>& Hole, ArrayT< Plane3T<double> >& Frustum)
{
    Frustum.Overwrite();

    if (Hole.WhatSideSimple(LightSource.Plane, MapT::RoundEpsilon)!=Polygon3T<double>::Back) return;
    if (LightSource.WhatSideSimple(Hole.Plane, MapT::RoundEpsilon)!=Polygon3T<double>::Front) return;

    unsigned long V2=Hole.Vertices.Size()-1;
    unsigned long V3;

    for (V3=0; V3<Hole.Vertices.Size(); V3++)
    {
        for (unsigned long V1=0; V1<LightSource.Vertices.Size(); V1++)
        {
            // Eigentlich würde ich hier gerne folgenden Wunsch-Code schreiben:
            //     try
            //     {
            //         Plane3T<double> FrustumPlane(Hole.Vertices[V2], LightSource.Vertices[V1], Hole.Vertices[V3]);
            //
            //         // ...
            //     }
            //     catch (DivisionByZero) { }  // Nicht mögliche FrustumPlanes einfach ignorieren.
            // Aus irgendeinem Grund ist die Verwendung oder das Fangen der DivisionByZero-Exception aber sehr langsam.
            // Deshalb rolle ich lieber den Plane3T<double>-Konstruktor aus, um ohne dieses Exception-Handling auszukommen.
            // Das Programm wird *deutlich* schneller, ca. Faktor 1,5. Ob das eine Schwäche des Watcom-Compilers ist??
            VectorT Normal(cross(Hole.Vertices[V3]-Hole.Vertices[V2], LightSource.Vertices[V1]-Hole.Vertices[V2]));
            double  NLength=length(Normal);

            if (NLength<MapT::RoundEpsilon) continue;
            Normal=scale(Normal, 1.0/NLength);

            Plane3T<double> FrustumPlane(Normal, dot(Hole.Vertices[V2], Normal));

            // Diese neue FrustumPlane nur dann akzeptieren, wenn das Hole auf ihrer Vorderseite liegt
            // (konstruktionsbedingt sowieso der Fall!) und die LightSource auf ihrer Rückseite liegt.
            // Wenn eine Edge des Hole in der Ebene der LightSource liegt, darf die LightSource
            // auch in der FrustumPlane liegen.
            Polygon3T<double>::SideT Side=LightSource.WhatSideSimple(FrustumPlane, MapT::RoundEpsilon);

            if (Side==Polygon3T<double>::Back || Side==Polygon3T<double>::InMirrored)
            {
                Frustum.PushBack(FrustumPlane);
                break;
            }
        }

        V2=V3;
    }

    // Rollen vertauschen: Das Loch sei nun die Lichtquelle, und die Lichtquelle das Loch! Siehe Skizze!
    V2=LightSource.Vertices.Size()-1;

    for (V3=0; V3<LightSource.Vertices.Size(); V3++)
    {
        for (unsigned long V1=0; V1<Hole.Vertices.Size(); V1++) // Optimize: Check if edges are in already existing frustum planes!
        {
            // Es bringt übrigens nichts, doppelt auftretende Planes hier vermeiden zu wollen!
            // Messungen waren z.B. 1:09:05 ohne Prüfung, 1:08:42 mit Prüfung auf Doppelvorkommen.
            // Könnte man aber später nochmal überprüfen...
         /* // Prüfe, ob wir diese Plane schon im Frustum haben.
            // Teste dazu, ob die drei Punkte in der Plane liegen.
            // Die Orientierung braucht dabei nicht beachtet zu werden.
            for (unsigned long FrustumNr=0; FrustumNr<FrustumSize1stPart; FrustumNr++)
            {
                const double Dist1=PlaneDistance(Frustum[FrustumNr],        Hole.Vertices[V1]);
                const double Dist2=PlaneDistance(Frustum[FrustumNr], LightSource.Vertices[V2]);
                const double Dist3=PlaneDistance(Frustum[FrustumNr], LightSource.Vertices[V3]);

                if (fabs(Dist1)<0.1 && fabs(Dist2)<0.1 && fabs(Dist3)<0.1) break;
            }
            if (FrustumNr<FrustumSize1stPart) continue; */

            // Eigentlich würde ich hier gerne folgenden Wunsch-Code schreiben:
            //     try
            //     {
            //         Plane3T<double> FrustumPlane(LightSource.Vertices[V2], Hole.Vertices[V1], LightSource.Vertices[V3]);
            //
            //         // ...
            //     }
            //     catch (DivisionByZero) { }  // Nicht mögliche Ebenen einfach ignorieren.
            // Aus irgendeinem Grund ist die Verwendung oder das Fangen der DivisionByZero-Exception aber sehr langsam.
            // Deshalb rolle ich lieber den Plane3T<double>-Konstruktor aus, um ohne dieses Exception-Handling auszukommen.
            // Das Programm wird *deutlich* schneller, ca. Faktor 1,5. Ob das eine Schwäche des Watcom-Compilers ist??
            VectorT Normal(cross(LightSource.Vertices[V3]-LightSource.Vertices[V2], Hole.Vertices[V1]-LightSource.Vertices[V2]));
            double  NLength=length(Normal);

            if (NLength<MapT::RoundEpsilon) continue;
            Normal=scale(Normal, 1.0/NLength);

            Plane3T<double> FrustumPlane(Normal, dot(LightSource.Vertices[V2], Normal));

            // Diese neue FrustumPlane nur dann akzeptieren, wenn die LightSource auf ihrer Rückseite
            // liegt (konstruktionsbedingt sowieso der Fall!) und das Hole auf ihrer Vorderseite liegt.
            // Wenn eine Edge der LightSource in der Ebene des Holes liegt, darf das Hole
            // auch in der FrustumPlane liegen.
            Polygon3T<double>::SideT Side=Hole.WhatSideSimple(FrustumPlane, MapT::RoundEpsilon);

            // Wegen dem Rollentausch ist die Orientierung für den WhatSideSimple() Test falsch, ...
            if (Side==Polygon3T<double>::Front || Side==Polygon3T<double>::InMirrored)
            {
                Frustum.PushBack(FrustumPlane);   // ...im Gesamten aber wieder richtig!
                break;
            }
        }

        V2=V3;
    }
}
Ejemplo n.º 27
0
ArrayT<uint8_t> StateT::GetDeltaMessage(const StateT& Other, bool Compress) const
{
    // Delta-compress the data.
    static ArrayT<uint8_t> DeltaData;

    DeltaData.Overwrite();

    for (unsigned int i = 0; i < m_Data.Size(); i++)
        DeltaData.PushBack(m_Data[i] ^ (i < Other.m_Data.Size() ? Other.m_Data[i] : 0));

    // Optionally RLE-compress the data, then write the delta message.
    ArrayT<uint8_t> DeltaMessage;

    if (Compress)
    {
        DeltaMessage.PushBack(1);
        PackBits(DeltaMessage, &DeltaData[0], DeltaData.Size());

#ifdef DEBUG
        // Make sure that unpacking yields the original data.
        ArrayT<uint8_t> Check;
        UnpackBits(Check, &DeltaMessage[1], DeltaMessage.Size()-1);
        assert(Check == DeltaData);
#endif
    }
    else
    {
        DeltaMessage.PushBack(0);
        DeltaMessage.PushBack(DeltaData);
    }

#if 0
    static std::ofstream Log("compress_log.txt");

    Log << "\n" << DeltaData.Size() << " bytes in original delta message\n";

    {
        uLongf          DestLen=compressBound(DeltaData.Size());
        ArrayT<uint8_t> Dest;

        Dest.PushBackEmptyExact(DestLen);

        const int Result=compress2(&Dest[0], &DestLen, &DeltaData[0], DeltaData.Size(), 9);

        Log << DestLen << " bytes in deflate-compressed message, ";
        Log << "compression result is " << Result << " (" << (Result == Z_OK ? "Z_OK" : "error") << ")\n";
    }

    {
        ArrayT<uint8_t> DestRLE;
        PackBits(DestRLE, &DeltaData[0], DeltaData.Size());

        Log << DestRLE.Size() << " bytes in RLE-compressed message.\n";

        ArrayT<uint8_t> DestRLE_CHECK;
        UnpackBits(DestRLE_CHECK, &DestRLE[0], DestRLE.Size());

        assert(DestRLE_CHECK == DeltaData);
    }
#endif

    return DeltaMessage;
}
Ejemplo n.º 28
0
// Enter the SuperLeaf 'CurrentSL' through the 'EnteringPortal' of the *preceding* SuperLeaf.
// The ancestors of 'CurrentSL' are recorded in 'AncestorSLs'. For the remaining parameters, see the implementation.
// Returns 'true' if visibility from the 'MasterSL' to the 'TargetSL' could be established, false otherwise.
bool DetermineVisibility(unsigned long CurrentSL, const Polygon3T<double>& EnteringPortal, ArrayT<unsigned long> AncestorSLs, const Polygon3T<double>& MasterPortal, const unsigned long TargetSL, BoundingBox3T<double> TargetBB)
{
    // Record that we can see from all ancestors into 'CurrentSL', and vice-versa.
    for (unsigned long AncestorNr=0; AncestorNr<AncestorSLs.Size(); AncestorNr++)
    {
        FlagVisible(AncestorSLs[AncestorNr], CurrentSL);
        FlagVisible(CurrentSL, AncestorSLs[AncestorNr]);
    }

    // If we reached the desired target, return success.
    if (CurrentSL==TargetSL) return true;

    // Clip 'TargetBB' against the plane of the 'EnteringPortal'. This is done to prevent the following kind of problem:
    // Consider a 'TargetSL' that is "thin" and diagonal. As a consequence, it will have a much too large 'TargetBB'.
    // If we then clip the 'TargetBB' against the frustum "MasterPortal --> EnteringPortal" below,
    // we might find that it is still (partially) inside this frustum.
    // However, these parts of the 'TargetBB' inside the frustum might be the much-too-large parts that arose from the special form of the 'TargetSL'.
    // In other words, had we done a better test instead (e.g. the "exact" one, by clipping the actual sub-portals of 'TargetSL' against the frustum),
    // we had found that the frustum actually missed the 'TargetSL' (that is, 'TargetSL' is entirely somewhere outside of the frustum).
    // In the former case, we'll find ourselves trying to find a visibility for something that's long "behind" us, and doing so at *exponential* costs!
    // In the latter case, we can skip all this from here.
    // Besides the "exact" test (which is also quite slow), we can also clip the 'TargetBB' against the plane of the 'EnteringPortal'.
    // The latter approach is conceptually a little different, but achieves the same positive net effect at lesser costs.
    switch (TargetBB.GetEpsilonBox(-MapT::RoundEpsilon).WhatSide(EnteringPortal.Plane))
    {
        case BoundingBox3T<double>::Front:
            return false;

        case BoundingBox3T<double>::Back:
            // Do nothing.
            break;

        case BoundingBox3T<double>::Both:
            // Note the /2 in MapT::RoundEpsilon/2, this should make sure that GetSplits() doesn't reach another conclusion than WhatSide(),
            // i.e. it prevents that GetSplits() cannot find a proper sub-box on *both* sides of the plane.
            TargetBB=TargetBB.GetSplits(EnteringPortal.Plane, MapT::RoundEpsilon/2)[1];
            break;
    }

    // Determine the frustum "MasterPortal --> EnteringPortal".
    ArrayT< Plane3T<double> > Frustum;    // Note that 'Frustum' cannot be declared as 'static', as this function recurses!
    FindFrustum(MasterPortal, EnteringPortal, Frustum);

    if (Frustum.Size()==0)
    {
        // printf("\nWARNING: Invalid frustum 1.\n");   // Should never happen.
        return false;
    }

    // Clip 'TargetBB' against the frustum "MasterPortal --> EnteringPortal".
    unsigned long FrustumNr;

    for (FrustumNr=0; FrustumNr<Frustum.Size(); FrustumNr++)
    {
        switch (TargetBB.GetEpsilonBox(-MapT::RoundEpsilon).WhatSide(Frustum[FrustumNr]))
        {
            case BoundingBox3T<double>::Front:
                // Do nothing.
                break;

            case BoundingBox3T<double>::Back:
                return false;

            case BoundingBox3T<double>::Both:
                // Note the /2 in MapT::RoundEpsilon/2, this should make sure that GetSplits() doesn't reach another conclusion than WhatSide(),
                // i.e. it prevents that GetSplits() cannot find a proper sub-box on *both* sides of the plane.
                TargetBB=TargetBB.GetSplits(Frustum[FrustumNr], MapT::RoundEpsilon/2)[0];
                break;
        }
    }

    // Bevor wir in das nächste SuperLeaf gehen, nimm das gegenwärtige 'CurrentSL' in die Liste der Vorgänger auf.
    AncestorSLs.PushBack(CurrentSL);

    for (unsigned long NeighbourNr=0; NeighbourNr<SuperLeaves[CurrentSL].Neighbours.Size(); NeighbourNr++)
    {
        const unsigned long NextSL    =SuperLeaves[CurrentSL].Neighbours[NeighbourNr].SuperLeafNr;
        Polygon3T<double>   NextPortal=SuperLeaves[CurrentSL].Neighbours[NeighbourNr].SubPortal;

        // Wenn für das 'NextSL' schon festgestellt wurde, daß das 'TargetSL' von dort aus nicht zu sehen ist, können wir direkt weitermachen.
        if (NextSL<AncestorSLs[0]/*MasterSL*/ && !IsVisible(NextSL, TargetSL)) continue;

        // Abkürzung: Gleiche Ebene? Dann weiter mit dem nächsten Portal!
        Polygon3T<double>::SideT Side=NextPortal.WhatSide(EnteringPortal.Plane, MapT::RoundEpsilon);

        if (Side==Polygon3T<double>::InIdentical || Side==Polygon3T<double>::InMirrored) continue;

        // Clippe 'NextPortal' gegen das Frustum "MasterPortal --> EnteringPortal".
        for (FrustumNr=0; FrustumNr<Frustum.Size(); FrustumNr++)
        {
            Polygon3T<double>::SideT Side=NextPortal.WhatSideSimple(Frustum[FrustumNr], MapT::RoundEpsilon);

                 if (Side==Polygon3T<double>::Both ) NextPortal=NextPortal.GetSplits(Frustum[FrustumNr], MapT::RoundEpsilon)[0];
            else if (Side!=Polygon3T<double>::Front) break;
        }
        if (FrustumNr<Frustum.Size()) continue;

        // Bestimme das Frustum "EnteringPortal --> NextPortal".
        static ArrayT< Plane3T<double> > Frustum2;
        FindFrustum(EnteringPortal, NextPortal, Frustum2);

        if (Frustum2.Size()==0)
        {
            // printf("\nWARNING: Invalid frustum 2.\n");   // Should never happen.
            continue;
        }

        // Clippe 'MasterPortal' gegen das Frustum "NextPortal --> EnteringPortal".
        // Um die Portale nicht spiegeln zu müssen, das gespiegelte Frustum benutzen!
        Polygon3T<double> MP=MasterPortal;

        for (FrustumNr=0; FrustumNr<Frustum2.Size(); FrustumNr++)
        {
            Polygon3T<double>::SideT Side=MP.WhatSideSimple(Frustum2[FrustumNr], MapT::RoundEpsilon);

                 if (Side==Polygon3T<double>::Both) MP=MP.GetSplits(Frustum2[FrustumNr], MapT::RoundEpsilon)[1];
            else if (Side!=Polygon3T<double>::Back) break;
        }
        if (FrustumNr<Frustum2.Size()) continue;

        if (DetermineVisibility(NextSL, NextPortal, AncestorSLs, MP, TargetSL, TargetBB)) return true;
    }

    return false;
}
Ejemplo n.º 29
0
// Analytically computes the 'SuperLeavesPVS'.
// A prior call to 'DetermineTrivialVisibility()' is assumed.
void BuildPVS()
{
    /* // Der komplette alte, aber einfache Code. Evtl. nützlich für Debugging-Zwecke.
    for (unsigned long MasterSL=0; MasterSL+1<SuperLeaves.Size(); MasterSL++)
    {
        printf("%5.1f%%\r", (double)MasterSL/SuperLeaves.Size()*100.0);
        fflush(stdout);

        // Bestimme für jedes SuperLeaves-Paar, ob eine gegenseitige Sichtbarkeit besteht.
        // Beachte, daß die folgende Schleife bei 'MasterSL+1' starten kann (statt bei '0'), da Sichtbarkeit immer wechselseitig ist.
        for (unsigned long TargetSL=MasterSL+1; TargetSL<SuperLeaves.Size(); TargetSL++)
        {
            // Wenn die Sichtbarkeit zwischen 'MasterSL' und 'TargetSL' bereits festgestellt wurde,
            // können wir direkt mit dem nächsten 'TargetSL' weitermachen. Sinnvoll für unmittelbare Nachbarn.
            // Auch vorangegangene Iterationen für andere, aber weiter weg liegende 'TargetSL's haben u.U.
            // "auf dem Weg" liegende SuperLeaves als sichtbar markiert, sodaß diese hier nicht nochmal getestet werden müssen.
            if (IsVisible(MasterSL, TargetSL)) continue;

            // Wenn das 'TargetSL' keine Nachbarn hat, können wir direkt mit dem nächsten 'TargetSL' weitermachen.
            // Für ein solches SL ohne Nachbarn haben wir sowieso keine sinnvolle "Target Bounding Box" in den 'SuperLeavesBBs' konstruiert!
            if (SuperLeaves[TargetSL].Neighbours.Size()==0) continue;

            // Intentionally ignore the returned result.
            CanSeeFromAToB(MasterSL, TargetSL);
        }
    } */


    // Für jedes SuperLeaf das PVS bestimmen.
    for (unsigned long MasterSL=0; MasterSL<SuperLeaves.Size(); MasterSL++)
    {
        printf("%5.1f%%\r", (double)MasterSL/SuperLeaves.Size()*100.0);
        fflush(stdout);

        // Initialisiere die Hilfsarrays für das 'MasterSL'.
        ArrayT<bool> AV;    // List of "Already     Visible" SuperLeaves from 'MasterSL'.
        ArrayT<bool> PV;    // List of "Potentially Visible" SuperLeaves from 'MasterSL'.
        ArrayT<bool> NV;    // List of "Not         Visible" SuperLeaves from 'MasterSL'.

        unsigned long SL;

        for (SL=0; SL<SuperLeaves.Size(); SL++)
        {
            AV.PushBack(false);
            PV.PushBack(false);
            NV.PushBack(false);
        }

        // Bestimme, welche SuperLeaves wir von 'MasterSL' aus schon sehen können.
        for (SL=0; SL<SuperLeaves.Size(); SL++)
            if (IsVisible(MasterSL, SL)) AV[SL]=true;

        // Bilde eine Liste 'PV' aller SuperLeaves, die von 'MasterSL' aus "potentially visible" sind.
        // Dies sind zunächst *alle* Nachbarn aller SuperLeaves in der 'AV' Liste, außer denjenigen,
        // die bereits in der 'AV' oder 'NV' Liste sind, oder deren Index-Nummern 'NeighbourSL' kleiner/gleich 'MasterSL' sind.
        // Der Grund für letzteres: Wenn früher schon festgestellt wurde, daß wir nicht von 'NeighbourSL' nach 'MasterSL' sehen können,
        // können wir uns jetzt den Test, ob wir von 'MasterSL' nach 'NeighbourSL' sehen können, sparen.
        // Wäre der frühere Test dagegen positiv gewesen, wäre 'AV[NeighbourSL]==true', was auch keinen 'PV'-Eintrag verursacht.
        for (SL=0; SL<SuperLeaves.Size(); SL++)
            if (AV[SL])
                for (unsigned long NeighbourNr=0; NeighbourNr<SuperLeaves[SL].Neighbours.Size(); NeighbourNr++)
                {
                    const unsigned long NeighbourSL=SuperLeaves[SL].Neighbours[NeighbourNr].SuperLeafNr;

                    if (!AV[NeighbourSL] && !NV[NeighbourSL] && NeighbourSL>MasterSL) PV[NeighbourSL]=true;
                }

        // For each 'TargetSL' in 'PV': Determine if 'TargetSL' is visible from 'MasterSL'.
        while (true)
        {
            unsigned long TargetSL;

            for (TargetSL=0; TargetSL<PV.Size(); TargetSL++) if (PV[TargetSL]) break;
            if (TargetSL>=PV.Size()) break;

            if (CanSeeFromAToB(MasterSL, TargetSL))
            {
                // Redo the init (but 'NV' is possibly not empty anymore).
                for (SL=0; SL<SuperLeaves.Size(); SL++)
                {
                    AV[SL]=false;
                    PV[SL]=false;
                }

                // Bestimme, welche SuperLeaves wir von 'MasterSL' aus schon sehen können.
                for (SL=0; SL<SuperLeaves.Size(); SL++)
                    if (IsVisible(MasterSL, SL)) AV[SL]=true;

                // Bilde eine Liste 'PV' aller SuperLeaves, die von 'MasterSL' aus "potentially visible" sind.
                // Dies sind zunächst *alle* Nachbarn aller SuperLeaves in der 'AV' Liste, außer denjenigen,
                // die bereits in der 'AV' oder 'NV' Liste sind, oder deren Index-Nummern 'NeighbourSL' kleiner/gleich 'MasterSL' sind.
                // Der Grund für letzteres: Wenn früher schon festgestellt wurde, daß wir nicht von 'NeighbourSL' nach 'MasterSL' sehen können,
                // können wir uns jetzt den Test, ob wir von 'MasterSL' nach 'NeighbourSL' sehen können, sparen.
                // Wäre der frühere Test dagegen positiv gewesen, wäre 'AV[NeighbourSL]==true', was auch keinen 'PV'-Eintrag verursacht.
                for (SL=0; SL<SuperLeaves.Size(); SL++)
                    if (AV[SL])
                        for (unsigned long NeighbourNr=0; NeighbourNr<SuperLeaves[SL].Neighbours.Size(); NeighbourNr++)
                        {
                            const unsigned long NeighbourSL=SuperLeaves[SL].Neighbours[NeighbourNr].SuperLeafNr;

                            if (!AV[NeighbourSL] && !NV[NeighbourSL] && NeighbourSL>MasterSL) PV[NeighbourSL]=true;
                        }
            }
            else
            {
                PV[TargetSL]=false;     // Delete 'TargetSL' from PV list.
                NV[TargetSL]=true;      // Add    'TargetSL' to   NV list.
            }
        }
    }

    printf("Final Avg Visibility: %10.5f\n", GetAverageVisibility());
}
Ejemplo n.º 30
0
template<class T> void Brush3T<T>::TraceBoundingBox(const BoundingBox3T<T>& BB, const Vector3T<T>& Origin, const Vector3T<T>& Dir, VB_Trace3T<T>& Trace) const
{
    static ArrayT<T> BloatDistanceOffsets;

    // Bloat-Distance-Offsets für alle Planes dieses Brushs bestimmen.
    while (BloatDistanceOffsets.Size()<Planes.Size()) BloatDistanceOffsets.PushBack(0.0);

    for (unsigned long PlaneNr=0; PlaneNr<Planes.Size(); PlaneNr++)
    {
        const Plane3T<T>& P=Planes[PlaneNr];

        BloatDistanceOffsets[PlaneNr]=dot(P.Normal, Vector3T<T>(P.Normal.x<0 ? BB.Max.x : BB.Min.x,
                                                                P.Normal.y<0 ? BB.Max.y : BB.Min.y,
                                                                P.Normal.z<0 ? BB.Max.z : BB.Min.z));
    }

    // Wenn 'Origin' im Inneren des (soliden) Brushs liegt, sitzen wir fest.
    unsigned long PlaneNr;

    for (PlaneNr=0; PlaneNr<Planes.Size(); PlaneNr++)
        if (Planes[PlaneNr].GetDistance(Origin)+BloatDistanceOffsets[PlaneNr]>=0) break;

    if (PlaneNr==Planes.Size())
    {
        Trace.StartSolid=true;
        Trace.Fraction  =0;
        return;
    }

    for (PlaneNr=0; PlaneNr<Planes.Size(); PlaneNr++)
    {
        // Bestimmen, bei welchem Bruchteil (Fraction F) von Dir wir die Plane schneiden.
        T Nenner=dot(Planes[PlaneNr].Normal, Dir);

        // Dir muß dem Normalenvektor der Ebene wirklich entgegenzeigen! Ist der Nenner==0, so ist Dir parallel zur Plane,
        // und mit dieser Plane ex. kein Schnittpunkt. Ist der Nenner>0, nutzen wir die Konvexität des Brushs aus:
        // Es gibt damit nur genau einen Schnittpunkt mit dem Brush (Eintrittsstelle) und die Plane behindert nicht
        // eine Bewegung von ihr weg (Feststecken wenn Dist==0 (s.u.)).
        if (Nenner>=0) continue;

        T Dist= Planes[PlaneNr].GetDistance(Origin)+BloatDistanceOffsets[PlaneNr];
        T F   =-Dist/Nenner;

        // Der Schnitt macht nur Sinn, wenn F im gewünschten Intervall liegt
        if (F<0 || F>Trace.Fraction) continue;

        // Prüfen, ob Schnittpunkt wirklich auf dem Brush liegt
        Vector3T<T> HitPos=Origin + Dir*F;
        unsigned long PNr;

        for (PNr=0; PNr<Planes.Size(); PNr++)
            if (PNr!=PlaneNr /*Rundungsfehlern zuvorkommen!*/ && Planes[PNr].GetDistance(HitPos)+BloatDistanceOffsets[PNr]>0.01) break;

        if (PNr<Planes.Size()) continue;

        // Wir haben die einzige Eintrittsstelle gefunden!
        // Eigentlich ist das errechete F einwandfrei. Wir wollen es jedoch nochmal etwas verringern, sodaß der sich
        // ergebende Schnittpunkt (HitPos) in einem Abstand von 0.03125 ÜBER der Ebene liegt! Bildhaft wird dazu die
        // Schnittebene um 0.03125 in Richtung ihres Normalenvektors geschoben und F wie oben neu errechnet.
        // Dies erspart uns ansonsten ernste Probleme:
        // - Wird diese Funktion nochmals mit dem Trace-Ergebnis (HitPos) als Origin-Vektor aufgerufen,
        //   kann dieser neue Origin-Vektor nicht wegen Rundungsfehlern in den Brush geraten (Solid!).
        // - Wird unser Trace-Ergebnis (HitPos) als Origin-Vektor dieser Funktion, aber mit einem anderen
        //   Brush benutzt, der gegenüber diesem Brush nur in der Schnittebene verschoben ist (z.B. eine lange
        //   Wand, die aus mehreren "Backsteinen" besteht), kommt es auch hier nicht zum (falschen)
        //   "Hängenbleiben" an einer gemeinsamen Brush-Kante.
        // Aber: Die HitPos kann natürlich trotzdem näher als 0.03125 an der Ebene liegen, nämlich genau dann, wenn
        // es nicht zu einem Schnitt kam und Dir zufällig dort endet. Wir ignorieren diese Möglichkeit: Kommt es doch
        // noch zu einem Schnitt, wird eben F==0. Deshalb können wir uns auch in diesem Fall nicht durch Rundungsfehler
        // ins Innere des Brushs schaukeln.
        F=-(Dist-(T)0.03125)/Nenner;                   // Vgl. Berechnung von F oben!

        if (F<0             ) F=0;
        if (F>Trace.Fraction) F=Trace.Fraction;     // pro forma

        Trace.Fraction    =F;
        Trace.ImpactNormal=Planes[PlaneNr].Normal;
        break;
    }
}