mirror of
https://github.com/OpenFOAM/ThirdParty-6.git
synced 2025-12-08 06:57:43 +00:00
413 lines
13 KiB
C++
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;
|
|
}
|