Средство просмотра автоматически меняет материал на значение по умолчанию

У меня проблема со сменой материала некоторых элементов

при загрузке геометрии:

_this.viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, () => {
    changeModelMaterial()
});

...

const changeModelMaterial = () => {
    const grey = new THREE.Color(0.5, 0.5, 0.5);
    let dbIds = getDbIds()
    changeAllElementsMaterial(grey)
    setMaterialOfDbIds(dbIds)
}

код, который я использую для изменения материала:

const changeAllElementsMaterial = (color) => {
    const fragmentList = _this.viewer.model.getFragmentList();
    for (let materialId of fragmentList.materialids) {
        if (fragmentList.materialmap[materialId]) {
            fragmentList.materialmap[materialId].map = null
            fragmentList.materialmap[materialId].color = color
            fragmentList.materialmap[materialId].needsUpdate = true;
        }
    }
    _this.viewer.impl.invalidate(true);
}

const setMaterialOfDbIds = (dbIds) => {
    var color_diffuse = 0xAB00EE;
    var color_specular = 0xEEABEE;
    var colorM = new THREE.MeshPhongMaterial({
        color: color_diffuse,
        specular: color_specular
    });
    _this.viewer.impl.matman().addMaterial(
        'ADN-Material-' +
        "common color material",  // or a GUID
        colorM,
        true);

   for (let dbId of dbIds) {
       _this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
           _this.viewer.model.getFragmentList().setMaterial(fragId, colorM);

       });
   }
   _this.viewer.impl.invalidate(true);
}

Это работает, потому что я вижу, что материалы модели изменены, но проблема в том, что материалы возвращаются к значениям по умолчанию через ~ 1-2 секунды.

После этого я не могу изменить материал, даже запустив этот код вручную.

Вопрос в том, почему Viewer блокирует изменение материала после этих 2 секунд, как это предотвратить.

И, может быть, вы сможете сказать мне, что я могу сделать лучше с материальными изменениями, например. может быть, что-то лучше, чем запускать мой код после GEOMETRY_LOAD. Лучше всего сменить материал перед первым рендером модели.

........

намекать:

при изменении события с GEOMETRY_LOADED_EVENT на OBJECT_TREE_CREATED_EVENT «иногда», но только иногда, он работает хорошо (материалы остаются до конца работы с моделью), но в основном, когда я запускаю свой метод после OBJECT_TREE_CREATED, он не работает (даже не работает, запустив его вручную, материалы каким-то образом заблокированы). Поэтому я подозреваю, что проблема возникла между GEOMETRY_LOAD и OBJECT_TREE_CREATED

Буду благодарен за любую помощь

============================== полный код ================== ============

index.html

<div id="main">
    <div id="MyViewerDiv"></div>
    <button id="open-nav-button" onClick="showDocInfo()">test</button>
</div>
<script src="https://developer.api.autodesk.com/derivativeservice/v2/viewers/three.min.js"></script>
<script src="https://developer.api.autodesk.com/derivativeservice/v2/viewers/viewer3D.min.js"></script>

<script type="text/javascript" src="lib/jquery.min.js"></script>

<script src="js/autodesk-viewer.js"></script>
<script src="js/extension/test-extension.js"></script>

<script>
    const autodeskViewer = new AutodeskViewer()
    const showDocInfo = () => {
        autodeskViewer.showDocInfo()
    }
</script>

autodesk-viewer.js

var AutodeskViewer = (function () {
function AutodeskViewer() {
    var _this = this;
    this.urn = 'urn:dXJuOmFkc2sub2JqZWN0czpvcy5vYmplY3Q6Zm9yZ2UtamF2YS1zYW1wbGUtYXBwLTFzcGduazdqcWpxdjhmYXV0YmNzd2R0cGdvN3VtNWY1L1BPQy1Gb3JnZS1JVCUyMDIwMTclMjBSdWNoXzEwMDUxNy5ud2Q';

    this.initializeViewer = function (containerId, documentId) {
        _this.viewerApp = new Autodesk.Viewing.ViewingApplication(containerId);
        var config = {
            extensions: ['TestExtension']
        };
        _this.viewerApp.registerViewer(_this.viewerApp.k3D, Autodesk.Viewing.Private.GuiViewer3D, config);
        _this.viewerApp.loadDocument(documentId, _this.onDocumentLoadSuccess, _this.onDocumentLoadFailure);
    }

    this.onDocumentLoadSuccess = function (doc) {
        const viewables = _this.viewerApp.bubble.search(av.BubbleNode.MODEL_NODE);
        if (viewables.length === 0) {
            return;
        }
        _this.viewerApp.selectItem(viewables[0].data, _this.onItemLoadSuccess, _this.onItemLoadFail);
        _this.viewer3d = _this.viewerApp.getCurrentViewer();
    }

    this.onDocumentLoadFailure = (viewerErrorCode) => {}

    this.onItemLoadSuccess = (viewer) => {
        _this.viewer = viewer
    }

    this.onItemLoadFail = (errorCode) => {}

    this.initialize = () => {
        var options = {
            env: 'AutodeskProduction',
            getAccessToken: _this.getToken,
            refreshToken: _this.getToken
        };
        Autodesk.Viewing.Initializer(options, _this.initCallback);
    };

    this.initCallback = function () {
        _this.initializeViewer('MyViewerDiv', _this.urn, '3d');
    };
    this.getToken = function (onGetAccessToken) {
        $.get("forge/oauth/token")
            .done(function (data) {
                token = data
                onGetAccessToken(token, 60 * 60);
            })
            .fail(function (error) {
                console.log('ERROR', error);
            });
    };

    this.showDocInfo = function () {};
    this.initialize();
}

return AutodeskViewer;
}());

test-extension.js

var _self;
var _viewer;
var _tempValue = 0;

function TestExtension(viewer, options) {
    Autodesk.Viewing.Extension.call(this, viewer, options);
    _self = this;
    _viewer = viewer;
}

const changeModelMaterial = () => {
    // _tempValue++;
    // if (_tempValue == 2) {
    const elements = [4340, 4342, 4344, 4346, 4348, 4367, 4371, 4375, 4380, 4452, 4468, 4488, 4503, 4517, 4520, 4522, 4524, 4526, 4528, 4530]

    changeAllElementsMaterial(new THREE.Color(0.5, 0.5, 0.5))
    setMaterialOfDbIds(elements)
    _tempValue = 0
    // }
}

const changeAllElementsMaterial = (color) => {
    var fragmentList = _viewer.model.getFragmentList();
    for (let materialId of fragmentList.materialids) {
        if (fragmentList.materialmap[materialId]) {
            fragmentList.materialmap[materialId].map = null
            fragmentList.materialmap[materialId].color = color
            fragmentList.materialmap[materialId].needsUpdate = true;
        }
    }
    _viewer.impl.invalidate(true);
}

const setMaterialOfDbIds = (dbIds) => {
    var colorM = new THREE.MeshPhongMaterial({
        color: new THREE.Color(0xAB00EE)
    });

    for (let dbId of dbIds) {
        _viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
            _viewer.model.getFragmentList().setMaterial(fragId, colorM);

        });
    }
    _viewer.impl.invalidate(true);
}

TestExtension.prototype = Object.create(Autodesk.Viewing.Extension.prototype);
TestExtension.prototype.constructor = TestExtension;

TestExtension.prototype.load = function () {
    _viewer.addEventListener(Autodesk.Viewing.OBJECT_TREE_CREATED_EVENT, changeModelMaterial)
    // _viewer.addEventListener(Autodesk.Viewing.GEOMETRY_LOADED_EVENT, changeModelMaterial)
    return true
};

TestExtension.prototype.unload = function () {
    return true
};

Autodesk.Viewing.theExtensionManager.registerExtension('TestExtension', TestExtension);

person Tomasz Cy-man    schedule 09.10.2017    source источник


Ответы (2)


Я нашел решение совершенно случайно ... из того, что я пытался сделать

до:

    const setMaterialOfDbIds = (dbIds) => {
        var color_diffuse = 0xAB00EE;
        var color_specular = 0xEEABEE;
        var colorM = new THREE.MeshPhongMaterial({
            color: color_diffuse,
            specular: color_specular
        });
        _this.viewer.impl.matman().addMaterial("common color material", colorM, true);

        for (let dbId of dbIds) {
            _this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
                _this.viewer.model.getFragmentList().setMaterial(fragId, colorM);
            });
        }
        _this.viewer.impl.invalidate(true);
    }

после

    const setMaterialOfDbIds = (dbIds) => {
        var color_diffuse = 0xAB00EE;
        var color_specular = 0xEEABEE;
        var colorM = new THREE.MeshPhongMaterial({
            color: color_diffuse,
            specular: color_specular
        });
        _this.viewer.impl.matman().addMaterial("common color material", colorM, true);

        for (let dbId of dbIds) {
            _this.viewer.model.getData().instanceTree.enumNodeFragments(dbId, function (fragId) {
                _this.viewer.model.getFragmentList().setMaterial(fragId, colorM);
                var fragProxy = _this.viewer.impl.getFragmentProxy(_this.viewer.model, fragId)
                fragProxy.updateAnimTransform()
            });
        }
        _this.viewer.impl.invalidate(true);
    }

На самом деле я не знаю, зачем добавлять

var fragProxy = _this.viewer.impl.getFragmentProxy(_this.viewer.model, fragId)
fragProxy.updateAnimTransform()

имело значение, я не видел ничего подобного ни в одном примере обновления материала. Что интересно, этот код работает только для нескольких элементов в модели, но он работает даже для тех элементов, материалы которых были изменены ранее (в методе changeAllElementsMaterial).

@Philippe Leefsma, если вы это понимаете, пожалуйста, расскажите подробнее, почему это работает

person Tomasz Cy-man    schedule 10.10.2017

Пока я не могу воспроизвести проблему со своей стороны, я использую следующий код (ES7), извлеченный из этого расширения: Viewing.Extension.Material

createColorMaterial (color) {

  const material = new THREE.MeshPhongMaterial({
    specular: new THREE.Color(color),
    side: THREE.DoubleSide,
    reflectivity: 0.0,
    color
  })

  const materials = this.viewer.impl.getMaterials()

  materials.addMaterial(
    this.guid(),
    material,
    true)

  return material
}

async onModelCompletedLoad() {

  const material = this.createColorMaterial(0xFF0000)

  const model = this.viewer.model

  const fragIds = await Toolkit.getFragIds(model)

  fragIds.forEach((fragId) => {

    model.getFragmentList().setMaterial(
      fragId, material)
  })

  this.viewer.impl.sceneUpdated(true)
} 

onModelCompletedLoad - это настраиваемое событие, запускаемое, когда были запущены и GEOMETRY_LOADED_EVENT, и OBJECT_TREE_CREATED_EVENT.

Прочтите эту статью для получения дополнительных сведений: Асинхронный просмотрщик уведомление о событиях

Я сомневаюсь, что вы можете легко изменить материалы до того, как модель будет сначала отрисована, однако вы можете использовать настраиваемый оверлей, который скрывает модель до тех пор, пока ваша настраиваемая логика не выполнит все необходимые шаги. Это подход, который я использую в своих демонстрациях по адресу: https://forge-rcdb.autodesk.io/configurator

После загрузки модели все нестандартные материалы сохраняются нормально:

введите здесь описание изображения

Расширение материала можно протестировать в режиме реального времени здесь.

надеюсь, это поможет

person Philippe    schedule 09.10.2017
comment
хорошо, теперь я делаю свои вещи после того, как два события (OBJECT_TREE_CREATED_EVENT и GEOMETRY_LOADED_EVENT) запущены, и с этим изменением он по-прежнему ведет себя так же. Думаю, сначала нужно выяснить, почему материалы можно менять только в первые 2-3 секунды. - person Tomasz Cy-man; 09.10.2017
comment
Если вы можете предоставить воспроизводимый образец, я посмотрю. Вы пробовали демо моего материала? Там определенно отлично работает, так что вы, должно быть, делаете что-то не так ... Вы также можете взглянуть на viewer.setThemingColor: stackoverflow.com/questions/38534862/ - person Philippe; 09.10.2017
comment
Я обновил свой вопрос, теперь вы можете увидеть весь код, который я использую, вы можете попробовать его самостоятельно. Теперь, когда расширение загрузится, я добавляю прослушиватель событий для OBJECT_TREE_CREATED_EVENT. Так что то, что я хочу делать, работает только иногда. Когда вы раскомментируете другого слушателя из метода загрузки и раскомментируете простой трюк из метода changeModelMaterial, вы должны увидеть, что материалы обновляются, а затем через ~ 2 секунды возвращаются к значениям по умолчанию. - person Tomasz Cy-man; 10.10.2017