Para permitir a interação 3D ou para poderes fazer ajustes nas câmaras, na iluminação e outros, as opções de configuração individuais devem ser feitas através do campo de texto JSON no palco do menu de administração do klar.land. Explicamos-te como isto funciona em pormenor aqui.
![](https://klarland.com/wp-content/uploads/2022/05/Screenshot_4-1024x970.png)
Um código JSON mínimo pode ser gerado após o carregamento de um ficheiro glb, premindo o botão “Generate defaultjson” (o ficheiro glb deve conter pelo menos uma câmara com o nome “Defaultcam”).
O código JSON mínimo tem o seguinte aspeto (uma fase já funciona com isto). Este código pode ser alargado conforme desejado, utilizando a seguinte documentação.
{ "Stage3dData" :[ { "threedfile" : "Dateiname.glb", "outlinelevel" : 0, "lightsetup": 0, "spritescale": 1, "defaultqualitylevel": 0, "scenetype": "_3d" } ] }
(!) nota importante: nos seguintes fragmentos de código JSON, podem faltar parênteses em alguns casos ou os parâmetros são especificados apenas de forma geral, como [number]. Por conseguinte, recomenda-se que copies ou adaptes o código apenas a partir de JSONs funcionais das fases.
Necessidade de dados
Stage3dData tem de ser especificado:
Estrutura:
"Stage3dData" :[ { "threedfile" : "szenenname.glb", "outlinelevel" : [number], "perspectives":[ { "name":"Standard Kamera", "camera":"Defaultcam" } ], "lightsetup": [number], "spritescale": [number], "defaultqualitylevel": [number], "scenetype": [string] }, ], "AOMaps" : [ { "mesh":"Meshname", "aomap":"Texturname.jpg" } ],
Explica-te:
“threedfile” : “szenenname.glb” Nome do ficheiro da cena (atualmente só são suportados glbs)
“outlinelevel” : 0 se os objectos já devem receber o contorno (moldura azul para destacar os objectos clicáveis) ao carregar a cena, 1 se os objectos só devem receber o seu contorno depois de clicares no botão.
“perspectivas” : opcional, lista das câmaras que devem aparecer no menu
“lightsetup” : 0 utiliza apenas HDR, 1 utiliza HDR + Point + Arealight (atualmente não implementado)
“spritescale” : 1 a escala das texturas dos botões,
“defaultqualitylevel” : 0 = renderpass + outlinepass + fxaapass (compromisso de desempenho + aparência), 1 = ssaapass + outlinepass (melhor anti-aliasing, pior desempenho)
“tipo de cenário” : “3d” ou “360
"useCases" :[ { "name":"Usecasename", "group":"Usecasegroup", "animationlayer": [number], "state":[number], "trigger_objects":["triggerobject"], "cam": { "name": "camname", "position": [0, 0, 0], "rotation":[0, 0, 0], "lookatTarget": "lookatobject" }, "animations": [ { "name":"Objektname Anim01", "tc":0, "loop":"true" }, { "name":"Objektname Anim02", "tc":0 } ] },
“camname” deve terminar em cam
“lookatTarget” : “lookatobject” (se não for especificado, o objeto clicado é utilizado como lookattarget)
“name” : “[Objektname Anim01]” (tem de começar com o objeto de animação em glb, bem como em JSON, seguido de um espaço e depois o nome único da animação) // por exemplo, ‘flap_up_armature open’, se não for possível utilizar um espaço no nome da animação devido ao software (por exemplo, no 3dsMax), também pode ser utilizado um carácter %.
“tc” :[number] (código de tempo em segundos, a animação é reproduzida após o tempo especificado, ou seja, é apenas um atraso do início – a animação é reproduzida completamente)
“loop” : true (se especificado e verdadeiro, a animação é reproduzida em loop, ou seja, repete-se)
“autoplay” : true – se uma animação deve ser reproduzida automaticamente desde o início
Explicação das camadas/estados de animação
As camadas de animação são utilizadas principalmente para realçar objectos e atribuir quais os objectos que são realçados e seleccionáveis quando se clica em que objectos.
O estado é utilizado quando um objeto tem vários estados e deve reproduzir uma animação diferente consoante o estado.
z. B.:
Camada de animação 0: Não recebe qualquer destaque e é utilizada para os botões, ou seja, quando a câmara salta para um local sem interação.
Camada de animação 1: Destacada assim que o objeto da camada de animação 0 (o botão) do grupo é clicado.
Camada de animação 2: É activada assim que o objeto da camada de animação 0 do grupo é clicado.
Exemplo:
Camada de animação 0: salta para a gaveta 3
Camada de animação 1: abre a gaveta 3
Camada de animação 2: Reproduz a animação da faca clicada, que está na gaveta.
Os estados 0/1 são atualmente utilizados para alternar a animação:
Então:
Objeto no estado 0 por defeito, se for clicado, verifica se tem uma animação com o estado 1. Se for esse o caso, a animação é reproduzida e o objeto fica no estado 1.
Se voltares a clicar nele, a animação do estado 1 é reproduzida e o objeto volta ao estado 0.
Exemplo:
Estado 0: Abre a porta do armário dos cereais
Estado 1: Fecha a porta do armário dos cereais (editado)
Estão atualmente planeados/em curso mais estados para além de 0 e 1 e a sua interrogação.
Mais informações:
A matriz de animações pode ser inserida como animação numa matriz de animações. Esta sequência de animação pode depois ser repetida.
Cor de fundo
Define a cor de fundo:
{ "Stage3dData": [ { "backgroundColor": "rgb(81, 87, 99)", .....
Para além de rgb, a cor de fundo pode ser especificada da seguinte forma:
“rgb(255, 0, 0)”
“rgb(100%, 0%, 0%)”
Nome da cor X11 – são suportados todos os 140 nomes de cores.
“azul-celeste
Cadeia de caracteres //HSL
“hsl(0, 100%, 50%)”
Luz ambiente Hdr
Optativo.
Se não for especificado, é utilizado o hdr de estúdio predefinido: /files/stages/hdr/studio_01.hdr.
Em“hdr” –“name” tens a opção de especificar o nome do ficheiro Hdr para esta fase. Esta deve estar localizada no servidor em “/files/stages/hdr”. Envia o ficheiro ao teu gestor de conta klar.land, que o disponibilizará para ti.
Atualmente, oferecemos os seguintes HDRI padrão:
- adams_place_brigde_1k.hdr
- autoshop_01_1k.hdr
- comfy_cafe_1k.hdr
- gradiente_01.hdr
- quarto_do_hotel_1k.hdr
- shanghai_bund_1k.hdr
- garagem_skylit_1k.hdr
- studio_01.hdr
- studio_01_heller.hdr
- studio_small_06_1k.hdr
- studio_small_08_1k.hdr
- venice_sunset_1k.hdr
- branco.hdr
“intensidade” : intensidade da luz – pode ter de ser testada várias vezes.
Exemplo – JSON:
"Stage3dData" :[ { "threedfile" : "muellmaschiene.glb", "outlinelevel" : 0, "lightsetup": 0, "hdr":{ "name":"venice_sunset_1k.hdr", "intensity": 3 }, "spritescale": 1, "defaultqualitylevel": 0, "scenetype": "3d",
Chamar documentos HTML através de um clique no objeto
Pode ser criado na área de documentos de um caso de utilização:
Exemplo – JSON:
{ "cam": { "name": "rauchmelder01cam", "position": [ 0, 0, 0 ], "rotation": [ 0, 0, 0 ], "lookattarget": "rauchmelder01" }, "name": "rm", "group": "rm", "state": 0, "documents": [ { "name": "rauchmelder.html", "type": "html", "uuid": "d90701d8-0769-4ba6-b23e-327ca0637df1", "playonclick": true } ], "animationlayer": 0, "trigger_objects": [ "rauchmeldergruppe01" ] },
Chamar uma hiperligação através de um clique no objeto
Para este efeito, num caso de utilização
“gotourl” : Tens de introduzir o “URL”
(abre atualmente num novo separador)
Exemplo – Caso de utilização:
{ "name":"zb out", "group":"zb", "animationlayer": 0, "state":0, "gotourl":"https://www.klar.land", "trigger_objects":["zbgroup"], "cam": { "name": "zbcam", "position": [0, 0, 0], "rotation":[0, 0, 0] }, "animations": [ { "name":"zbgroup%out", "tc":0 } ] }
Material – Animações
Basicamente, podes criar um objeto MaterialAnimations Array em qualquer useCase:
“materialAnimations“: [{}] Podes introduzir os seguintes parâmetros:
name: nada precisa de ser considerado aqui, exceto que o nome é único e não aparece uma segunda vez no json.
ação: aqui deves especificar o tipo de animação do material a executar.
Os valores possíveis são:
changeMaterialValue
addMaterialValueTween
altera o valor da posição da textura
addTexturePositionValue
altera o valor do grau de rotação da textura
addTextureRotationDegreeValue
páraMaterialAnimation
A título de explicação:
A diferença entre adicionar e modificar é que com adicionar os valores desejados são adicionados aos valores antigos do material, com modificar os valores atuais são modificados para os valores especificados.
Isto é especialmente relevante para os loops.
Por exemplo, se uma textura tiver de se mover para a direita no eixo x, podes usar changeTexturePositionValue, por exemplo, de 0 para 2. Se especificares que isto deve ser feito como um ciclo, a textura mover-se-á da posição x=0 para x=2. No entanto, quando o ciclo é executado uma segunda vez, a textura já não se move porque é movida para x=2 mas já está nessa posição.
Se quiseres aumentar o valor em 1 por cada execução do ciclo, deves utilizar a ação “adicionar” apropriada. Isto permite-te ajustar os valores dos materiais, bem como rodar ou mover texturas. Com stopMaterialAnimation, podes parar a execução de MaterialAnimations. Isto é relevante, por exemplo, para animações que estão num ciclo infinito e que devem ser interrompidas.
Faz um laço:
Trata-se de um parâmetro facultativo.
Se não estiver definido, o materialAnimation é executado uma vez.
Se este parâmetro estiver definido e tiver o valor 0 ou true, o ciclo é infinito.
Se introduzires um valor numérico aqui, a animação é repetida tantas vezes quantas as especificadas no valor.
loop = 12 faz com que a animação do material seja repetida exatamente 12 vezes.
inicia_tc:
end_tc:
Aqui, o tempo de início e de fim da animação é definido em segundos (são possíveis casas decimais).
start_tc indica o momento em que a animação do material deve começar depois de o UseCase ter sido executado.
start_tc = 2 a animação começa com 2 segundos de atraso, com start_tc = 0 a animação começa imediatamente.
end_tc indica quando a animação deve parar.
A combinação de ambos os valores determina a velocidade a que a animação deve ser executada. Quanto mais próximos estiverem os valores de start_tc e end_tc, mais rapidamente a animação será reproduzida.
materialName:Especifica aqui o nome do material a ser animado.
materialproperty: determina qual a propriedade/textura do material que deve ser influenciada.
possíveis materialProperties:
opacidade
emissivoIntensidade
emissivo
aoMapIntensity
bumpScale
cor
displacementScale
envMapIntensity
intensidade do mapa de luz
metalização
rugosidade
texturas possíveis:
mapa
alphaMap
emissivoMapa
aoMap
bumpMap
envMap
deslocaçãoMapa
mapa de luz
metalnessMap
normalMapa
valor afetado:
Este é um parâmetro opcional que só precisa de ser especificado se as acções addTexturePositionValue ou changeTexturePositionValue forem especificadas.
Atualmente, x ou y podem ser especificados aqui.
Se uma textura se deve mover horizontalmente, introduz x, se se deve mover verticalmente, introduz y aqui.
Por exemplo: “affectedvalue”: “x”,
tweenValue
O valor numérico para o qual a propriedade selecionada deve ser definida.
Se for selecionada uma ação de rotação de textura, a rotação pretendida deve ser especificada em graus.
Por exemplo, roda a textura uma vez em 180 graus:
“tweenValue”: 180,
json Exemplo:
{ "useCases": [ { "name": "Start Opacity Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube0" ], "materialAnimations": [ { "loop": true, "name": "changeOpacity", "action": "changeMaterialValue", "end_tc": 10, "start_tc": 0, "tweenValue": 0, "materialName": "Material0", "materialproperty": "opacity" } ] }, { "name": "Start Diffuse Map Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube1" ], "materialAnimations": [ { "loop": 0, "name": "add diffuse pos", "action": "addTexturePositionValue", "end_tc": 2, "start_tc": 0, "tweenValue": 2, "materialName": "Material1", "affectedvalue": "x", "materialproperty": "map" } ] }, { "name": "Start AlphaMap Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube5" ], "materialAnimations": [ { "name": "add diffuse pos", "action": "addTexturePositionValue", "end_tc": 8, "start_tc": 0, "tweenValue": 2, "materialName": "Material5", "affectedvalue": "x", "materialproperty": "map" } ] }, { "name": "Start Diffuse Map Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube2" ], "materialAnimations": [ { "loop": 2, "name": "change diffuse pos", "action": "changeTexturePositionValue", "end_tc": 10, "start_tc": 0, "tweenValue": 2, "materialName": "Material2", "affectedvalue": "y", "materialproperty": "map" } ] }, { "name": "Start Diffuse Map Rotation Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube3" ], "materialAnimations": [ { "loop": 1, "name": "add diffuse rotation", "action": "addTextureRotationDegreeValue", "end_tc": 10, "start_tc": 0, "tweenValue": 180, "materialName": "Material3", "materialproperty": "map" } ] }, { "name": "trigger lod1 anim", "group": "Cube6", "state": 0, "animations": [ { "tc": 0, "name": "Cube6_lod1 Action" } ], "animationlayer": 0, "trigger_objects": [ "Cube6_lod1" ] }, { "name": "Stop Opacity Animation", "group": "Material", "state": 0, "animationlayer": 0, "trigger_objects": [ "Cube4" ], "materialAnimations": [ { "name": "changeOpacity", "action": "stopMaterialAnimation", "start_tc": 0 } ] } ], "Stage3dData": [ { "controls": { "maxZoom": 100000, "minZoom": 1, "zoomMode": 1, "maxDistance": 100000, "minDistance": 1 }, "scenetype": "_3d", "lightsetup": 0, "threedfile": "cube6.glb", "loddistance": 5, "spritescale": 1, "outlinelevel": 0, "defaultqualitylevel": 0 } ] }
Chama os meios de comunicação (pdfs/vídeos/…)
![](https://klarland.com/wp-content/uploads/2022/05/Screenshot_7-1024x528.png)
Ao clicar num objeto ou num botão, é possível recuperar um ou mais documentos (vídeo/mp4, pdf, jpg, png), que aparecem no separador “Media” para serem recuperados.
Os meios de comunicação devem ser carregados para o servidor na pasta docs da respectiva fase: /files/stages/[uuid]/docs. (Envias os dados ao teu gestor de conta em klar.land, que os armazena para ti).
“trigger_objects” objeto ou botão de acionamento
“cam” – aqui, entre outras coisas, especifica o nome da câmara associada e o alvo da câmara.
“documents” – um ou mais documentos a recuperar
“tipo” – vídeo | imagem | pdf
“uuid” – gera online em https://www.uuidgenerator.net/ e copia + cola
“playonclick” : true | false – se um documento deve aparecer diretamente
Exemplo – JSON:
"useCases" : [ { "name":"klick_dl_pdf", "group":"01", "animationlayer": 0, "poi_uuid": "0f7fa12e-dd7c-11eb-ba80-0242ac130004", "state":0, "trigger_objects":["button_vorne_rechts"], "cam": { "name": "Pdfcam", "position": [0, 0, 0], "rotation":[0, 0, 0], "lookatTarget":"button_vorne_rechts" },"documents": [ { "name":"01/Freund_Demovideo.mp4", "type":"video", "uuid":"ac848c20-e879-11eb-9a03-0242ac130003", "playonclick":true },{ "name":"01/Freunde_DemoPDF.pdf", "type":"pdf", "uuid":"1bd675a6-dd7c-11eb-ba80-0242ac130004" },{ "name":"01/klardenker_Unternehmenspräsentation_DE.pdf", "type":"pdf", "uuid":"cb5473d6-e879-11eb-9a03-0242ac130003" },{ "name":"01/netzwerken-web.jpg", "type":"image", "uuid":"fe743ad0-e879-11eb-9a03-0242ac130003" },{ "name":"01/Showroom_Demo.mp4", "type":"video", "uuid":"139e7574-e87a-11eb-9a03-0242ac130003" },{ "name":"01/Vorwort_DemoPDF.pdf", "type":"pdf", "uuid":"2ef28950-e87a-11eb-9a03-0242ac130003" } ]
Chamar os meios de comunicação a partir dos “Documentos” – importante para o multilinguismo
Primeiro, cria os documentos no espaço pretendido. Fazes isto da seguinte forma:
Navega até ao espaço pretendido.
Selecciona O meu computador -> Docs.
Depois, por exemplo, cria a tua própria pasta “stages”, onde podes escolher livremente o nome de acordo com as tuas preferências.
Clica em / abrir pasta.
Em seguida, cria um documento através do botão “+”.
Encontra um campo recém-criado para um documento e procura o ficheiro pretendido no teu computador através do “Browser” e carrega-o. Depois clica em “Guardar”.
Se quiseres oferecer o ficheiro noutras línguas, selecciona a outra língua pretendida e, em seguida, o ficheiro pretendido na língua correspondente através do “Navegador de ficheiros”. Clica novamente em “Guardar” e o documento fica disponível na nova língua adicionada.
Em seguida, copia o UUID do documento. Precisas disto para o inserir no json na fase, o que é feito como nos outros casos acima referidos.
O script funciona de tal forma que, se existir um UUID, verifica se existe uma entrada de base de dados correspondente. Se for esse o caso, o documento é carregado e visualizado. Se não for, pega no nome/caminho e carrega o ficheiro correspondente. Isto significa que as fases existentes não são atingidas.
Para as novas fases, basta introduzir o UUID e o tipo.
Um exemplo de uma entrada json com ligação a um ficheiro através do Doc UUID:
"useCases" : [ "name": "klick_dl_pdf_03", "group": "03", "state": 0, "poi_uuid": "5cdbbbbc-e883-11eb-9a03-0242ac130003", "documents": [ { "name": "03/klardenker_Unternehmenspräsentation_DE.pdf", "type": "pdf", "uuid": "8107123e-e883-11eb-9a03-0242ac130003" }, { "name": "03/Meditation_DemoVideo.mp4", "type": "video", "uuid": "3e7da4a1-6122-483b-9749-2c11caa87fd4", "playonclick": true }, { "name": "03/Sein1_DemoPDF.pdf", "type": "pdf", "uuid": "a4ad3ec0-e883-11eb-9a03-0242ac130003" }, { "name": "03/Sein2_DemoPDF.pdf", "type": "pdf", "uuid": "b2d9a1c8-e883-11eb-9a03-0242ac130003" }, { "name": "03/Seminar-web.jpg", "type": "image", "uuid": "c1f88a5c-e883-11eb-9a03-0242ac130003" } ], "animationlayer": 0, "trigger_objects": [ "button_03" ] }, ]
Spritesheets (quase apngs) (para botões)
![](https://klarland.com/wp-content/uploads/2022/05/Screenshot_6.png)
As folhas de sprites, tal como utilizadas pelos apngs, são definidas no grupo“buttons“.
Os ficheiros png que contêm os spritesheets são armazenados no servidor no diretório “/files/stages/sprites”. (Se quiseres usar spritesheets individuais, por favor fornece-os ao teu gestor de conta de klar.land e ele pode disponibilizá-los no servidor).
“buttonObjectName” é o nome do objeto 3D na cena. A cadeia “botão” deve ser incluída no nome. Isto faz com que o objeto se comporte como um botão. Ou seja, fica sempre virado para a câmara e, quando clicado, o botão desaparece.
“spritename” é o nome do ficheiro png localizado em “/files/stages/sprites”.
“tilesHoriz” : Número de azulejos horizontais
“tilesVert” : Número de azulejos verticais
“numTiles” : normalmente é o produto dos dois valores anteriores. No entanto, em casos excepcionais, este valor também pode ser diferente (por exemplo, se existirem alguns azulejos vazios no final).
“tileDispDuration” : Número de milissegundos durante os quais um mosaico deve ser apresentado.
“invisibleOnClick” : verdadeiro ou falso, opcional. Indica se um botão fica invisível depois de clicar ou não (=falso). A predefinição é verdadeira.
Exemplo – JSON:
"Stage3dData" :[ { "threedfile" : "01.glb", "outlinelevel" : 0, "lightsetup": 0, "spritescale": 1, "defaultqualitylevel": 0, "scenetype": "3d", "buttons":[ { "buttonObjectName":"button_vorne_rechts", "spritename":"floorbutton_spritesheet.png", "tilesHoriz":25, "tilesVert":1, "numTiles":25, "tileDispDuration":55, "invisibleOnClick":"false" } ],
Mapeamento de tons
Podes escolher entre 5 modos para ajustar o aspeto do palco através do mapeamento de tons (efeito pós-renderização).
Para tal, podem ser necessários vários testes em combinação com o Hdr e as intensidades de luz para obter o aspeto ideal para a respectiva fase.
Se nada for especificado, THREE.ACESFilmicToneMapping é utilizado por defeito.
Exemplo – JSON:
{ "Stage3dData": [ { "tonemapping": 2, ....
O mapa de tons pode assumir os valores 0, 1, 2, 3, 4 ou 5 – sendo que
0 = THREE.NoToneMapping
1 = THREE.LinearToneMapping
2 = THREE.ReinhardToneMapping
3 = THREE.CineonToneMapping
4 = THREE.ACESFilmicToneMapping
5 = THREE.CustomToneMapping
![](https://klarland.com/wp-content/uploads/2022/07/Screenshot_4-1024x462.png)
![](https://klarland.com/wp-content/uploads/2022/07/Screenshot_3-1024x469.png)
Texturas de vídeo
![](https://klarland.com/wp-content/uploads/2022/05/Screenshot_5-1024x590.png)
Para texturas de vídeo, são especificados os respectivos nomes dos objectos 3D (“videoObject“) nos quais as texturas de vídeo devem aparecer, bem como o caminho relativo para o ficheiro de vídeo (“videoFile“) no servidor.
Os vídeos devem ser carregados para o servidor na pasta docs da respectiva fase: /files/stages/[uuid]/docs
(Coordena o armazenamento de dados com o teu gestor de conta klar.land).
Exemplo – JSON:
"Stage3dData" :[ { "threedfile" : "01.glb", "outlinelevel" : 0, "lightsetup": 100, "spritescale": 1, "defaultqualitylevel": 0, "scenetype": "3d", "videotextures":[ { "videoObject":"_videoplane", "videoFile":"01/Freund_Demovideo.mp4" } ] } ]
“opções de “controlos
Vários parâmetros para definir o zoom e a distância das câmaras, bem como a atribuição do botão do rato a uma fase.
Exemplo – JSON:
{ "Stage3dData": [ { "hdr": { "name": "autoshop_01_1k.hdr", "intensity": 2 }, "controls": { "maxZoom": 5, "minZoom": 1, "zoomMode": 2, "maxDistance": 5, "minDistance": 1 },
{ "Stage3dData": [ { "controls": { "zoomMode": 3, "leftMouseMode":5, "middleMouseMode":1, "rightMouseMode": 1, }, ......
“zoomMode” pode assumir os valores 1 / 2 / 3. Isto significa que
1 = Carrinho (standard)
2 = ZOOM
3 = NENHUM
ou seja, 3 desactiva o zoom
“leftMouseMode” / “middleMouseMode” / “rightMouseMode” podem assumir os valores 1 / 2 / 3 / 4 / 5 / 6. Isto significa que
1 = ROTATE
2 = CAMINHÃO
3 = DESLOCAMENTO
4 = DOLLY
5 = ZOOM
6 = NENHUM
Se nada for especificado no código JSON, os valores predefinidos são
zoomMode = Dolly
leftMouseMode = ROTATE
rightMouseMode = TRUCK
middleMouseMode = DOLLY
Fases 360
(Segue documentação)
"spaces" : [ (werden nur in 360° Szenen verwendet) { "id" : [number] Stageid, "name": [string] Stagename, "ClickSpots": [ { "name":"Gehe in Flur", "position": [8.67872, 15.1609, -12.1186], "type":"spaceChangeSpot", "gotoSpace": [number] },{ "name":"Zeige Lampeninfo", "position": [-15.4345, 6.06061, 0], "type":"infoSpot" } ] },
Deixe um comentário