Files
ThirdParty-6/ParaView-5.0.1/Web/Core/vtkPVWebApplication.cxx

413 lines
13 KiB
C++

/*=========================================================================
Program: ParaView
Module: $RCSfile$
Copyright (c) Kitware, Inc.
All rights reserved.
See Copyright.txt or http://www.paraview.org/HTML/Copyright.html for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkPVWebApplication.h"
#include "vtkBase64Utilities.h"
#include "vtkCamera.h"
#include "vtkCommand.h"
#include "vtkDataEncoder.h"
#include "vtkImageData.h"
#include "vtkJPEGWriter.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
#include "vtkPNGWriter.h"
#include "vtkPointData.h"
#include "vtkPVRenderView.h"
#include "vtkRendererCollection.h"
#include "vtkRenderWindow.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkSmartPointer.h"
#include "vtkSMContextViewProxy.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMRenderViewProxy.h"
#include "vtkSMViewProxy.h"
#include "vtkTimerLog.h"
#include "vtkUnsignedCharArray.h"
#include "vtkWebGLExporter.h"
#include "vtkWebGLObject.h"
#include "vtkWebInteractionEvent.h"
#include <assert.h>
#include <cmath>
#include <map>
class vtkPVWebApplication::vtkInternals
{
public:
struct ImageCacheValueType
{
public:
vtkSmartPointer<vtkUnsignedCharArray> Data;
bool NeedsRender;
bool HasImagesBeingProcessed;
vtkObject* ViewPointer;
unsigned long ObserverId;
ImageCacheValueType() : NeedsRender(true), HasImagesBeingProcessed(false), ViewPointer(NULL), ObserverId(0) { }
void SetListener(vtkObject* view)
{
if(this->ViewPointer == view)
{
return;
}
if(this->ViewPointer && this->ObserverId)
{
this->ViewPointer->RemoveObserver(this->ObserverId);
this->ObserverId = 0;
}
this->ViewPointer = view;
if(this->ViewPointer)
{
this->ObserverId = this->ViewPointer->AddObserver(vtkCommand::AnyEvent, this, &ImageCacheValueType::ViewEventListener);
}
}
void RemoveListener(vtkObject* view)
{
if(this->ViewPointer && this->ViewPointer == view && this->ObserverId)
{
this->ViewPointer->RemoveObserver(this->ObserverId);
this->ObserverId = 0;
this->ViewPointer = NULL;
}
}
void ViewEventListener(vtkObject*, unsigned long, void*)
{
this->NeedsRender = true;
}
};
typedef std::map<void*, ImageCacheValueType> ImageCacheType;
ImageCacheType ImageCache;
typedef std::map<void*, unsigned int > ButtonStatesType;
ButtonStatesType ButtonStates;
vtkNew<vtkDataEncoder> Encoder;
// WebGL related struct
struct WebGLObjCacheValue
{
public:
int ObjIndex;
std::map<int, std::string> BinaryParts;
};
// map for <vtkWebGLExporter, <webgl-objID, WebGLObjCacheValue> >
typedef std::map<std::string, WebGLObjCacheValue> WebGLObjId2IndexMap;
std::map<vtkWebGLExporter*, WebGLObjId2IndexMap> WebGLExporterObjIdMap;
// map for <vtkSMViewProxy, vtkWebGLExporter>
std::map<vtkSMViewProxy*, vtkSmartPointer<vtkWebGLExporter> > ViewWebGLMap;
std::string LastAllWebGLBinaryObjects;
};
vtkStandardNewMacro(vtkPVWebApplication);
//----------------------------------------------------------------------------
vtkPVWebApplication::vtkPVWebApplication():
ImageEncoding(ENCODING_BASE64),
ImageCompression(COMPRESSION_JPEG),
Internals(new vtkPVWebApplication::vtkInternals())
{
}
//----------------------------------------------------------------------------
vtkPVWebApplication::~vtkPVWebApplication()
{
delete this->Internals;
this->Internals = NULL;
}
//----------------------------------------------------------------------------
bool vtkPVWebApplication::GetHasImagesBeingProcessed(vtkSMViewProxy* view)
{
const vtkInternals::ImageCacheValueType& value = this->Internals->ImageCache[view];
return value.HasImagesBeingProcessed;
}
//----------------------------------------------------------------------------
vtkUnsignedCharArray* vtkPVWebApplication::InteractiveRender(vtkSMViewProxy* view, int quality)
{
// for now, just do the same as StillRender().
return this->StillRender(view, quality);
}
//----------------------------------------------------------------------------
void vtkPVWebApplication::InvalidateCache(vtkSMViewProxy* view)
{
this->Internals->ImageCache[view].NeedsRender = true;
}
//----------------------------------------------------------------------------
vtkUnsignedCharArray* vtkPVWebApplication::StillRender(vtkSMViewProxy* view, int quality)
{
if (!view)
{
vtkErrorMacro("No view specified.");
return NULL;
}
vtkInternals::ImageCacheValueType& value = this->Internals->ImageCache[view];
value.SetListener(view);
if (value.NeedsRender == false &&
value.Data != NULL &&
view->GetNeedsUpdate() == false)
{
//cout << "Reusing cache" << endl;
bool latest = this->Internals->Encoder->GetLatestOutput(view->GetGlobalID(), value.Data);
value.HasImagesBeingProcessed = !latest;
return value.Data;
}
//cout << "Regenerating " << endl;
//vtkTimerLog::ResetLog();
//vtkTimerLog::CleanupLog();
//vtkTimerLog::MarkStartEvent("StillRenderToString");
//vtkTimerLog::MarkStartEvent("CaptureWindow");
// TODO: We should add logic to check if a new rendering needs to be done and
// then alone do a new rendering otherwise use the cached image.
vtkImageData* image = view->CaptureWindow(1);
image->GetDimensions(this->LastStillRenderImageSize);
//vtkTimerLog::MarkEndEvent("CaptureWindow");
//vtkTimerLog::MarkEndEvent("StillRenderToString");
//vtkTimerLog::DumpLogWithIndents(&cout, 0.0);
this->Internals->Encoder->PushAndTakeReference(view->GetGlobalID(), image, quality);
assert(image == NULL);
if (value.Data == NULL)
{
// we need to wait till output is processed.
//cout << "Flushing" << endl;
this->Internals->Encoder->Flush(view->GetGlobalID());
//cout << "Done Flushing" << endl;
}
bool latest = this->Internals->Encoder->GetLatestOutput(view->GetGlobalID(), value.Data);
value.HasImagesBeingProcessed = !latest;
value.NeedsRender = false;
return value.Data;
}
//----------------------------------------------------------------------------
const char* vtkPVWebApplication::StillRenderToString(vtkSMViewProxy* view, unsigned long time, int quality)
{
vtkUnsignedCharArray* array = this->StillRender(view, quality);
if (array && array->GetMTime() != time)
{
this->LastStillRenderToStringMTime = array->GetMTime();
//cout << "Image size: " << array->GetNumberOfTuples() << endl;
return reinterpret_cast<char*>(array->GetPointer(0));
}
return NULL;
}
//----------------------------------------------------------------------------
bool vtkPVWebApplication::HandleInteractionEvent(
vtkSMViewProxy* view, vtkWebInteractionEvent* event)
{
vtkRenderWindowInteractor *iren = NULL;
if (view->GetRenderWindow())
{
iren = view->GetRenderWindow()->GetInteractor();
}
else
{
vtkErrorMacro("Interaction not supported for view : " << view);
return false;
}
int viewSize[2];
vtkSMPropertyHelper(view, "ViewSize").Get(viewSize, 2);
int posX = std::floor(viewSize[0] * event->GetX() + 0.5);
int posY = std::floor(viewSize[1] * event->GetY() + 0.5);
int ctrlKey =
(event->GetModifiers() & vtkWebInteractionEvent::CTRL_KEY) != 0? 1: 0;
int shiftKey =
(event->GetModifiers() & vtkWebInteractionEvent::SHIFT_KEY) != 0? 1: 0;
iren->SetEventInformation(posX, posY, ctrlKey, shiftKey, event->GetKeyCode());
unsigned int prev_buttons = this->Internals->ButtonStates[view];
unsigned int changed_buttons = (event->GetButtons() ^ prev_buttons);
iren->MouseMoveEvent();
if ( (changed_buttons & vtkWebInteractionEvent::LEFT_BUTTON) != 0 )
{
if ( (event->GetButtons() & vtkWebInteractionEvent::LEFT_BUTTON) != 0)
{
iren->LeftButtonPressEvent();
}
else
{
iren->LeftButtonReleaseEvent();
}
}
if ( (changed_buttons & vtkWebInteractionEvent::RIGHT_BUTTON) != 0 )
{
if ( (event->GetButtons() & vtkWebInteractionEvent::RIGHT_BUTTON) != 0)
{
iren->RightButtonPressEvent();
}
else
{
iren->RightButtonReleaseEvent();
}
}
if ( (changed_buttons & vtkWebInteractionEvent::MIDDLE_BUTTON) != 0 )
{
if ( (event->GetButtons() & vtkWebInteractionEvent::MIDDLE_BUTTON) != 0)
{
iren->MiddleButtonPressEvent();
}
else
{
iren->MiddleButtonReleaseEvent();
}
}
this->Internals->ButtonStates[view] = event->GetButtons();
bool needs_render = (changed_buttons != 0 || event->GetButtons());
this->Internals->ImageCache[view].NeedsRender = needs_render;
return needs_render;
}
// ---------------------------------------------------------------------------
const char* vtkPVWebApplication::GetWebGLSceneMetaData(vtkSMViewProxy* view)
{
if (!view)
{
vtkErrorMacro("No view specified.");
return NULL;
}
vtkRenderWindow* renWin = view->GetRenderWindow();
vtkPVRenderView* pvRenderView =
vtkPVRenderView::SafeDownCast(view->GetClientSideView());
if(renWin == NULL || pvRenderView == NULL)
{
vtkErrorMacro("The view is supported for WebGL export: " << view);
return NULL;
}
// We use the camera focal point to be the center of rotation
double centerOfRotation[3];
vtkCamera *cam = pvRenderView->GetActiveCamera();
cam->GetFocalPoint(centerOfRotation);
if(this->Internals->ViewWebGLMap.find(view) ==
this->Internals->ViewWebGLMap.end())
{
this->Internals->ViewWebGLMap[view] =
vtkSmartPointer<vtkWebGLExporter>::New();
}
vtkWebGLExporter* webglExporter = this->Internals->ViewWebGLMap[view];
webglExporter->parseScene(
renWin->GetRenderers(), view->GetGlobalIDAsString(),VTK_PARSEALL);
vtkInternals::WebGLObjId2IndexMap webglMap;
for(int i=0; i<webglExporter->GetNumberOfObjects(); ++i)
{
vtkWebGLObject* wObj = webglExporter->GetWebGLObject(i);
if(wObj && wObj->isVisible())
{
vtkInternals::WebGLObjCacheValue val;
val.ObjIndex = i;
for(int j=0; j<wObj->GetNumberOfParts(); ++j)
{
val.BinaryParts[j] = "";
}
webglMap[wObj->GetId()] = val;
}
}
this->Internals->WebGLExporterObjIdMap[webglExporter] = webglMap;
webglExporter->SetCenterOfRotation(
static_cast<float>(centerOfRotation[0]),
static_cast<float>(centerOfRotation[1]),
static_cast<float>(centerOfRotation[2]));
return webglExporter->GenerateMetadata();
}
//----------------------------------------------------------------------------
const char* vtkPVWebApplication::GetWebGLBinaryData(
vtkSMViewProxy* view, const char* id, int part)
{
if (!view)
{
vtkErrorMacro("No view specified.");
return NULL;
}
if(this->Internals->ViewWebGLMap.find(view) ==
this->Internals->ViewWebGLMap.end())
{
if(this->GetWebGLSceneMetaData(view) == NULL)
{
vtkErrorMacro("Failed to generate WebGL MetaData for: " << view);
return NULL;
}
}
vtkWebGLExporter* webglExporter = this->Internals->ViewWebGLMap[view];
if(webglExporter == NULL)
{
vtkErrorMacro("There is no cached WebGL Exporter for: " << view);
return NULL;
}
if(this->Internals->WebGLExporterObjIdMap[webglExporter].size() > 0 &&
this->Internals->WebGLExporterObjIdMap[webglExporter].find(id) !=
this->Internals->WebGLExporterObjIdMap[webglExporter].end())
{
vtkInternals::WebGLObjCacheValue* cachedVal =
&(this->Internals->WebGLExporterObjIdMap[webglExporter][id]);
if(cachedVal->BinaryParts.find(part) != cachedVal->BinaryParts.end())
{
if(cachedVal->BinaryParts[part].empty())
{
vtkWebGLObject* obj = webglExporter->GetWebGLObject(cachedVal->ObjIndex);
if(obj && obj->isVisible())
{
// Manage Base64
vtkNew<vtkBase64Utilities> base64;
unsigned char* output = new unsigned char[obj->GetBinarySize(part)*2];
int size = base64->Encode(
obj->GetBinaryData(part), obj->GetBinarySize(part), output, false);
cachedVal->BinaryParts[part] = std::string((const char *)output, size);
delete[] output;
}
}
return cachedVal->BinaryParts[part].c_str();
}
}
return NULL;
}
//----------------------------------------------------------------------------
void vtkPVWebApplication::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "ImageEncoding: " << this->ImageEncoding << endl;
os << indent << "ImageCompression: " << this->ImageCompression << endl;
}