Litomyšl - Semestrální projekt A7B39MVR FEL ČVUT

Členové týmu:

Scénář modelované krajiny

Jako inspiraci k našemu semestrálnímu projektu jsme si vybrali Smetanovo náměstí v Litomyšli. Vybrali jsme se toto místo pro jeho unikátnost a malebnost. Toto náměstí je proslavené svou architekturou, která mu dodává jedinečnou charakteristiku stejně jako jeho ústřední věž.

Fáze projektu:

Fáze 0 - Sběr podkladů

Pro vytvoření dostatečně detailních modelů budov a dalších prvků obklopujících náměstí jsme se ho snažili přesně nafotit, abychom se dále rozhodli pro další postup. Během projektu je možné že se rozhodneme upravit či změnit některé modely, neboť stávající rozhodnutí nebude dostačující pro tento předmět či nebude naplňovat naše představy.

Fotodokumentace objektů

Zde můžeme nalézt základní fotodokumentaci Smetanova náměstí, které se budeme snažit zvirtualizovat během tohoto semestru.

Vybrané interaktivní prvky

Tyto prvky byly vybrány na základě jejich možností pro určtou interaktivitu při jejich přiblížení, či aktivací pomocí mouse-overu či jiné akce.

Budovy vybrané pro generaci gramatiky

Dané budovy byly vybrány jako základy pro jejich vymodelování pomocí gramatik a jejich variací kolem náměstí.

Budovy vybrané pro 3D rekonstrukci v programu Google SketchUp

Dané budovy byly vybrány jako vzory pro 3D rekonstrukci v programu od společnosti Google, využijeme několik možných pohledů pro přesnější model.

Objekty vybrané pro 3D rekonstrukci

Objekty jako lampa pouličního osvětlení, lavička a parkovací automat byly vybrány pro 3D rekonstrukci, bude však potřeba dodat i další detailnější fotografie.

Zpět k menu ↑

Fáze 1 - Vymodelovaná scéna s modely gramatiky, 3D rekonstrukcemi a interaktivními objekty

Celková scéna 1.fáze

Náhled na tento model ve fullscreen v novém tabu

Náhled na model

Screen

Geometrie

  • FPS (HD_LOD): 3-13 fps
  • FPS (LD_LOD): 23-26 fps

Problémy této fáze:

Jednotlivé objekty obsažené ve scéně

Zde je možné stáhnout všechny soubory k celkové scéně.       Celková VRML scéna ke stažení

Zpět k menu ↑

Fáze 2 - Scéna z fáze 1 převeda do X3D

Nebyly přidány šipky, neboť způsobovaly nestabilitu celé scény. Avšak byly vytvořeny viz.

Statický náhled na scénu

Screen

Náhled na tuto dynamickou scénu ve fullscreen v novém tabu

Javascriptové soubory ke stažení

Náhled na Javaskriptové kódy:

Main.JS

Hlavní skript ovládající scénu a dynamické akce.

function init(){
var hydrant = new initT('Hydrant2');
var hydrant2 = new initT('Hydrant3');
listenerT(hydrant);
listenerT(hydrant2);

var parkomat = new initT('Parkomat2');
var parkomat2 = new initT('Parkomat3');
listenerT(parkomat);
listenerT(parkomat2);

var kytka = new initT('Kytka1');
listenerT(kytka);

var lampa = new initLampa('Lampa2');
var lampa2 = new initLampa('Lampa3');
listenerLampa(lampa);
listenerLampa(lampa2);

var vlajka=new initVlajka('Vlajka1');
listenerVlajka(vlajka);


var kobra=new initKobra('Kobra1');
listenerKobra(kobra);

ghosts=[
new GhostTouch(hydrant),
new GhostTouch(hydrant2),
new GhostTouch(parkomat),
new GhostTouch(parkomat2),
new GhostTouch(kytka),
new GhostTouch(lampa),
new GhostTouch(lampa2),
new GhostTouch(kobra),
new GhostHover(vlajka)];
gt=new initGhostsToogle(ghosts);

listenerGhostsToogle(gt);


time=new initTime();
initTimeToggle(time);
}

function initGhostsToogle(aa){
this.st=false;
this.fn=function(){
getRandom(aa).method();
}
}

function listenerGhostsToogle(gt){
el=document.getElementById('ghostsToggle');
el.addEventListener('click',function(){
console.log('click');
if(gt.st){
gt.st=false;
clearInterval(gt.int);
}else{
gt.st=true;
gt.int=setInterval(gt.fn,5000);
}
});
}

function GhostTouch(element){
this.method=function(){
eventFire(element.element, 'click');
}
}

function GhostHover(element){
this.st=false;
var me=this;
this.method=function(){
if(me.st){
me.st=false;
eventFire(element.element, 'mouseout');
}else{
me.st=true;
eventFire(element.element, 'mouseover');
}
}
}

function getRandom(elements){
return elements[Math.floor(Math.random() * elements.length)];
}

function initTime(){
this.day=0;
this.hour=0;
this.sun=document.getElementById('lit__DLL');
this.minute=0;
this.hud=document.getElementById('time');
var me=this;
this.func=function(){
me.minute=me.minute+12;
if(me.minute>60){
me.hour++;
me.minute=me.minute%60;
}
if(me.hour>=24){
me.day++;
me.hour=me.hour%24;
}
o=((60*(me.hour+18)+me.minute)/24/60)*2*Math.PI;
me.sun.setAttribute('direction',[-Math.cos(o),-Math.sin(o),0].join());
setTime(me.hud,me.day,me.hour,me.minute);
}
this.t=setInterval(this.func,1000);
}

function initTimeToggle(t){
paused=false;
document.getElementById('timeToggle').addEventListener('click',function(){
if(paused){
paused=false;
t.t=setInterval(t.func,1000);
}else{
paused=true;
clearInterval(t.t)
}
});
}

function setTime(hud,day,hour, minute){
hud.innerHTML=String(day)+', '+pad(hour,2)+':'+pad(minute,2);
}

function pad(num, size) {
var s = num+"";
while (s.length < size) s = "0" + s;
return s;
}

function initKobra(id){
this.element=document.getElementById(id);
this.or=getInlinedElement(id,'Or');
this.t=[0,1,0,0];
}

function listenerKobra(h){
h.element.addEventListener('click',function(){
vals=h.or.getAttribute('destination').split(',');
h.t[3]=h.t[3]+1.54;
console.log(h.or.getAttribute('destination'));
h.or.setAttribute('destination',h.t.join());
});
}

function initVlajka(id){
this.element=document.getElementById(id);
this.start=getInlinedElement(id,'TiS');
this.sound=getInlinedElement(id,'AC');
this.url=this.sound.getAttribute('url');
this.sound.setAttribute('loop','true')
this.start.setAttribute('startTime',(new Date()).getTime()/1000);
this.start.setAttribute('pauseTime',(new Date()).getTime()/1000);
this.sound.setAttribute('pauseTime',(new Date()).getTime()/1000);
this.sound.setAttribute('url','')
}

function listenerVlajka(h){
h.element.addEventListener('mouseover',function(){
h.sound.setAttribute('url',h.url)
h.start.setAttribute('resumeTime',(new Date()).getTime()/1000);
h.sound.setAttribute('resumeTime',(new Date()).getTime()/1000);
});
h.element.addEventListener('mouseout',function(){
console.log('out')
h.sound.setAttribute('url','')
h.start.setAttribute('pauseTime',(new Date()).getTime()/1000);
h.sound.setAttribute('pauseTime',(new Date()).getTime()/1000);
});
}

function initT(id){
this.element=document.getElementById(id);
this.start=getInlinedElement(id,'TiS');
this.sound=getInlinedElement(id,'AC');
this.url=this.sound.getAttribute('url');
this.sound.setAttribute('pauseTime',(new Date()).getTime()/1000);
this.sound.setAttribute('url','');
}

function listenerT(h){
h.element.addEventListener('click', function() {
console.log('click')
h.start.setAttribute('startTime',(new Date()).getTime()/1000);
h.sound.setAttribute('url',h.url)
h.sound.setAttribute('stopTime',(new Date()).getTime()/1000);
h.sound.setAttribute('startTime',(new Date()).getTime()/1000);
});
}

function initLampa(id){
this.element=document.getElementById(id);
this.start=getInlinedElement(id,'TiS');
this.sound=getInlinedElement(id,'AC');
this.light=getInlinedElement(id,'SL');
this.color=getInlinedElement(id,'Color_low');
this.color2=getInlinedElement(id,'Color_high');
this.url=this.sound.getAttribute('url');
this.sound.setAttribute('pauseTime',(new Date()).getTime()/1000);
this.sound.setAttribute('url','');
this.start_color=this.color.getAttribute('diffuseColor');
this.active=false;
}

function listenerLampa(h){
h.element.addEventListener('click', function() {
console.log('click')
if(h.active){
h.light.setAttribute('on',false);
h.color.setAttribute('diffuseColor',h.start_color);
h.color2.setAttribute('diffuseColor',h.start_color);
h.active=false;
}
else{
h.start.setAttribute('startTime',(new Date()).getTime()/1000);
h.sound.setAttribute('url',h.url);
h.sound.setAttribute('stopTime',(new Date()).getTime()/1000);
h.sound.setAttribute('startTime',(new Date()).getTime()/1000);
h.light.setAttribute('on',true);
h.active=true;
}
});
}

function eventFire(el, etype){
if (el.fireEvent) {
(el.fireEvent('on' + etype));
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}

Protos.JS

Zpracovává prota do scény.

function processProtos(x3dNode) {
var protos = x3dNode.getElementsByTagName('ExternProtoDeclare');
if (protos.length === 0) {
protos = x3dNode.getElementsByTagName('externprotodeclare');
}
console.log(protos.length, 'Externproto declare found');
for (var i = 0; i < protos.length; i++) {
var proto = loadExternProto(protos[i]);
if (proto) {
//console.log('new extern proto',i,proto);
protos[i].parentNode.appendChild(proto);
protos[i].parentNode.removeChild(protos[i]);
i--;
}
}
protos = x3dNode.getElementsByTagName('ProtoDeclare');
if (protos.length === 0) {
protos = x3dNode.getElementsByTagName('protodeclare');
}
console.log(protos.length, 'proto declare found');
for (var i = 0; i < protos.length; i++) {
loadProto(x3dNode, protos[i]);
}
console.log(x3dNode.nodeName, x3dNode.childNodes.length);
}

function loadExternProto(externproto) {
var protoUrl = externproto.getAttribute('url');
protoUrl = protoUrl.replace(/"/g, '');
var idx = protoUrl.indexOf("#");
var name = protoUrl.substr(idx + 1);
protoUrl = protoUrl.substr(0, idx);
console.log('loading', protoUrl, name);
var p = null;
var scene = loadX3DScene(protoUrl);
console.log('loaded', scene);
if (scene !== undefined && scene) {
var protos = scene.getElementsByTagName('ProtoDeclare');
if (protos.length === 0) {
protos = scene.getElementsByTagName('protodeclare');
}
for (var i = 0; i < protos.length; i++) {
if (protos[i].getAttribute('name') === name) {
p = protos[i].cloneNode(true);
p.setAttribute('name', externproto.getAttribute('name'));
updateExternProtoUrl(p, protoUrl);
break;
}
}
}
//console.log(p);
return p;
}

function updateExternProtoUrl(proto, url) {
var idx = url.lastIndexOf('/');
if (idx === -1)
return;
var baseUrl = url.substr(0, idx);
var protos = proto.getElementsByTagName('ExternProtoDeclare');
if (protos.length === 0) {
protos = proto.getElementsByTagName('externprotodeclare');
}
for (var i = 0; i < protos.length; i++) {
var protoUrl = protos[i].getAttribute('url'), base = baseUrl;
protoUrl = protoUrl.replace(/"/g, '');
console.log(protoUrl);
protoUrl = base.concat(((protoUrl.charAt(0) === '/') ? '' : '/'), protoUrl);
console.log('Updated externproto url', protoUrl);
protos[i].setAttribute('url', protoUrl);
}
var inl = proto.getElementsByTagName('inline');
if (inl.length === 0) {
inl = proto.getElementsByTagName('Inline');
}
for (var i = 0; i < inl.length; i++) {
var inlUrl = inl[i].getAttribute('url'), base = baseUrl;
inlUrl = inlUrl.replace(/"/g, '');
console.log(inlUrl);
protoUrl = base.concat(((inlUrl.charAt(0) === '/') ? '' : '/'), inlUrl);
console.log('Updated inline url', inlUrl);
inl[i].setAttribute('url', inlUrl);
}
}

function loadProto(x3dNode, proto) {
var protoI = getProtoInterface(proto);

//najdu v dokumentu reference na objekt a zacne nahrazovani
protoInstances = x3dNode.getElementsByTagName(protoI.name);

console.log('protoI.name: ' + protoI.name, protoInstances.length + ' instances');

for (var j = 0; j < protoInstances.length; j++) {
//console.log(j+" "+protoInstances[j]);
//console.log(j, protoInstances[j]);
var g = document.createElement('Group');

if (protoInstances[j].hasAttribute("USE") || protoInstances[j].hasAttribute("use")) {
g.setAttribute("USE", protoInstances[j].getAttribute("USE"));
protoInstances[j].parentNode.appendChild(g);
continue;
}
var protoVal = prepareRealProtoValues(protoI, protoInstances[j]);
if ('DEF' in protoVal) {
g.setAttribute("DEF", protoVal['DEF']);
}
if ('id' in protoVal) {
g.setAttribute("id", protoVal['id']);
} else {
g.setAttribute("id", protoI.name + j);
}
var protoBody = proto.getElementsByTagName('ProtoBody')[0];
//console.log(j, 'body',protoBody);

for (k = 0; k < protoBody.childNodes.length; k++) {
//testuji zda mam doopravdy X3Dom node
if (protoBody.childNodes[k].nodeType === Node.ELEMENT_NODE) {
//console.log(k, protoBody.childNodes[k]);
var element = protoBody.childNodes[k].cloneNode(true);

replaceProtoValues(element, protoVal);

setNamespace(protoI.name + j, element, true);
g.appendChild(element);
}
}
protoInstances[j].parentNode.appendChild(g);
}
//*
while (protoInstances.length > 0) {
protoInstances[0].parentNode.removeChild(protoInstances[0]);
}//*/
}

//precte zahlavi PROTOtypu a pripravi si jsObject pro snadnejsi praci
function getProtoInterface(proto) {
var prI = {}; //protoInterface object

prI.name = proto.getAttribute('name');

protoI = proto.getElementsByTagName('ProtoInterface');
protoIfields = protoI[0].getElementsByTagName('field');

for (var j = 0; j < protoIfields.length; j++) {
//POZOR - toLowerCase je tu proto, ze u instanci jsou z nejakeho neznameho duvodu jmena atributu malym pismem - podobne v replaceProtoValues()
var attrName = protoIfields[j].getAttribute("name").toLowerCase();
prI[attrName] = {};
prI[attrName].value = protoIfields[j].getAttribute("value");
prI[attrName].accessType = protoIfields[j].getAttribute("accessType");
}
//console.log(prI);
return prI;
}

//udela kopii zahlavi prototypu a zmeny atributy dle pozadavku instance
function prepareRealProtoValues(protoInterface, instance) {
var prI = jQuery.extend(true, {}, protoInterface); //hluboka kopie objektu - vznikla mi tu zavislost na jquery !
//var prI = JSON.decode(JSON.encode(protoInterface)); //hluboka kopie objektu - asi by se melo resit jinak !

//console.log(prI);
for (var i = 0; i < instance.attributes.length; i++) {
//POZOR - instance.attributes[i].name je z nejakeho neznameho duvodu malymi pismeny, proto jsou v nekterych funkcich attr.toLowerCase()...
if (instance.attributes[i].name.toUpperCase() === "DEF") {
prI['DEF'] = instance.attributes[i].value;
} else if (instance.attributes[i].name.toLowerCase() === "id") {
prI['id'] = instance.attributes[i].value;
} else {
prI[instance.attributes[i].name.toLowerCase()].value = instance.attributes[i].value;
}
}
return prI;
}

//nahradi v elementu atributy definovane pomoci IS a CONNECT ze ty v jsObjektu zahlaviPrototypu
function replaceProtoValues(element, protoInterface) {

isElem = element.getElementsByTagName('IS');
if (isElem.length === 0) {
isElem = element.getElementsByTagName('is');
}

//for (var j = 0; j < isElem.length; j++) {
while (isElem.length > 0) {
isElement = isElem[0];
isParent = isElement.parentNode;

connections = isElement.getElementsByTagName('connect');
for (var k = 0; k < connections.length; k++) {
//POZOR - toLowerCase je tu proto, ze u instanci jsou z nejakeho neznameho duvodu jmena atributu malym pismem - podobne v getProtoInterface()
var fieldName = connections[k].getAttribute('protoField').toLowerCase();
if (protoInterface.hasOwnProperty(fieldName)) { //pro jistotu si overime, ze ta vlastnost existuje v zahlavi
isParent.setAttribute(connections[k].getAttribute('nodeField'), protoInterface[fieldName].value);
}
}
isParent.removeChild(isElem[0]);
}

}

if (!Array.forEach) {
Array.forEach = function(array, fun, thisp) {
var len = array.length;
for (var i = 0; i < len; i++) {
if (i in array) {
fun.call(thisp, array[i], i, array);
}
}
};
}

function setNamespace(prefix, childDomNode, mapDEFToID) {
if (childDomNode instanceof Element && childDomNode.setAttribute !== undefined) {
if (childDomNode.hasAttribute('id')) {
childDomNode.setAttribute('id', prefix.toString().replace(' ', '') + '__' + childDomNode.getAttribute('id'));
}
if (childDomNode.hasAttribute('DEF')) {
childDomNode.setAttribute('DEF', prefix.toString().replace(' ', '') + '__' + childDomNode.getAttribute('DEF'));
if (mapDEFToID) {
childDomNode.setAttribute('id', childDomNode.getAttribute('DEF'));
}
} else if (childDomNode.hasAttribute('USE')) {
childDomNode.setAttribute('USE', prefix.toString().replace(' ', '') + '__' + childDomNode.getAttribute('USE'));
}
if (childDomNode.localName.toUpperCase() === 'ROUTE') {
childDomNode.setAttribute('fromNode',prefix.toString().replace(' ', '') + '__' + childDomNode.getAttribute('fromNode'));
childDomNode.setAttribute('toNode',prefix.toString().replace(' ', '') + '__' + childDomNode.getAttribute('toNode'));
}
}
if (childDomNode.hasChildNodes()) {
Array.forEach(childDomNode.childNodes, function(children) {
setNamespace(prefix, children, mapDEFToID);
});
}
}


function loadX3DScene(dname) {
//var xhttp = new window.XMLHttpRequest();
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
/*
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {

}
};
*/
if (xhttp.overrideMimeType) {
xhttp.overrideMimeType('text/xml');
}
xhttp.open("GET", dname, false);
xhttp.send();
var inlScene = null, xml = null;
if (navigator.appName != "Microsoft Internet Explorer") {
xml = xhttp.responseXML;
}

if (xml === null) {
xml = new DOMParser().parseFromString(xhttp.responseText, "text/xml");
}
//console.log(xml,xhttp.responseXML,xhttp.responseText);
if (xml !== undefined && xml !== null) {
inlScene = xml.getElementsByTagName('Scene')[0] || xml.getElementsByTagName('scene')[0];
}
return inlScene;
}



///extra functions

function getInlinedElement(inName, id) {
return document.getElementById(inName + '__' + id);
}

function info(node, s) {
//if (node.nodeValue)
if (node === null)
return;
var n = node.childNodes;//getElementsByTagName("*");
console.log(n.length, node.nodeName, node.parentNode.nodeName);
for (var i = 0; i < n.length; i++) {
console.log(s, n[i].nodeName, n[i].nodeValue);
info(n[i], ' ' + s);

//if (n[i].hasAttributes('id')) console.log(" ",n[i].getAttribute('id'));
}
}

Console logger uvnitř faze2.html

Zapisuje události do logu během zobrazování scény.

<script type="text/javascript">
var bg;
document.onload = function() {
var counter = 0;
document.getElementById('inID').addEventListener('load',function(ev) {
//info(this);
counter++;
console.log(counter,"inline onload start");
if (counter === 1) {

//copy children of inline to parent of inline
var el;
for (k = 0; k < this.childNodes.length; k++) {
//console.log(this.childNodes[k],this.childNodes[k].localName);
el = this.childNodes[k].cloneNode(true);
if (el.localName.toLowerCase() === 'background') {
bg = el;
el.setAttribute('set_bind','true');

} else
this.parentNode.appendChild(el);
}
// removes children from inline
// this.setAttribute('url','');
console.log("process protos", counter);

processProtos(this.parentNode);

console.log(ev,this.nodeName,this.childNodes.length);

//this.parentNode.removeChild(this);
init();

console.log("protos loaded", counter);
} else {
console.log(this.nodeName,this.childNodes.length);
}
console.log(counter,"inline onload end");
});

// return false;
};
</script>

Zpět k menu ↑