scene.C 10.8 KB
Newer Older
1
2
3
4
5
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
    \\  /    A nd           | Copyright (C) 2015 OpenFOAM Foundation
6
     \\/     M anipulation  | Copyright (C) 2015-2016 OpenCFD Ltd.
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    for more details.

    You should have received a copy of the GNU General Public License
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.

\*---------------------------------------------------------------------------*/

// OpenFOAM includes
#include "scene.H"
#include "Constant.H"

// VTK includes
#include "vtkCamera.h"
#include "vtkCubeSource.h"
#include "vtkLightKit.h"
#include "vtkPolyDataMapper.h"
#include "vtkPNGWriter.h"
#include "vtkRenderer.h"
#include "vtkRendererCollection.h"
#include "vtkRenderWindow.h"
#include "vtkWindowToImageFilter.h"


// * * * * * * * * * * * * * Private Member Functions  * * * * * * * * * * * //

44
45
46
47
void Foam::functionObjects::runTimePostPro::scene::readCamera
(
    const dictionary& dict
)
48
49
50
51
52
{
    if (dict.readIfPresent("nFrameTotal", nFrameTotal_))
    {
        if (nFrameTotal_ < 1)
        {
53
54
            FatalIOErrorInFunction(dict)
                << "nFrameTotal must be 1 or greater"
55
56
57
58
                << exit(FatalIOError);
        }
    }

Prashant Sonakar's avatar
Prashant Sonakar committed
59
    if (dict.readIfPresent("startPosition", startPosition_))
60
    {
Prashant Sonakar's avatar
Prashant Sonakar committed
61
        if ((startPosition_ < 0) || (startPosition_ > 1))
62
        {
63
64
            FatalIOErrorInFunction(dict)
                << "startPosition must be in the range 0-1"
65
66
                << exit(FatalIOError);
        }
Prashant Sonakar's avatar
Prashant Sonakar committed
67
68
69
70
        else
        {
            position_ = startPosition_;
        }
71
72
73
74
75
76
77
    }

    if (nFrameTotal_ > 1)
    {
        scalar endPosition = dict.lookupOrDefault<scalar>("endPosition", 1);
        if ((endPosition < 0) || (endPosition > 1))
        {
78
79
            FatalIOErrorInFunction(dict)
                << "endPosition must be in the range 0-1"
80
81
                << exit(FatalIOError);
        }
Prashant Sonakar's avatar
Prashant Sonakar committed
82
        dPosition_ = (endPosition - startPosition_)/scalar(nFrameTotal_ - 1);
83
84
    }

85
86
87
    cameraPosition_ = Function1<vector>::New("position", dict);
    cameraFocalPoint_ = Function1<point>::New("focalPoint", dict);
    cameraUp_ = Function1<vector>::New("up", dict);
88

89
90
91
    dict.readIfPresent("clipBox", clipBox_);
    dict.lookup("parallelProjection") >> parallelProjection_;
    if (!parallelProjection_)
92
    {
93
        if (dict.found("viewAngle"))
94
        {
95
            cameraViewAngle_ = Function1<scalar>::New("viewAngle", dict);
96
        }
97
        else
98
        {
99
            cameraViewAngle_.reset
100
            (
101
                new Function1Types::Constant<scalar>("viewAngle", 35.0)
102
103
104
105
            );
        }
    }

106
107
108
109
110
111
112
113
114
115
116
    if (dict.found("zoom"))
    {
        cameraZoom_ = Function1<scalar>::New("zoom", dict);
    }
    else
    {
        cameraZoom_.reset
        (
            new Function1Types::Constant<scalar>("zoom", 1.0)
        );
    }
117
118
119
}


120
121
122
123
void Foam::functionObjects::runTimePostPro::scene::readColours
(
    const dictionary& dict
)
124
125
126
127
128
{
    const wordList colours = dict.toc();
    forAll(colours, i)
    {
        const word& c = colours[i];
129
        colours_.insert(c, Function1<vector>::New(c, dict).ptr());
130
131
132
133
    }
}


134
135
136
137
138
void Foam::functionObjects::runTimePostPro::scene::initialise
(
    vtkRenderer* renderer,
    const word& outputName
)
139
140
{
    currentFrameI_ = 0;
141
    position_ = startPosition_;
142
143
144
145

    outputName_ = outputName;

    // Set the background
146
    const vector backgroundColour = colours_["background"]->value(position_);
147
148
149
150
151
152
153
154
155
156
157
    renderer->SetBackground
    (
        backgroundColour.x(),
        backgroundColour.y(),
        backgroundColour.z()
    );

    // Apply gradient background if "background2" defined
    if (colours_.found("background2"))
    {
        renderer->GradientBackgroundOn();
158
        vector backgroundColour2 = colours_["background2"]->value(position_);
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177

        renderer->SetBackground2
        (
            backgroundColour2.x(),
            backgroundColour2.y(),
            backgroundColour2.z()
        );
    }

    // Depth peeling
    renderer->SetUseDepthPeeling(true);
    renderer->SetMaximumNumberOfPeels(4);
    renderer->SetOcclusionRatio(0);

    // Set the camera
    vtkSmartPointer<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New();
    camera->SetParallelProjection(parallelProjection_);
    renderer->SetActiveCamera(camera);

178
179
180
181
    // Add the lights
    vtkSmartPointer<vtkLightKit> lightKit = vtkSmartPointer<vtkLightKit>::New();
    lightKit->AddLightsToRenderer(renderer);

182
    if (clipBox_ != boundBox::greatBox)
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
    {
        const point& min = clipBox_.min();
        const point& max = clipBox_.max();
        vtkSmartPointer<vtkCubeSource> clipBox =
            vtkSmartPointer<vtkCubeSource>::New();
        clipBox->SetXLength(max.x() - min.x());
        clipBox->SetYLength(max.y() - min.y());
        clipBox->SetZLength(max.z() - min.z());
        clipBox->SetCenter
        (
            min.x() + 0.5*(max.x() - min.x()),
            min.y() + 0.5*(max.y() - min.y()),
            min.z() + 0.5*(max.z() - min.z())
        );
        vtkSmartPointer<vtkPolyDataMapper> clipMapper =
            vtkSmartPointer<vtkPolyDataMapper>::New();
        clipMapper->SetInputConnection(clipBox->GetOutputPort());

201
202
203
204
        clipBoxActor_ = vtkSmartPointer<vtkActor>::New();
        clipBoxActor_->SetMapper(clipMapper);
        clipBoxActor_->VisibilityOff();
        renderer->AddActor(clipBoxActor_);
205
206
207
208
    }
}


209
210
211
212
void Foam::functionObjects::runTimePostPro::scene::setCamera
(
    vtkRenderer* renderer
) const
213
{
214
215
216
    vtkCamera* camera = renderer->GetActiveCamera();

    if (parallelProjection_)
217
    {
218
219
        // Restore parallel scale to allow application of zoom (later)
        camera->SetParallelScale(1);
220
    }
221
    else
222
223
224
    {
        // Restore viewAngle (it might be reset by clipping)
        camera->SetViewAngle(cameraViewAngle_->value(position_));
225
    }
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

    const vector up = cameraUp_->value(position_);
    const vector pos = cameraPosition_->value(position_);
    const point focalPoint = cameraFocalPoint_->value(position_);
    const scalar zoom = cameraZoom_->value(position_);

    camera->SetViewUp(up.x(), up.y(), up.z());
    camera->SetPosition(pos.x(), pos.y(), pos.z());
    camera->SetFocalPoint(focalPoint.x(), focalPoint.y(), focalPoint.z());


    // Apply clipping if required
    // Note: possible optimisation - if the camera is static, this only needs
    //       to be done once on initialisation
    if (clipBox_ != boundBox::greatBox)
    {
        // Call ResetCamera() to fit clip box in view
        clipBoxActor_->VisibilityOn();
        renderer->ResetCamera();
        clipBoxActor_->VisibilityOff();
    }

    // Zoom applied after all other operations
    camera->Zoom(zoom);

    camera->Modified();
252
253
254
}


255
256
Foam::string
Foam::functionObjects::runTimePostPro::scene::frameIndexStr() const
257
258
259
260
261
262
263
264
265
266
{
    string str = Foam::name(currentFrameI_);
    str.insert(0, 4 - str.length(), '0');

    return str;
}


// * * * * * * * * * * * * * * * * Constructors  * * * * * * * * * * * * * * //

267
268
269
270
271
Foam::functionObjects::runTimePostPro::scene::scene
(
    const objectRegistry& obr,
    const word& name
)
272
273
274
275
:
    obr_(obr),
    name_(name),
    colours_(),
Andrew Heather's avatar
Andrew Heather committed
276
277
278
279
    cameraPosition_(nullptr),
    cameraFocalPoint_(nullptr),
    cameraUp_(nullptr),
    cameraViewAngle_(nullptr),
280
281
282
    cameraZoom_(nullptr),
    clipBox_(boundBox::greatBox),
    clipBoxActor_(),
283
284
    parallelProjection_(true),
    nFrameTotal_(1),
Prashant Sonakar's avatar
Prashant Sonakar committed
285
    startPosition_(0),
286
287
288
289
290
291
292
293
294
    position_(0),
    dPosition_(0),
    currentFrameI_(0),
    outputName_("unknown")
{}


// * * * * * * * * * * * * * * * * Destructor  * * * * * * * * * * * * * * * //

295
Foam::functionObjects::runTimePostPro::scene::~scene()
296
297
298
299
300
{}


// * * * * * * * * * * * * * * * Member Functions  * * * * * * * * * * * * * //

301
const Foam::HashPtrTable<Foam::Function1<Foam::vector>, Foam::word>&
302
Foam::functionObjects::runTimePostPro::scene::colours() const
303
304
305
306
307
{
    return colours_;
}


308
Foam::label Foam::functionObjects::runTimePostPro::scene::frameIndex() const
309
310
311
312
313
{
    return currentFrameI_;
}


314
Foam::scalar Foam::functionObjects::runTimePostPro::scene::position() const
315
316
317
318
319
{
    return position_;
}


320
321
322
323
void Foam::functionObjects::runTimePostPro::scene::read
(
    const dictionary& dict
)
324
325
326
327
328
329
{
    readCamera(dict.subDict("camera"));
    readColours(dict.subDict("colours"));
}


330
bool Foam::functionObjects::runTimePostPro::scene::loop(vtkRenderer* renderer)
331
332
{
    static bool initialised = false;
333
    setCamera(renderer);
334
335
336
337
338
339
340

    if (!initialised)
    {
        initialised = true;
        return true;
    }

341
342
343
344
    // Ensure that all objects can be seen without clipping
    // Note: can only be done after all objects have been added!
    renderer->ResetCameraClippingRange();

345
346
347
348
349
    // Save image from last iteration
    saveImage(renderer->GetRenderWindow());

    currentFrameI_++;

Prashant Sonakar's avatar
Prashant Sonakar committed
350
    position_ = startPosition_ + currentFrameI_*dPosition_;
351

352
353
354
355
356
357
    if (currentFrameI_ < nFrameTotal_)
    {
        return true;
    }
    else
    {
358
        initialised = false;
359
360
361
362
363
        return false;
    }
}


364
365
366
367
void Foam::functionObjects::runTimePostPro::scene::saveImage
(
    vtkRenderWindow* renderWindow
) const
368
369
370
371
372
373
{
    if (!renderWindow)
    {
        return;
    }

374
375
    const Time& runTime = obr_.time();

376
377
378
    fileName prefix(Pstream::parRun() ?
        runTime.path()/".."/"postProcessing"/name_/obr_.time().timeName() :
        runTime.path()/"postProcessing"/name_/obr_.time().timeName());
379

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
    mkDir(prefix);

    renderWindow->Render();

    // Set up off-screen rendering
    vtkSmartPointer<vtkWindowToImageFilter> windowToImageFilter =
        vtkSmartPointer<vtkWindowToImageFilter>::New();

    windowToImageFilter->SetInput(renderWindow);

    //// Add alpha channel for transparency
    // windowToImageFilter->SetInputBufferTypeToRGBA();
    windowToImageFilter->SetInputBufferTypeToRGB();

//    windowToImageFilter->ReadFrontBufferOff();
    windowToImageFilter->Update();

    // Save the image
    vtkSmartPointer<vtkPNGWriter> writer = vtkSmartPointer<vtkPNGWriter>::New();
    fileName fName(prefix/outputName_ + '.' + frameIndexStr() + ".png");
    writer->SetFileName(fName.c_str());
    writer->SetInputConnection(windowToImageFilter->GetOutputPort());

    Info<< "    Generating image: " << fName << endl;

    writer->Write();
}


// ************************************************************************* //