CodeSys开发3d机械臂显示控件

1.背景

在之前的【CodeSys创建自定义的html5控件】中,我们介绍了如何创建html5控件并加到CodeSys的控件库中。
目前在做一个机械手项目,想要显示一下机械手的姿态。很自然地想到,利用html5控件来显示。
经过搜索,恰好有个叫three.js的能在html5中使用的3d库满足需求。于是,就打算将其集成进来。
实际在集成的过程中,碰到了很多问题,但基本都一一解决了。现在把我实现的过程记录下来。

2.效果

先看看效果
控件参数界面:
在这里插入图片描述在这里插入图片描述

运行界面:
在这里插入图片描述

3.出现的问题及解决方案

3.1.can not read property of undefined…

假如按照官方的例子【Project Logo HTML5-API-Examples 】来写,有可能会报can not read property of undefined。如下图所示
在这里插入图片描述

这个原因官方貌似也有说明。
【API that HTML5 controls can call to interact with IEC code】
在这里插入图片描述主要是CDSWebVisuAccess这个对象要在iFrame加载完成后才能使用。因此,我们所有的函数,最好是在load信号发出后再执行。类似这样:

    this.init = function(){
    // 自己的js代码
    }
    window.addEventListener("load", this.init);

3.2.three.js脚本文件的下载及使用

用来渲染3d界面的three.js库,假如直接从github【/mrdoob/three.js/】下载,得到的几个js文件貌似不能直接用(可能是给npm用的?)。因此,我参考了这里【html three.js 引入.stl模型示例】,直接从cdn下载算了。

src="https://cdn.jsdelivr.net/npm/three@0.137/build/three.min.js"
src="https://cdn.jsdelivr.net/npm/three@0.137/examples/js/controls/OrbitControls.js"
src="https://cdn.jsdelivr.net/npm/three@0.137/examples/js/loaders/STLLoader.js"

从github官方直接下载的貌似是module,导入的用法(写法)貌似不一样。要类似这样子用。(还没测试,先挖个坑)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>Three.js Module Example</title>
</head>
<body>
    <script type="module">
        import * as THREE from './path/to/three.module.min.js';
        // 使用THREE创建场景、相机、渲染器等
    </script>
</body>
</html>

或者

<script type="module" src="path/to/three.module.min.js"></script>

关于如何实现three.js加载机械臂模型及通过角度值控制机械臂的运动,比较复杂(相对来说),到时有时间再另外写一篇文章来说明了。

3.3.文件没有上传至PLC

更改版本号。
有时候你明明在【HTML5控件编辑器】中添加了某些图片、脚本或者其他文件,但是安装、上传后,却无法在PLC对应的位置找到。
这可能是codesys的检测文件功能问题。最好最彻底的应对办法是,每做一次修改(代码修改、文件增减等),都设置一个新的版本号。这样就会触发全量更新。这样做就应该没问题了。

3.4.文件路径问题

在这里插入图片描述上传到PLC的文件,并不会保持原有的名字。而是出于避免文件重名冲突的考虑,自动把文件重命名了。假如想知道重命名后的实际文件名,就需要使用CDSWebVisuAccess.getAdditionalFile这个函数了。

window.CDSWebVisuAccess.getAdditionalFile("Codesys_Logo.svg").then( (newFilePath) => {
				var image = new Image();
				image.src = newFilePath;
				document.body.appendChild(image);
			}
			);

3.5.Content-Security-Policy的问题

在这里插入图片描述

Content-Security-Policy:由于违反了下列指令:“default-src ‘nonce-ItNLPfWapdlvNbrIxlV4CA==’ ‘unsafe-inline’”,此页面位于 blob:null/6f717d2c-d87e-4cdd-a2b7-9019f897e440 的资源(connect-src)无法加载

这个问题好像是three.js的问题?利用CodeSys的自带函数(CDSWebVisuAccess中的函数)对文件读取是没啥问题的、用Image加载图片也是没问题的。就是这个THREE.STLLoader.load()才出问题

        var tmpLoader = new THREE.STLLoader();
        tmpLoader.load(filePath);

必须要绕开他才行。
直接读取blob,然后blob转 ArrayBuffer,直接使用 STLLoader的parse函数。

    function loadModel(modelFile) {
        // 加载 STL 文件
        window.CDSWebVisuAccess.getBinaryFile(modelFile).then((fileBlob) => {
            // 创建FileReader实例
            const reader = new FileReader();
            // 当FileReader完成读取Blob时,这个函数会被调用
            reader.onload = function (event) {
                // event.target.result 将是一个ArrayBuffer
                const arrayBuffer = event.target.result;

                var loader = new THREE.STLLoader();
                var geometry = loader.parse(arrayBuffer)
            }
            
            // 以ArrayBuffer的形式读取Blob
            reader.readAsArrayBuffer(fileBlob);
        });
     }

经过测试,凡是使用了URL.createObjectURL,fetch等方式访问资源的话,都会触犯它的天规。因此都必须避开。好在‘data:xxx’的base64连接没问题。
狗日的,连data类型的也拒绝
在这里插入图片描述

3.6.在某些工程中无法看到安装的html5工具箱的问题

这是由于codesys默认不支持html5工具,需要开启支持才行。
比如说codesys自带的例程Robotics_Jogging,直接去界面工具箱是看不到html5相关工具的
在这里插入图片描述
必须要在Visulization Mananger处右键点击添加WebVisu,并且在Visulization Mananger处勾选【支持客户端动化和覆盖本地元素】
在这里插入图片描述

然后就可以看到了(假如还是看不到,就重启工程)
在这里插入图片描述

3.7.模型贴图问题

假如直接使用THREE.TextureLoader,也是会触犯CodeSys的安全策略,导致无法顺利加载的。
但我们可以利用CDSWebVisuAccess.getBinaryFile加载得到贴图的原始数据,然后再做处理。
搞了贴图的效果:
在这里插入图片描述

3.7.1.使用图片解码库解码 (不建议)

调用pngjs来对数据进行解码,然后把解码后的数据给到THREE.DataTexture从而得到贴图,这个时候才能贴到材质上。
你搜pngjs的话,会找到好几个,我是到这里[foliojs/png.js]下载的。将png.js和zlib.js加进来就行了。

window.CDSWebVisuAccess.getBinaryFile("myImage.png").then((fileBlob) => {
                // 创建FileReader实例
                const reader = new FileReader();
                // 当FileReader完成读取Blob时,这个函数会被调用
                reader.onload = function (event) {
                    // event.target.result 将是一个ArrayBuffer
                    const arrayBuffer = event.target.result;

                    // 创建一个 Uint8Array 来引用 ArrayBuffer
                    const data = new Uint8Array(arrayBuffer);

                    const png = new PNG(data);

                    let pixelBytes = png.pixelBitlength / 8
                    console.log("png size", png.width, png.height, pixelBytes)

                    let imgFormat = THREE.RGBFormat; // 默认设置为 RGB 格式
                    switch(pixelBytes) {
                        case 1: imgFormat = THREE.LuminanceFormat; break; // 灰度图像
                        case 2: imgFormat = THREE.LuminanceAlphaFormat; break; // 灰度图像带 Alpha 通道
                        case 3: imgFormat = THREE.RGBFormat; break; // RGB 图像
                        case 4: imgFormat = THREE.RGBAFormat; break; // RGBA 图像
                        default: imgFormat = THREE.RGBFormat; break; // 默认设置为 RGB 格式
                    }
                    const width  = png.width; // 宽度
                    const height = png.height; // 高度

                    // 创建DataTexture
                    var texture = new THREE.DataTexture(
                        png.decodePixels(),  // 包含像素数据的Uint8Array
                        width, // 纹理宽度
                        height, // 纹理高度
                        imgFormat, // THREE.RGBAFormat, // 数据格式
                        THREE.UnsignedByteType, // THREE.UnsignedByteType, // 数据类型
                        THREE.UVMapping, // UV映射模式
                        THREE.ClampToEdgeWrapping, // wrapping模式
                        THREE.ClampToEdgeWrapping, // wrapping模式
                        THREE.LinearFilter, // minFilter
                        THREE.LinearFilter // magFilter
                    );
                    texture.flipY = false;
                    texture.encoding = THREE.LinearEncoding; //THREE.LinearEncoding THREE.RGBEEncoding THREE.sRGBEncoding
                    texture.generateMipmaps = true;
                    texture.needsUpdate = true; // 重要:通知 three.js 更新纹理

                    // 直接设置不知道为何不行,在html是可以的
                    // joint4.material.map = texture

                    var material = new THREE.MeshStandardMaterial(joint4.material);
                    material.map = texture
                    joint4.material = material

                    // var material = new THREE.MeshStandardMaterial({ map: texture });
                    // joint4.material = material

                    console.log(joint4, joint4.material)
                }

                // 以ArrayBuffer的形式读取Blob
                reader.readAsArrayBuffer(fileBlob);
            });

3.7.2.使用Image、canvas来进行解码 (建议)

除了使用解码库来解码,更加推荐的是使用canvas来将图片绘制出来然后再获取canvas的rgba数据,这样就不用找解码库了。

window.CDSWebVisuAccess.getBinaryFile("myImage.png").then((fileBlob) => {
        let sourceURI = URL.createObjectURL(fileBlob);

        // 创建 Image 对象
        const image = new Image();

        // 设置 Image 对象的 src 属性
        image.src = sourceURI;

        // 监听 onload 事件
        image.onload = () => {
            // 创建 Canvas
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');

            // 设置 Canvas 的大小与图像一致
            canvas.width = image.width;
            canvas.height = image.height;

            // 将图像绘制到 Canvas
            ctx.drawImage(image, 0, 0, image.width, image.height);

            // 获取解码后的 RGBA 数据
            const imageData = ctx.getImageData(0, 0, image.width, image.height);
            const rgbaData = imageData.data;

            // 创建DataTexture
            var texture = new THREE.DataTexture(
                rgbaData,  // 包含像素数据的Uint8Array
                image.width, // 纹理宽度
                image.height, // 纹理高度
                THREE.RGBAFormat, // THREE.RGBAFormat, // 数据格式
                THREE.UnsignedByteType, // THREE.UnsignedByteType, // 数据类型
                THREE.UVMapping, // UV映射模式
                THREE.ClampToEdgeWrapping, // wrapping模式
                THREE.ClampToEdgeWrapping, // wrapping模式
                THREE.LinearFilter, // minFilter
                THREE.LinearFilter // magFilter
            );
            texture.flipY = false;
            texture.encoding = THREE.LinearEncoding; //THREE.LinearEncoding THREE.RGBEEncoding THREE.sRGBEncoding
            texture.generateMipmaps = true;
            texture.needsUpdate = true; // 重要:通知 three.js 更新纹理

            // 直接设置不知道为何不行,在html是可以的
            // joint4.material.map = texture

            var material = new THREE.MeshStandardMaterial(joint4.material);
            material.map = texture
            joint4.material = material

            // var material = new THREE.MeshStandardMaterial({ map: texture });
            // joint4.material = material

            console.log(joint4, joint4.material)
        }

    });

3.8.加载gltf/glb模型

因为gltf/glb模型文件会在一个文件里面包含了物体网格、材质、贴图、动画等等的资源。比较适合我的应用场景。
加载的话,是对GLTFLoader.js进行魔改实现的。
这个原始文件的下载地址为

<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/GLTFLoader.js"></script>

主要是把原本调用fileloader、textureloader的地方进行了修改,改成CDSWebVisuAccess.getBinaryFile,以及使用pngjs进行解码(最新的改成利用Image+canvas来进行解码,更好)。
修改了两个地方,
第一个,在load( url, onLoad, onProgress, onError )函数处:
在这里插入图片描述将原来的注释掉,然后加入修改后的。
修改完的函数为:

load( url, onLoad, onProgress, onError ) {

			console.log('---begine load1', url)

			const scope = this;
			let resourcePath;

			if ( this.resourcePath !== '' ) {

				resourcePath = this.resourcePath;

			} else if ( this.path !== '' ) {

				resourcePath = this.path;

			} else {

				resourcePath = THREE.LoaderUtils.extractUrlBase( url );

			} // Tells the LoadingManager to track an extra item, which resolves after
			// the model is fully loaded. This means the count of items loaded will
			// be incorrect, but ensures manager.onLoad() does not fire early.


			console.log('---begine load2', resourcePath)

			this.manager.itemStart( url );

			const _onError = function ( e ) {

				if ( onError ) {

					onError( e );

				} else {

					console.error( e );

				}

				scope.manager.itemError( url );
				scope.manager.itemEnd( url );

			};

			// const loader = new THREE.FileLoader( this.manager );
			// loader.setPath( this.path );
			// loader.setResponseType( 'arraybuffer' );
			// loader.setRequestHeader( this.requestHeader );
			// loader.setWithCredentials( this.withCredentials );
			// console.log("loader begin load", loader)
			// loader.load( url, function ( data ) {
			// 	try {

			// 		scope.parse( data, resourcePath, function ( gltf ) {

			// 			onLoad( gltf );
			// 			scope.manager.itemEnd( url );

			// 		}, _onError );

			// 	} catch ( e ) {

			// 		_onError( e );

			// 	}

			// }, onProgress, _onError );

			
			window.CDSWebVisuAccess.getBinaryFile(url).then((fileBlob) => {

				console.log("file blob", fileBlob)

				// 创建FileReader实例
				const reader = new FileReader();

				// 当FileReader完成读取Blob时,这个函数会被调用
				reader.onload = function (event) {
					// event.target.result 将是一个ArrayBuffer
					const arrayBuffer = event.target.result;

					console.log("arrayBuffer", arrayBuffer)

					try {
						scope.parse(arrayBuffer, resourcePath, function (gltf) {
							console.log("gltf", gltf)
							onLoad(gltf);
							scope.manager.itemEnd(url);

						}, _onError);

					} catch (e) {

						_onError(e);

					}
				}

				// 以ArrayBuffer的形式读取Blob
				reader.readAsArrayBuffer(fileBlob);
			});
		}

第二个,在loadTextureImage(textureIndex, source, loader)函数处:
在这里插入图片描述
将原来的注释掉,然后加入修改后的。
修改完的函数为(这是旧版的,只支持png格式的贴图;新版的更好,理论支持大部分图片格式。请继续往前看):

		loadTextureImage(textureIndex, source, loader) {
			const parser = this;
			const json = this.json;
			const options = this.options;
			const textureDef = json.textures[textureIndex];
			const URL = self.URL || self.webkitURL;
			let sourceURI = source.uri;
			let isObjectURL = false;
			let hasAlpha = true;
			if (source.mimeType === 'image/jpeg') hasAlpha = false;

			// if (source.bufferView !== undefined) {
			// 	// Load binary image data from bufferView, if provided.
			// 	sourceURI = parser.getDependency('bufferView', source.bufferView).then(function (bufferView) {
			// 		if (source.mimeType === 'image/png') {

			// 			// Inspect the PNG 'IHDR' chunk to determine whether the image could have an
			// 			// alpha channel. This check is conservative — the image could have an alpha
			// 			// channel with all values == 1, and the indexed type (colorType == 3) only
			// 			// sometimes contains alpha.
			// 			//
			// 			// https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
			// 			const colorType = new DataView(bufferView, 25, 1).getUint8(0, false);
			// 			hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;

			// 		}

			// 		isObjectURL = true;
			// 		const blob = new Blob([bufferView], {
			// 			type: source.mimeType
			// 		});
			// 		sourceURI = URL.createObjectURL(blob);

			// 		console.log("--the created blob", sourceURI)
			// 		return sourceURI;
			// 	});
			// } else if (source.uri === undefined) {
			// 	throw new Error('THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView');
			// }

			// return Promise.resolve(sourceURI).then(function (sourceURI) {

			// 	return new Promise(function (resolve, reject) {

			// 		let onLoad = resolve;

			// 		if (loader.isImageBitmapLoader === true) {

			// 			onLoad = function (imageBitmap) {

			// 				resolve(new THREE.CanvasTexture(imageBitmap));

			// 			};

			// 		}

			// 		loader.load(resolveURL(sourceURI, options.path), onLoad, undefined, reject);

			// 		// 就在这里取代 textureloader 的操作
			// 		console.log("-------replace loader:",
			// 			source.uri)

			// 	});

			// }).then(function (texture) {

			// 	// Clean up resources and configure Texture.
			// 	if (isObjectURL === true) {

			// 		URL.revokeObjectURL(sourceURI);

			// 	}

			// 	texture.flipY = false;
			// 	if (textureDef.name) texture.name = textureDef.name; // When there is definitely no alpha channel in the texture, set THREE.RGBFormat to save space.

			// 	if (!hasAlpha) texture.format = THREE.RGBFormat;
			// 	const samplers = json.samplers || {};
			// 	const sampler = samplers[textureDef.sampler] || {};
			// 	texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || THREE.LinearFilter;
			// 	texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.LinearMipmapLinearFilter;
			// 	texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping;
			// 	texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.RepeatWrapping;
			// 	parser.associations.set(texture, {
			// 		type: 'textures',
			// 		index: textureIndex
			// 	});
			// 	return texture;

			// });

			// 取得图片的原生blob数据
			if (source.bufferView !== undefined) {
				sourceURI = parser.getDependency('bufferView', source.bufferView).then(function (bufferView) {
					// 这个bufferView就是 ArrayBuffer 类型
					console.log("bufferview", bufferView)

					return new Promise((resolve, reject) => {
						// 创建一个 Uint8Array 来引用 ArrayBuffer
						const data = new Uint8Array(bufferView);

						const png = new PNG(data);

						let pixelBytes = png.pixelBitlength / 8
						console.log("png size", png.width, png.height, pixelBytes)

						let imgFormat = THREE.RGBFormat; // 默认设置为 RGB 格式
						switch (pixelBytes) {
							case 1: imgFormat = THREE.LuminanceFormat; break; // 灰度图像
							case 2: imgFormat = THREE.LuminanceAlphaFormat; break; // 灰度图像带 Alpha 通道
							case 3: imgFormat = THREE.RGBFormat; break; // RGB 图像
							case 4: imgFormat = THREE.RGBAFormat; break; // RGBA 图像
							default: imgFormat = THREE.RGBFormat; break; // 默认设置为 RGB 格式
						}
						const width = png.width; // 宽度
						const height = png.height; // 高度

						// 创建DataTexture
						var texture = new THREE.DataTexture(
							png.decodePixels(),  // 包含像素数据的Uint8Array
							width, // 纹理宽度
							height, // 纹理高度
							imgFormat, // THREE.RGBAFormat, // 数据格式
							THREE.UnsignedByteType, // THREE.UnsignedByteType, // 数据类型
							THREE.UVMapping, // UV映射模式
							THREE.ClampToEdgeWrapping, // wrapping模式
							THREE.ClampToEdgeWrapping, // wrapping模式
							THREE.LinearFilter, // minFilter
							THREE.LinearFilter // magFilter
						);
						texture.flipY = false;
						texture.encoding = THREE.LinearEncoding; //THREE.LinearEncoding THREE.RGBEEncoding THREE.sRGBEncoding
						texture.generateMipmaps = true;
						texture.needsUpdate = true; // 重要:通知 three.js 更新纹理

						console.log("---", texture)
						resolve(texture)
					});
				});
			}

			return Promise.resolve(sourceURI).then(function (texture) {
				parser.associations.set(texture, {
					type: 'textures',
					index: textureIndex
				});

				console.log("the texture", texture)
				return texture;
			})

		}

上面的使用了pngjs,有点麻烦,而且还不支持其他格式的图片(比如jpeg)。经过一通测试,发现可以用canvas绘制再获取的方式避开cors的限制。请把loadTextureImage(textureIndex, source, loader)更改成下面这样:

loadTextureImage(textureIndex, source, loader) {
			const parser = this;
			const json = this.json;
			const options = this.options;
			const textureDef = json.textures[textureIndex];
			const URL = self.URL || self.webkitURL;
			let sourceURI = source.uri;
			let isObjectURL = false;
			let hasAlpha = true;
			if (source.mimeType === 'image/jpeg') hasAlpha = false;

			if (source.bufferView !== undefined) {
				// Load binary image data from bufferView, if provided.
				sourceURI = parser.getDependency('bufferView', source.bufferView).then(function (bufferView) {
					if (source.mimeType === 'image/png') {

						// Inspect the PNG 'IHDR' chunk to determine whether the image could have an
						// alpha channel. This check is conservative — the image could have an alpha
						// channel with all values == 1, and the indexed type (colorType == 3) only
						// sometimes contains alpha.
						//
						// https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_header
						const colorType = new DataView(bufferView, 25, 1).getUint8(0, false);
						hasAlpha = colorType === 6 || colorType === 4 || colorType === 3;

					}

					isObjectURL = true;
					const blob = new Blob([bufferView], {
						type: source.mimeType
					});
					sourceURI = URL.createObjectURL(blob);

					return sourceURI;
				});
			} else if (source.uri === undefined) {
				throw new Error('THREE.GLTFLoader: Image ' + textureIndex + ' is missing URI and bufferView');
			}

			return Promise.resolve(sourceURI).then(function (sourceURI) {

				// return new Promise(function (resolve, reject) {

				// 	let onLoad = resolve;

				// 	if (loader.isImageBitmapLoader === true) {

				// 		onLoad = function (imageBitmap) {

				// 			resolve(new THREE.CanvasTexture(imageBitmap));

				// 		};

				// 	}

				// 	loader.load(resolveURL(sourceURI, options.path), onLoad, undefined, reject);

				// 	// 就在这里取代 textureloader 的操作
				// 	console.log("-------replace loader:",
				// 		source.uri)

				// });

				return new Promise(function (resolve, reject) {
					// 创建 Image 对象
					const image = new Image();

					// 设置 Image 对象的 src 属性
					image.src = resolveURL(sourceURI, options.path);

					console.log("blob url", sourceURI)

					// 创建 Canvas
					const canvas = document.createElement('canvas');
					const ctx = canvas.getContext('2d');

					// 监听 onload 事件
					image.onload = () => {
						console.log("image loaded", image)
						// 设置 Canvas 的大小与图像一致
						canvas.width = image.width;
						canvas.height = image.height;

						// 将图像绘制到 Canvas
						ctx.drawImage(image, 0, 0, image.width, image.height);

						// 获取解码后的 RGBA 数据
						const imageData = ctx.getImageData(0, 0, image.width, image.height);
						const rgbaData = imageData.data;

						// 因为这样得到的数据一定是rgba格式的,所有要将hasAlpha设置为true
						hasAlpha = true

						// 输出解码后的 RGBA 数据
						console.log('Decoded RGBA data:', rgbaData);

						// 创建DataTexture
						var texture = new THREE.DataTexture(
							rgbaData,  // 包含像素数据的Uint8Array
							image.width, // 纹理宽度
							image.height, // 纹理高度
							THREE.RGBAFormat, // THREE.RGBAFormat, // 数据格式
							THREE.UnsignedByteType, // THREE.UnsignedByteType, // 数据类型
							THREE.UVMapping, // UV映射模式
							THREE.ClampToEdgeWrapping, // wrapping模式
							THREE.ClampToEdgeWrapping, // wrapping模式
							THREE.LinearFilter, // minFilter
							THREE.LinearFilter // magFilter
						);
						texture.flipY = false;
						texture.encoding = THREE.LinearEncoding; //THREE.LinearEncoding THREE.RGBEEncoding THREE.sRGBEncoding
						texture.generateMipmaps = true;
						texture.needsUpdate = true; // 重要:通知 three.js 更新纹理

						console.log("---texture", texture)
						resolve(texture)
					};
				});

			}).then(function (texture) {

				// Clean up resources and configure Texture.
				if (isObjectURL === true) {
					URL.revokeObjectURL(sourceURI);
				}

				texture.flipY = false;
				if (textureDef.name) texture.name = textureDef.name; // When there is definitely no alpha channel in the texture, set THREE.RGBFormat to save space.

				if (!hasAlpha) texture.format = THREE.RGBFormat;
				const samplers = json.samplers || {};
				const sampler = samplers[textureDef.sampler] || {};
				texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || THREE.LinearFilter;
				texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.LinearMipmapLinearFilter;
				texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping;
				texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.RepeatWrapping;
				parser.associations.set(texture, {
					type: 'textures',
					index: textureIndex
				});

				console.log("---final texture:", texture)
				return texture;

			});

		}

4.总结

有很多坑及限制,但是确实能用。


参考
【html three.js 引入.stl模型示例】
【Three.js的3D显示】
【API that HTML5 controls can call to interact with IEC code】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值