source: projectionDesigner/trunk/projdesigner/src/ProjectionModel.cpp @ 161

Last change on this file since 161 was 4, checked in by Torben Dannhauer, 15 years ago
File size: 16.6 KB
Line 
1#include <QImage>
2#include <QPainter>
3#include <QGLPixelBuffer>
4
5#include "Scene.h"
6#include "Screen.h"
7#include "Channel.h"
8#include "RSync.h"
9#include "Exporter.h"
10#include "GUIControler.h"
11
12#include "ProjectionModel.h"
13
14using namespace projection;
15
16QDir ProjectionModel::pluginsDir;
17QStringList ProjectionModel::pluginFileNames;
18QList<ProjectorInterface*> ProjectionModel::projector_interfaces;
19QList<SceneInterface*> ProjectionModel::scene_interfaces;
20
21Q_IMPORT_PLUGIN(projdesigner_default)
22
23/**
24 * Constructor.
25 */
26ProjectionModel::ProjectionModel() : QObject()
27{
28                loadPlugins();
29
30    m_pScene = new Scene(this);
31    m_pScreen = new Screen(this);
32
33    m_pSelectedChannel = NULL;
34
35    m_projectorAreaTexIndex = 0;
36    m_viewAreaTexIndex = 0;
37
38    m_designMode = DESIGN_MODE_DISTORTIONMAP;
39
40    m_bBlockUpdate = false;
41
42    m_pRSync = new RSync(this);
43    m_pExporter = new Exporter(this);
44    m_pGUI = new GUIControler(this);
45
46    m_pGLWidget = NULL;
47    m_pPBuffer = NULL;
48
49#ifdef _DEBUG
50    m_updateViewsCount = 0;
51    m_updateOffscreenCount = 0;
52#endif // _DEBUG
53}
54
55/**
56 * Destructor.
57 */
58ProjectionModel::~ProjectionModel()
59{
60    if (glIsTexture(m_projectorAreaTexIndex))
61        glDeleteTextures(1, &m_projectorAreaTexIndex);
62    if (m_pRSync->isServer())
63    {
64        while (getNumChannels() > 0)
65            removeChannel(0);
66    }
67    delete m_pScreen;
68    delete m_pScene;
69
70    delete m_pRSync;
71    delete m_pExporter;
72    delete m_pGUI;
73}
74
75/**
76 * Initialize as a server.
77 *
78 * @param portNo Port number.
79 */
80void ProjectionModel::initServer(int portNo)
81{
82    m_pRSync->initServer(portNo);
83    m_pGUI->init(true);
84}
85
86/**
87 * Initialize as a client.
88 *
89 * @param address Client address.
90 * @param portNo Port number.
91 */
92void ProjectionModel::initClient(const QString& address, int portNo)
93{
94    m_pRSync->initClient(address, portNo);
95    m_pGUI->init(false);
96}
97
98/**
99 * Add a new channel.
100 *
101 * @return New channel.
102 */
103Channel* ProjectionModel::addChannel()
104{
105    Channel* pChannel = new Channel(this);
106    if (m_pSelectedChannel)
107        *pChannel = *m_pSelectedChannel;
108    m_pChannels.push_back(pChannel);
109
110    return pChannel;
111}
112
113/**
114 * Retrieve the number of the channels.
115 *
116 * @return The number of the channels.
117 */
118unsigned int ProjectionModel::getNumChannels() const
119{
120    return m_pChannels.size();
121}
122
123/**
124 * Retrieve a channel object
125 *
126 * @param index Index of channel to retrieve.
127 * @return Channel object.
128 */
129Channel* ProjectionModel::getChannel(int index) const
130{
131    if (index >= 0 && index < m_pChannels.size())
132        return m_pChannels[index];
133    return NULL;
134
135}
136
137/**
138 * Retrieve Index of the specified channel.
139 *
140 * @param pChannel Channel object.
141 * @return Index of the specified channel.
142 */
143int ProjectionModel::getChannelIndex(Channel* pChannel) const
144{
145    for (int i=0; i<m_pChannels.size(); ++i)
146        if (m_pChannels[i] == pChannel)
147            return i;
148    return -1;
149}
150
151/**
152 * Remove the specified channel.
153 *
154 * @param index Index of the channel to remove.
155 */
156void ProjectionModel::removeChannel(int index)
157{
158    if (index < 0)
159        return;
160
161    Channel* pChannel = m_pChannels.takeAt(index);
162    if (m_pSelectedChannel == pChannel) {
163        m_pGUI->selectChannel(NULL);
164        m_pSelectedChannel = NULL;
165    }
166    if (pChannel) {
167        m_pGUI->removeChannel(pChannel);
168        delete pChannel;
169    }
170
171    updateViews();
172}
173
174/**
175 * Remove all channels.
176 */
177void ProjectionModel::removeAllChannels()
178{
179    m_pSelectedChannel = NULL;
180    m_pGUI->selectChannel(NULL);
181
182    while (getNumChannels() > 0)
183        removeChannel(0);
184
185    m_pGUI->updateGUI();
186}
187
188/**
189 * Select a channel.
190 *
191 * @param pChannel Channel object to select.
192 */
193void ProjectionModel::selectChannel(Channel* pChannel)
194{
195    if (m_pSelectedChannel != pChannel)
196    {
197        m_pSelectedChannel = pChannel;
198        m_pGUI->selectChannel(pChannel);
199        updateViews();
200    }
201}
202
203/**
204 * Retrieve the selected channel object.
205 *
206 * @return Selected channel object.
207 */
208Channel* ProjectionModel::getSelectedChannel() const
209{
210    return m_pSelectedChannel;
211}
212
213/**
214 * Select the previous channel.
215 */
216void ProjectionModel::selectPreviousChannel()
217{
218    if (m_pChannels.size() == 0) return;
219
220    if (m_pSelectedChannel)
221        selectChannel(getChannel((getChannelIndex(m_pSelectedChannel)-1)%getNumChannels()));
222    else
223        selectChannel(getChannel(getNumChannels()-1));
224}
225
226/**
227 * Select the next channel.
228 */
229void ProjectionModel::selectNextChannel()
230{
231    if (m_pChannels.size() == 0) return;
232
233    if (m_pSelectedChannel)
234        selectChannel(getChannel((getChannelIndex(m_pSelectedChannel)+1)%getNumChannels()));
235    else
236        selectChannel(getChannel(0));
237}
238
239/**
240 * Check whether any channels with the specified name is existing or not.
241 *
242 * @return True if a channel with the specified name is existing.
243 */
244bool ProjectionModel::hasChannel(const QString& name) const
245{
246    for (int i=0; i<m_pChannels.size(); ++i)
247        if (m_pChannels.at(i)->getName() == name)
248            return true;
249    return false;
250}
251
252/**
253 * Create a unique name for specified channel.
254 *
255 * @param name Candidate name of channel. It may not be a unique name.
256 * @param pChannel Channel object to give a name.
257 * @return Unique name for the specified channel.
258 */
259QString ProjectionModel::getUniqueName(const QString& name, Channel* pChannel) const
260{
261    bool bUnique = true;
262    int i;
263    for (i=0; i<m_pChannels.size(); ++i)
264        if (m_pChannels[i] != pChannel && m_pChannels[i]->getName() == name)
265            bUnique = false;
266    if (!bUnique) {
267        int count = 0;
268        QString uniqueName;
269        while (true) {
270            uniqueName = name + QString("_%1").arg(count);
271            bUnique = true;
272            for (i=0; i<m_pChannels.size(); ++i)
273                if (m_pChannels[i]->getName() == uniqueName)
274                    bUnique = false;
275            if (bUnique) break;
276            count++;
277        }
278        return uniqueName;
279    }
280
281    return name;
282}
283
284/**
285 * Create a projection area texture and retrieve its texture object index.
286 *
287 * @return Texture object index of the projection area texture.
288 */
289GLuint ProjectionModel::getProjectorAreaTexture()
290{
291    if (m_projectorAreaTexIndex == 0)
292    {
293        QPixmap pixmap(256, 256);
294        pixmap.fill(QColor(0, 0, 64));
295        QPainter painter(&pixmap);
296        painter.setPen(QColor(255, 255, 255));
297        painter.drawRect(0, 0, 255, 255);
298        m_projectorAreaTexIndex = getGUI()->getGLWidget()->bindTexture(pixmap);
299    }
300    return m_projectorAreaTexIndex;
301}
302
303/**
304 * Create a view area texture and retrieve its texture object index.
305 *
306 * @return Texture object index of the view area texture.
307 */
308GLuint ProjectionModel::getViewAreaTexture()
309{
310    if (m_viewAreaTexIndex == 0)
311    {
312        QPixmap pixmap(256, 256);
313        pixmap.fill(QColor(64, 64, 0));
314        QPainter painter(&pixmap);
315        painter.setPen(QColor(255, 255, 255));
316        painter.drawRect(0, 0, 255, 255);
317        m_viewAreaTexIndex = getGUI()->getGLWidget()->bindTexture(pixmap);
318    }
319    return m_viewAreaTexIndex;
320}
321
322/**
323 * Set design mode.
324 *
325 * @param designMode Design mode.
326 */
327void ProjectionModel::setDesignMode(DESIGN_MODE designMode)
328{
329    if (m_designMode != designMode)
330    {
331        m_designMode = designMode;
332        updateViews();
333
334        QDomDocument doc;
335        doc.appendChild(domElement("Model", doc));
336        getRSync()->sendChanges(RSYNC_COMMAND_MODEL, doc.toString(0));
337    }
338}
339
340/**
341 * Retrieve the current design mode.
342 *
343 * @param designMode Design mode.
344 */
345DESIGN_MODE ProjectionModel::getDesignMode() const
346{
347    return m_designMode;
348}
349
350/**
351 * Block or unblock to update views.
352 *
353 * @param bBlock True to block updating views.
354 */
355void ProjectionModel::setBlockUpdate(bool bBlock)
356{
357    m_bBlockUpdate = bBlock;
358}
359
360/**
361    * Check whether blocking or not update views.
362    *
363    * @return True if blocking to update views.
364    */
365bool ProjectionModel::getBlockUpdate() const
366{
367    return m_bBlockUpdate;
368}
369
370/**
371 * Redraw all views (DesignViewWindow and ProjectorWindows).
372 */
373void ProjectionModel::updateViews()
374{
375#ifdef _DEBUG
376    m_updateViewsCount++;
377#endif // _DEBUG
378
379    if (!m_bBlockUpdate)
380        m_pGUI->updateViews();
381}
382
383/**
384 * Redraw off-screen buffer of the specified channel, then redraw all views.
385 */
386void ProjectionModel::updateViewAndOffscreens()
387{
388#ifdef _DEBUG
389    m_updateOffscreenCount++;
390#endif // _DEBUG
391
392    for (int i=0; i<m_pChannels.size(); ++i)
393        m_pChannels[i]->renderSceneToViewBuffer();
394
395    if (!m_bBlockUpdate)
396        m_pGUI->updateViews();
397}
398
399/**
400 * Load projection settings from a file.
401 *
402 * @param fileName File name to load.
403 * @return True if successfully loaded.
404 */
405bool ProjectionModel::loadFile(const QString& fileName)
406{
407    QFile file(fileName);
408    if (!file.open(QIODevice::ReadOnly))
409        return false;
410
411    QDomDocument doc;
412    doc.setContent(&file);
413    file.close();
414
415    if(!restoreSettings(doc))
416                return false;
417
418    return true;
419}
420
421/**
422 * Save the current projection settings to a file.
423 *
424 * @param fileName File name to save.
425 * @return True if successfully saved.
426 */
427bool ProjectionModel::saveFile(const QString& fileName)
428{
429    QFile file(fileName);
430    if (file.open(QIODevice::WriteOnly|QIODevice::Truncate))
431    {
432        QTextStream out(&file);
433        QDomDocument doc;
434
435        storeSettings(doc);
436
437        doc.save(out, 4);
438        file.flush();
439        file.close();
440
441        return true;
442    }
443    return false;
444}
445
446/**
447 * Restart from a new file.
448 *
449 * @return True if everything is ok.
450 */
451bool ProjectionModel::newFile()
452{
453    QDomDocument doc;
454    doc.setContent(QString(""));
455    restoreSettings(doc);
456                return true;
457}
458
459/**
460 * Restore the model from XML data.
461 *
462 * @param element Parent XML element of the model data.
463 */
464bool ProjectionModel::initFromDOMElement(const QDomElement& element)
465{
466        if (!element.isNull())
467        {
468                if (element.attribute("designMode") == "distortionMap")
469            setDesignMode(DESIGN_MODE_DISTORTIONMAP);
470        else
471            setDesignMode(DESIGN_MODE_BLENDMAP);
472        }
473
474        return true;    // Todo: Secure this funtion and return only true if no critical error occurs
475}
476
477/**
478 * Store the current model as XML data.
479 *
480 * @param name XML node name of the data.
481 * @param doc XML document to store the data.
482 * @return Current screen data as XML data.
483 */
484QDomElement ProjectionModel::domElement(const QString& name, QDomDocument& doc) const
485{
486        QDomElement de = doc.createElement(name);
487    de.setAttribute("designMode", m_designMode==DESIGN_MODE_DISTORTIONMAP?"distortionMap":"blendMap");
488
489    return de;
490}
491
492/**
493 * Restore projection settings from xml data.
494 *
495 * @param doc XML document to restore the projection settings from.
496 */
497bool ProjectionModel::restoreSettings(QDomDocument& doc)
498{
499        // separate from environment (cocoon)
500    blockSignals(true);
501    m_pRSync->setBlockSync(true);
502
503        // Deleting old Channels
504    while (getNumChannels() > 0)
505        removeChannel(0);
506
507        // ??
508    QDomElement main;
509    main = doc.documentElement();
510
511        // Setting data version
512        QString version = main.attribute("version");
513        if (version.isNull()) version="1.0.0";
514
515        // Initialising channels
516        QDomElement channel = main.firstChildElement("Channel");
517    while (!channel.isNull()) {
518        Channel* pChannel = addChannel();
519        if( !pChannel->initFromDOMElement(channel, version) )
520                        return false;
521        channel = channel.nextSiblingElement("Channel");
522    }
523
524        // Now initializing other parts (its important that the channels are initialized first, because then sync to remote channels is possible.)
525    if( !initFromDOMElement(main.firstChildElement("Model")) )
526                return false;
527    if( !m_pScene->initFromDOMElement(main.firstChildElement("Scene")) )
528                return false;
529    if( !m_pScreen->initFromDOMElement(main.firstChildElement("Screen")) )
530                return false;
531        if( !m_pExporter->initFromDOMElement(main.firstChildElement("Exporter")) )
532                return false;
533
534
535    if (m_pRSync->isServer())
536    {
537        if (!main.firstChildElement("GUI").isNull())
538                        if( !m_pGUI->initFromDOMElement(main.firstChildElement("GUI")) )
539                                return false;
540                m_pGUI->updateGUI();
541    }
542   
543        // Reopen to environment :)
544    m_pRSync->setBlockSync(false);
545
546    m_pRSync->sendReloadSettings();     // <== Function secured: is only performed when server
547
548        // Reopen to environment :)
549    blockSignals(false);
550
551    selectNextChannel();
552
553    updateViewAndOffscreens();
554
555        return true;
556}
557
558/**
559 * Store projection settings to xml data.
560 *
561 * @param doc XML document to store the projection settings.
562 */
563void ProjectionModel::storeSettings(QDomDocument& doc)
564{
565    QDomElement main = doc.createElement("Distortion");
566
567        main.setAttribute("version", APPLICATION_VERSION);
568
569    main.appendChild(domElement("Model", doc));
570    main.appendChild(m_pExporter->domElement("Exporter", doc));
571    main.appendChild(m_pScene->domElement("Scene", doc));
572    main.appendChild(m_pScreen->domElement("Screen", doc));
573    main.appendChild(m_pExporter->domElement("Exporter", doc));
574    for (int i=0; i<m_pChannels.size(); ++i)
575        main.appendChild(m_pChannels[i]->domElement("Channel", doc));
576
577    main.appendChild(m_pGUI->domElement("GUI", doc));
578
579    doc.appendChild(main);
580}
581
582/**
583 * Retrieve the scene object.
584 *
585 * @return Scene object.
586 */
587Scene* ProjectionModel::getScene() const
588{
589    return m_pScene;
590}
591
592/**
593 * Retrieve the screen object.
594 *
595 * @return Screen object.
596 */
597Screen* ProjectionModel::getScreen() const
598{
599    return m_pScreen;
600}
601
602/**
603 * Retrieve the remote state synchronize object.
604 *
605 * @return Remote state synchronize object.
606 */
607RSync* ProjectionModel::getRSync() const
608{
609    return m_pRSync;
610}
611
612/**
613 * Retrieve the exporter object.
614 *
615 * @return Exporter object.
616 */
617Exporter* ProjectionModel::getExporter() const
618{
619    return m_pExporter;
620}
621
622/**
623 * Retrieve the GUI controler object.
624 *
625 * @return GUI controler object.
626 */
627GUIControler* ProjectionModel::getGUI() const
628{
629    return m_pGUI;
630}
631
632/**
633 * Loads the plugins
634 */
635void ProjectionModel::loadPlugins()
636{
637    foreach (QObject *plugin, QPluginLoader::staticInstances())
638        {
639            ProjectorInterface *projector_interface = qobject_cast<ProjectorInterface*>(plugin);
640            if (projector_interface)
641            {
642                projector_interfaces += projector_interface;
643            }
644            SceneInterface *scene_interface = qobject_cast<SceneInterface*>(plugin);
645            if (scene_interface)
646            {
647                scene_interfaces += scene_interface;
648            }
649        }
650
651    pluginsDir = QDir(qApp->applicationDirPath());
652
653    // The following ifdef/endif block is from Qt Examples
654#if defined(Q_OS_WIN)
655//    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
656//        pluginsDir.cdUp();
657#elif defined(Q_OS_MAC)
658    if (pluginsDir.dirName() == "MacOS") {
659        pluginsDir.cdUp();
660        pluginsDir.cdUp();
661        pluginsDir.cdUp();
662    }
663#endif
664    if (!pluginsDir.cd("../lib")) return;
665
666    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) {
667        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
668        QObject *plugin = loader.instance();
669        if (plugin) {
670            pluginFileNames += fileName;
671
672            ProjectorInterface *projector_interface = qobject_cast<ProjectorInterface*>(plugin);
673            if (projector_interface)
674            {
675                projector_interfaces += projector_interface;
676            }
677            SceneInterface *scene_interface = qobject_cast<SceneInterface*>(plugin);
678            if (scene_interface)
679            {
680                scene_interfaces += scene_interface;
681            }
682        }
683        else
684        {
685            QString errorString = loader.errorString();
686            QString message;
687            if (errorString=="Unknown error")
688            {
689                message = trUtf8("<p><b>%1</b>: Le plugin a été rejeté<br><i><u>Raison invoquée par le loader</u>: %2</i><br>ATTENTION: Cela peut venir d'une configuration en debug du plugin dynamique.<p><b>OK</b> lance quand même l'application,<br><b>Cancel</b> annule le lancement.").arg(fileName).arg(errorString);
690            }
691            else
692            {
693                message = trUtf8("<p><b>%1</b>: Le plugin a été rejeté<br><i><u>Raison invoquée par le loader</u>: %2</i><p><b>OK</b> lance quand même l'application,<br><b>Cancel</b> annule le lancement.").arg(fileName).arg(errorString);
694            }
695            if (QMessageBox::Cancel==QMessageBox::warning(NULL, trUtf8("Attention"), message, QMessageBox::Ok|QMessageBox::Cancel))
696                exit(-1);
697        }
698    }
699}
700
701/**
702 * Set a GLWidget for the current render context.
703 *
704 * @parma pGLWidget GLWidget of the current render context.
705 */
706void ProjectionModel::setRenderContext(QGLWidget* pGLWidget)
707{
708    m_pGLWidget = pGLWidget;
709    m_pPBuffer = NULL;
710}
711
712/**
713 * Set a P-Buffer for the current render context.
714 *
715 * @parma pPBuffer P-Buffer of the current render context.
716 */
717void ProjectionModel::setRenderContext(QGLPixelBuffer* pPBuffer)
718{
719    m_pGLWidget = NULL;
720    m_pPBuffer = pPBuffer;
721}
722
723/**
724 * Activate the current render context.
725 */
726void ProjectionModel::activateRenderContext()
727{
728    if (m_pGLWidget)
729        m_pGLWidget->makeCurrent();
730    if (m_pPBuffer)
731        m_pPBuffer->makeCurrent();
732}
Note: See TracBrowser for help on using the repository browser.