001/*
002 * (C) Copyright 2006-2016 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Tiago Cardoso <tcardoso@nuxeo.com>
018 */
019package org.nuxeo.ecm.platform.threed.service;
020
021import org.apache.commons.logging.Log;
022import org.apache.commons.logging.LogFactory;
023import org.nuxeo.ecm.core.api.Blob;
024import org.nuxeo.ecm.core.api.DocumentModel;
025import org.nuxeo.ecm.core.api.blobholder.BlobHolder;
026import org.nuxeo.ecm.core.api.blobholder.SimpleBlobHolder;
027import org.nuxeo.ecm.core.convert.api.ConversionService;
028import org.nuxeo.ecm.core.work.api.Work;
029import org.nuxeo.ecm.core.work.api.WorkManager;
030import org.nuxeo.ecm.platform.threed.ThreeD;
031import org.nuxeo.ecm.platform.threed.ThreeDBatchProgress;
032import org.nuxeo.ecm.platform.threed.TransmissionThreeD;
033import org.nuxeo.runtime.api.Framework;
034import org.nuxeo.runtime.model.ComponentContext;
035import org.nuxeo.runtime.model.ComponentInstance;
036import org.nuxeo.runtime.model.DefaultComponent;
037
038import java.io.Serializable;
039import java.util.ArrayList;
040import java.util.Collection;
041import java.util.HashMap;
042import java.util.List;
043import java.util.Map;
044import java.util.concurrent.TimeUnit;
045import java.util.stream.Collectors;
046
047import static org.nuxeo.ecm.core.work.api.Work.State.*;
048import static org.nuxeo.ecm.platform.threed.ThreeDDocumentConstants.RENDER_VIEWS_PROPERTY;
049import static org.nuxeo.ecm.platform.threed.ThreeDDocumentConstants.TRANSMISSIONS_PROPERTY;
050import static org.nuxeo.ecm.platform.threed.convert.Constants.*;
051
052/**
053 * Default implementation of {@link ThreeDService}
054 *
055 * @since 8.4
056 */
057public class ThreeDServiceImpl extends DefaultComponent implements ThreeDService {
058
059    protected static final Log log = LogFactory.getLog(ThreeDServiceImpl.class);
060
061    public static final String RENDER_VIEWS_EP = "renderViews";
062
063    public static final String DEFAULT_RENDER_VIEWS_EP = "automaticRenderViews";
064
065    public static final String DEFAULT_LODS_EP = "automaticLOD";
066
067    protected AutomaticLODContributionHandler automaticLODs;
068
069    protected AutomaticRenderViewContributionHandler automaticRenderViews;
070
071    protected RenderViewContributionHandler renderViews;
072
073    @Override
074    public void activate(ComponentContext context) {
075        automaticLODs = new AutomaticLODContributionHandler();
076        automaticRenderViews = new AutomaticRenderViewContributionHandler();
077        renderViews = new RenderViewContributionHandler();
078    }
079
080    @Override
081    public void deactivate(ComponentContext context) {
082        WorkManager workManager = Framework.getService(WorkManager.class);
083        if (workManager != null && workManager.isStarted()) {
084            try {
085                workManager.shutdownQueue(
086                        workManager.getCategoryQueueId(ThreeDBatchUpdateWork.CATEGORY_THREED_CONVERSION), 10,
087                        TimeUnit.SECONDS);
088            } catch (InterruptedException e) {
089                Thread.currentThread().interrupt();
090                throw new RuntimeException(e);
091            }
092        }
093        automaticLODs = null;
094        automaticRenderViews = null;
095        renderViews = null;
096    }
097
098    @Override
099    public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
100        switch (extensionPoint) {
101        case RENDER_VIEWS_EP:
102            renderViews.addContribution((RenderView) contribution);
103            break;
104        case DEFAULT_RENDER_VIEWS_EP:
105            automaticRenderViews.addContribution((AutomaticRenderView) contribution);
106            break;
107        case DEFAULT_LODS_EP:
108            automaticLODs.addContribution((AutomaticLOD) contribution);
109        }
110    }
111
112    @Override
113    public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) {
114        switch (extensionPoint) {
115        case RENDER_VIEWS_EP:
116            renderViews.removeContribution((RenderView) contribution);
117            break;
118        case DEFAULT_RENDER_VIEWS_EP:
119            automaticRenderViews.removeContribution((AutomaticRenderView) contribution);
120            break;
121        case DEFAULT_LODS_EP:
122            automaticLODs.removeContribution((AutomaticLOD) contribution);
123        }
124    }
125
126    @Override
127    public void cleanBatchData(DocumentModel doc) {
128        List<Map<String, Serializable>> emptyList = new ArrayList<>();
129        doc.setPropertyValue(TRANSMISSIONS_PROPERTY, (Serializable) emptyList);
130        doc.setPropertyValue(RENDER_VIEWS_PROPERTY, (Serializable) emptyList);
131    }
132
133    @Override
134    public void launchBatchConversion(DocumentModel doc) {
135        cleanBatchData(doc);
136        ThreeDBatchUpdateWork work = new ThreeDBatchUpdateWork(doc.getRepositoryName(), doc.getId());
137        WorkManager workManager = Framework.getService(WorkManager.class);
138        workManager.schedule(work, WorkManager.Scheduling.IF_NOT_SCHEDULED, true);
139    }
140
141    @Override
142    public BlobHolder batchConvert(ThreeD originalThreed) {
143        ConversionService cs = Framework.getService(ConversionService.class);
144        // get all the 3d content blobs
145        List<Blob> in = new ArrayList<>();
146        in.add(originalThreed.getBlob());
147        if (originalThreed.getResources() != null) {
148            in.addAll(originalThreed.getResources());
149        }
150
151        // gather 3D contribution default contributions
152        List<RenderView> renderViews = (List<RenderView>) getAutomaticRenderViews();
153        List<AutomaticLOD> lods = (List<AutomaticLOD>) getAutomaticLODs();
154
155        // setup all work to be done in batch process (renders, lods)
156        Map<String, Serializable> params = new HashMap<>();
157
158        // operators
159        String operators = "import info";
160        // add renders
161        operators += new String(new char[renderViews.size()]).replace("\0", " render");
162        // add lods
163        operators += new String(new char[lods.size()]).replace("\0", " lod info convert");
164        params.put(OPERATORS_PARAMETER, operators);
165
166        // render ids
167        params.put(RENDER_IDS_PARAMETER, renderViews.stream().map(RenderView::getId).collect(Collectors.joining(" ")));
168
169        // lod ids
170        params.put(LOD_IDS_PARAMETER, lods.stream().map(AutomaticLOD::getId).collect(Collectors.joining(" ")));
171
172        // percPoly
173        params.put(PERC_POLY_PARAMETER,
174                lods.stream().map(AutomaticLOD::getPercPoly).map(String::valueOf).collect(Collectors.joining(" ")));
175
176        // maxPoly
177        params.put(MAX_POLY_PARAMETER,
178                lods.stream().map(AutomaticLOD::getMaxPoly).map(String::valueOf).collect(Collectors.joining(" ")));
179
180        params.put(COORDS_PARAMETER,
181                renderViews.stream().map(renderView -> renderView.getAzimuth() + "," + renderView.getZenith()).collect(
182                        Collectors.joining(" ")));
183
184        // dimensions
185        params.put(DIMENSIONS_PARAMETER,
186                renderViews.stream().map(renderView -> renderView.getWidth() + "x" + renderView.getHeight()).collect(
187                        Collectors.joining(" ")));
188
189        return cs.convert(BATCH_CONVERTER, new SimpleBlobHolder(in), params);
190    }
191
192    @Override
193    public Collection<RenderView> getAvailableRenderViews() {
194        return renderViews.registry.values();
195    }
196
197    @Override
198    public Collection<RenderView> getAutomaticRenderViews() {
199        return automaticRenderViews.registry.values()
200                                            .stream()
201                                            .filter(AutomaticRenderView::isEnabled)
202                                            .sorted((o1, o2) -> o1.getOrder() - o2.getOrder())
203                                            .map(AutomaticRenderView::getId)
204                                            .map(this::getRenderView)
205                                            .filter(RenderView::isEnabled)
206                                            .collect(Collectors.toList());
207    }
208
209    @Override
210    public Collection<AutomaticLOD> getAvailableLODs() {
211        return automaticLODs.registry.values();
212    }
213
214    @Override
215    public Collection<AutomaticLOD> getAutomaticLODs() {
216        return automaticLODs.registry.values()
217                                     .stream()
218                                     .filter(AutomaticLOD::isEnabled)
219                                     .sorted((o1, o2) -> o1.getOrder() - o2.getOrder())
220                                     .collect(Collectors.toList());
221    }
222
223    @Override
224    public AutomaticLOD getAutomaticLOD(String automaticLODId) {
225        return automaticLODs.registry.get(automaticLODId);
226    }
227
228    @Override
229    public RenderView getRenderView(String renderViewId) {
230        return renderViews.registry.get(renderViewId);
231    }
232
233    @Override
234    public RenderView getRenderView(Integer azimuth, Integer zenith) {
235        return renderViews.registry.values()
236                                   .stream()
237                                   .filter(renderView -> renderView.getAzimuth().equals(azimuth)
238                                           && renderView.getZenith().equals(zenith))
239                                   .findFirst()
240                                   .orElse(null);
241    }
242
243    @Override
244    public TransmissionThreeD convertColladaToglTF(TransmissionThreeD colladaThreeD) {
245        ConversionService cs = Framework.getService(ConversionService.class);
246        Map<String, Serializable> parameters = new HashMap<>();
247        List<Blob> blobs = new ArrayList<>();
248        blobs.add(colladaThreeD.getBlob());
249        if (colladaThreeD.getResources() != null) {
250            blobs.addAll(colladaThreeD.getResources());
251        }
252        BlobHolder result = cs.convert(COLLADA2GLTF_CONVERTER, new SimpleBlobHolder(blobs), parameters);
253        return new TransmissionThreeD(result.getBlobs().get(0), null, colladaThreeD.getInfo(),
254                colladaThreeD.getPercPoly(), colladaThreeD.getMaxPoly(), colladaThreeD.getPercTex(),
255                colladaThreeD.getMaxTex(), colladaThreeD.getName());
256    }
257
258    @Override
259    public ThreeDBatchProgress getBatchProgress(String repositoryName, String docId) {
260        WorkManager workManager = Framework.getService(WorkManager.class);
261        Work work = new ThreeDBatchUpdateWork(repositoryName, docId);
262        Work workRunning = workManager.find(work.getId(), RUNNING);
263        if (workRunning != null) {
264            return new ThreeDBatchProgress(ThreeDBatchProgress.STATUS_CONVERSION_RUNNING, workRunning.getStatus());
265        }
266        Work workScheduled = workManager.find(work.getId(), SCHEDULED);
267        if (workScheduled != null) {
268            return new ThreeDBatchProgress(ThreeDBatchProgress.STATUS_CONVERSION_QUEUED, "");
269        }
270        return new ThreeDBatchProgress(ThreeDBatchProgress.STATUS_CONVERSION_UNKNOWN, "");
271    }
272}