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