-------------------------- Download: https://ry3yr.github.io/SampleWebMMD-master.zip Src: https://github.com/Momijinn/SampleWebMMD ===/js/main.js ===== let scene, renderer, camera, mesh, helper, cameraAnimation; let ready = false; const windowWidth = window.innerWidth; const windowHeight = window.innerHeight; const clock = new THREE.Clock(); function getQueryStringValue(key) { const urlParams = new URLSearchParams(window.location.search); return urlParams.get(key); } const pmx = getQueryStringValue('pmx'); const stage = getQueryStringValue('stage'); let Pmx; let Stage; if (pmx) { Pmx = `./pmx/pronama/${pmx.trim()}`; } else { console.log("No PMX selected."); } if (stage) { Stage = `./stages${stage.trim()}`; } else { console.log("No stage selected."); } if (!Pmx) { Pmx = `./pmx/pronama/AoiZaizen/AoiZaizen.pmx`; } if (!Stage) { Stage = "./stages/sorclair/sorclair.pmx"; } const MotionObjects = [ { id: "001", pose: "001", VmdClip: null, AudioClip: false }, { id: "002", pose: "002", VmdClip: null, AudioClip: false }, { id: "003", pose: "003", VmdClip: null, AudioClip: false }, { id: "bts-bestofme", pose: "bts-bestofme", VmdClip: null, AudioClip: true }, ]; window.onload = () => { Init(); LoadModeler(); Render(); }; function Init() { document.getElementById("moveLeftButton").addEventListener("click", moveCameraLeft); document.getElementById("moveRightButton").addEventListener("click", moveCameraRight); document.getElementById("moveUpButton").addEventListener("click", moveCameraUp); document.getElementById("moveDownButton").addEventListener("click", moveCameraDown); document.getElementById("rotaterightButton").addEventListener("click", rotateCameraRight); document.getElementById("rotateleftButton").addEventListener("click", rotateCameraLeft); function moveCameraLeft() { camera.position.x -= 1; } function moveCameraRight() { camera.position.x += 1; } function moveCameraUp() { camera.position.y += 1; } function moveCameraDown() { camera.position.y -= 1; } function rotateCameraRight() { mesh.rotateY(Math.PI / 4); } function rotateCameraLeft() { mesh.rotateY(-Math.PI / 4); } scene = new THREE.Scene(); const ambient = new THREE.AmbientLight(0xeeeeee); scene.add(ambient); renderer = new THREE.WebGLRenderer({ alpha: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(window.innerWidth, window.innerHeight); renderer.setClearColor(0xcccccc, 0); document.body.appendChild(renderer.domElement); camera = new THREE.PerspectiveCamera(100, windowWidth / windowHeight, 1, 1000); camera.position.set(0, 19, 20); } async function LoadModeler() { const loader = new THREE.MMDLoader(); function LoadPMX() { return new Promise(resolve => { loader.load(Pmx, object => { mesh = object; scene.add(mesh); resolve(true); },);});} function LoadVMD(id) { return new Promise(resolve => { const path = "./vmd/" + id + ".vmd"; const val = MotionObjects.findIndex(MotionObject => MotionObject.id == id); loader.loadAnimation(path, mesh, vmd => { vmd.name = id; MotionObjects[val].VmdClip = vmd; //newid = id; //console.log(newid); document.getElementById('readystate').innerHTML = "Checking motion vmd - " + id + ' (if stuck, click <a href="js/main.js" target="_blank">here</a>)'; resolve(true); },);});} function LoadCamera(id) { let camid; if (new URLSearchParams(window.location.search).has('camera')) { camid = new URLSearchParams(window.location.search).get('camera'); } else { camid = localStorage.getItem('camid'); if (!camid) { camid = 'bts-bestofme'; } } const path = "./camera/" + camid + ".vmd"; return new Promise((resolve, reject) => { loader.loadAnimation(path, camera, cameraAnimation => { cameraAnimation.name = path; resolve(cameraAnimation); console.log(path); }, onProgress, (xhr) => reject(new Error(`Failed to load Camera VMD file: ${path}`))); }); } function LoadAudio(id) { return new Promise(resolve => { const path = "./audio/" + id + ".mp3"; const val = MotionObjects.findIndex(MotionObject => MotionObject.id == id); if (MotionObjects[val].AudioClip) { new THREE.AudioLoader().load(path, buffer => { const listener = new THREE.AudioListener(); const audio = new THREE.Audio(listener).setBuffer(buffer); MotionObjects[val].AudioClip = audio; document.getElementById('readystate').innerHTML = "Checking audio - " + id + ' (if stuck, click <a href="audio" target="_blank">here</a>)'; resolve(true); },); } else { resolve(false);}});} const loadAdditionalPMX = () => { return new Promise(resolve => { loader.load(Stage, object => { const additionalMesh = object; scene.add(additionalMesh); resolve(true); const loadAdditionalPMX = () => { return new Promise(resolve => { loader.load(Stage, object => { const additionalMesh = object; scene.add(additionalMesh); resolve(true); }); }); }; let path; //preperations for 001.vmd and 002.vmd try {path = `./stages${stage.trim()}`;} catch (e) { path = '/default/default';} const lastIndex = path.lastIndexOf("/"); const basePath = path.substring(0, lastIndex); const vmd1 = `${basePath}/001.vmd`; const vmd2 = `${basePath}/002.vmd`; console.log(vmd1); console.log(vmd2); let mixer; const url = window.location.href; const isAnimated = url.indexOf('animated') !== -1; if (isAnimated) { function createMixer() { mixer = new THREE.AnimationMixer(object); } function playAnimation(animationClip) { const action = mixer.clipAction(animationClip); action.play(); } loader.loadAnimation(vmd1, object, (animationClip) => { createMixer(); playAnimation(animationClip); }); loader.loadAnimation(vmd2, object, (animationClip) => { playAnimation(animationClip); }); function animate() { requestAnimationFrame(animate); const deltaTime = clock.getDelta(); if (mixer) { mixer.update(deltaTime); } renderer.render(scene, camera); } animate(); } },);});}; await Promise.all([loadAdditionalPMX()]); await LoadPMX(); await Promise.all(MotionObjects.map(async (MotionObject) => {return await LoadVMD(MotionObject.id);})); await Promise.all(MotionObjects.map(async (MotionObject) => {return await LoadAudio(MotionObject.id);})); cameraAnimation = await LoadCamera("camera-animation-id"); // Ensure to load camera animation VmdControl("loop", true);} function VmdControl(id, loop) { const index = MotionObjects.findIndex(MotionObject => MotionObject.id == id); if (index === -1) { console.log("not Found ID"); //document.getElementById('readystate').textContent = "Default Camera: ready"; document.getElementById('readystate').textContent = "Camera: ready - " + localStorage.getItem('camid'); return; } ready = false; helper = new THREE.MMDAnimationHelper({ afterglow: 2.0, resetPhysicsOnLoop: true }); const enablePhysics = localStorage.getItem("physicsareon") === "true"; helper.add(mesh, { animation: MotionObjects[index].VmdClip, physics: enablePhysics }); if (MotionObjects[index].AudioClip) { MotionObjects[index].AudioClip.play(); } if (cameraAnimation) { helper.add(camera, {animation: cameraAnimation}); } const mixer = helper.objects.get(mesh).mixer; if (!loop) { mixer.existingAction(MotionObjects[index].VmdClip).setLoop(THREE.LoopOnce); } mixer.addEventListener("loop", (event) => { console.log("loop"); }); mixer.addEventListener("finished", (event) => { console.log("finished"); VmdControl("loop", true); }); ready = true; } function onProgress(xhr) { if (xhr.lengthComputable) { const percentComplete = xhr.loaded / xhr.total * 100; console.log(Math.round(percentComplete, 2) + '% downloaded'); } } function onError(xhr) { console.log("ERROR", xhr); } function Render() { requestAnimationFrame(Render); renderer.clear(); renderer.render(scene, camera); if (ready) { helper.update(clock.getDelta()); } } const generatePoseClickEvent = (motionObjects) => { const functionBody = motionObjects.map(function (motion) { return "case '" + motion.pose + "':\n" + " VmdControl('" + motion.id + "', " + (motion.pose === "pose1") + ");\n" + " break;\n"; }).join(""); const poseClickEventFunction = new Function("id", "switch (id) { " + functionBody + " default: VmdControl('001', true); break; }"); return poseClickEventFunction; }; const PoseClickEvent = generatePoseClickEvent(MotionObjects); console.log(PoseClickEvent); ===/index.html== <div id="readystate">...</div> <button onclick="togglePhysicsAndReload()">Toggle Physics</button> <script> function togglePhysicsAndReload() { var physicsAreOn = localStorage.getItem("physicsareon"); if (physicsAreOn) { localStorage.removeItem("physicsareon"); } else { localStorage.setItem("physicsareon", "true");} location.reload();} var physicsAreOn = localStorage.getItem("physicsareon"); if (physicsAreOn) { document.querySelector("button").textContent = "Physics On"; } else { document.querySelector("button").textContent = "Physics Off";} </script> <div class="keyboard"> <button id="." onclick="togglePhysicsAndReload()" style="background-color: transparent; border: none;color: transparent;">Move L3.__</button> <button id="moveUpButton">Move Up</button><br> <button id="moveLeftButton">Move Left</button> <button id="moveDownButton">Move Down</button> <button id="moveRightButton">Move Right</button> <button id="." style="background-color: transparent; border: none;color: transparent;">.__</button> <button id="rotateleftButton">RotateLeft</button> <button id="rotaterightButton">RotateRight</button> <a target="_blank" href="https://www.youtube.com/playlist?list=PLBva3abEZvyT-_ajETBGeOCGBA_AFBT5Z#https://www.reddit.com/r/mikumikudance" style=color:blue>r/MMD</a> <a target="_blank" href="https://codepen.io/ryedai1/pens/tags/?selected_tag=mikumikudance" style=color:gray>(tools)</a> </div> <details> <input type="text" id="buttonvmdInput" value="001" style="width: 100px;"> <button type="button" onclick="copyToClipboard()">Copy</button> <button type="button" onclick="PoseClickEvent(document.getElementById('buttonvmdInput').value)">cstm</button> <button type="button" onclick="localStorage.setItem('camid', document.getElementById('buttonvmdInput').value); PoseClickEvent(document.getElementById('buttonvmdInput').value);">cstm+cam</button> <input type="checkbox" id="checkboxSendToPHP"> <label for="checkboxSendToPHP">Send to PHP</label> <!--CopytoClipboard---Code-----> <script> function copyToClipboard() { var inputText = document.getElementById("buttonvmdInput").value; var outputText = '{ id: "' + inputText + '", pose: "' + inputText + '", VmdClip: null, AudioClip: false },'; if (document.getElementById("checkboxSendToPHP").checked) { // Send the output text to mainvmdwrite.php var url = "mainvmdwrite.php"; var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function() { if (xhr.readyState === 4 && xhr.status === 200) { alert("Text sent to mainvmdwrite.php!"); }}; xhr.send("outputText=" + encodeURIComponent(outputText)); } else { navigator.clipboard.writeText(outputText) .then(function() { alert("Text copied to clipboard!");}) .catch(function(error) { console.error("Error copying text: ", error); });}} </script> </details> <script> document.addEventListener('DOMContentLoaded', function() { if (window.self !== window.top) { var copyButton = document.querySelector('button[onclick="copyToClipboard()"]'); copyButton.disabled = true; copyButton.addEventListener('mouseover', function() { var tooltip = document.createElement('span'); tooltip.innerText = 'Disabled by CORS Policy';tooltip.style.position = 'absolute';tooltip.style.backgroundColor = '#000';tooltip.style.color = '#fff';tooltip.style.padding = '5px'; tooltip.style.borderRadius = '4px';tooltip.style.fontSize = '12px';tooltip.style.visibility = 'visible';tooltip.style.top = copyButton.offsetTop + copyButton.offsetHeight + 'px'; tooltip.style.left = copyButton.offsetLeft + 'px';document.body.appendChild(tooltip);}); copyButton.addEventListener('mouseout', function() { var tooltip = document.querySelector('span'); tooltip.parentNode.removeChild(tooltip);});}}); </script> <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> </head> <div class="pose"> <button type="button" value="001" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">vm1</button> <button type="button" value="002" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">vm2</button> <button type="button" value="003" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">vm3(camoff)</button> <button type="button" value="004walknthink" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">vm4</button> <button type="button" value="005hard-carry" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">tst(audio)</button> <!--set audio to "true" in main.js and have a 005hard-carry.mp3 in audio folder"--> <button type="button" value="bts-bestofme" onclick="localStorage.setItem('camid', this.value);PoseClickEvent(this.value);">best(audio)</button> <!--<button id="loadButton" value="./vmd/002.vmd">Load VMD File</button>--> <a href="?pmx=YusakuFujiki/yusaku.pmx">Yu</a> <a href="?pmx=AoiZaizen/AoiZaizen.pmx">Aoi</a> <a href="?pmx=Xion/xion.pmx">Xion</a> <a href="?pmx=2b/na_2b_0418n.pmx">2B</a> <a href="?pmx=TLoZ_Zelda_(Scholar)/zelda_scholar.pmx">BOTW Zelda</a> <a href="?pmx=TLoZ_Link_BOTW)/linkbotw.pmx">BOTW Link</a> <a href="?pmx=kh_roxas/kh_roxas.pmx">Roxas</a> <a href="?pmx=kh_terra/Casual/Terra%20[Casual].pmx">Terra(KHBBS)</a> <a href="?pmx=kh_aqua/Casual%20Aqua/Aqua%20[Casual%20Skirt].pmx">Aqua(KHBBS)</a> </div> <div class="stage"> <a href="javascript:(function() {var currentUrl = window.location.href;var updatedUrl = currentUrl.replace(/(\?|&)stage=([^&]*)/g, '') + (currentUrl.indexOf('?') > -1 ? '&' : '?') +'stage=/herogarden/herogarden.pmx';window.location.href = updatedUrl;})()">herogarden</a> <a href="javascript:(function() {var currentUrl = window.location.href;var updatedUrl = currentUrl.replace(/(\?|&)stage=([^&]*)/g, '') + (currentUrl.indexOf('?') > -1 ? '&' : '?') +'stage=/parkavenue/parkavenue.pmx';window.location.href = updatedUrl;})()">parkavenue</a> <a href="javascript:(function() {var currentUrl = window.location.href;var updatedUrl = currentUrl.replace(/(\?|&)stage=([^&]*)/g, '') + (currentUrl.indexOf('?') > -1 ? '&' : '?') +'stage=/livestageclub/livestageclubanimated.pmx';window.location.href = updatedUrl;})()">LiveStage</a> </div> <script src="./libs/three.js"></script> <script src="./libs/mmdparser.min.js"></script> <script src="./libs/ammo.min.js"></script> <script src="./libs/TGALoader.js"></script> <script src="./libs/MMDLoader.js"></script> <script src="./libs/MMDAnimationHelper.js"></script> <script src="./libs/mmd-loader.min.js"></script> <script src="./libs/CCDIKSolver.js"></script> <script src="./libs/MMDPhysics.js"></script> <!--<script src="./libs/Vmd.js"></script> <script src="./libs/VmdFileParser.js"></script>--> <!--<script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/build/three.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/libs/mmdparser.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/ammo.js@0.0.10/ammo.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/loaders/TGALoader.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/loaders/MMDLoader.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/animation/MMDAnimationHelper.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/animation/CCDIKSolver.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r106/examples/js/animation/MMDPhysics.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/jsm/controls/OrbitControls.js"></script> --> <script src="./js/main.js"></script> </body> </html> </plaintext>